Browse Source

withdraw from bank

pbs
jake 4 years ago
parent
commit
4187e93455
  1. 42
      src/ship/items/bank.rs
  2. 119
      src/ship/items/inventory.rs
  3. 41
      src/ship/items/manager.rs
  4. 14
      src/ship/packet/builder/message.rs
  5. 5
      src/ship/packet/handler/direct_message.rs
  6. 785
      tests/test_bank.rs

42
src/ship/items/bank.rs

@ -24,6 +24,15 @@ impl StackedBankItem {
pub fn count(&self) -> usize { pub fn count(&self) -> usize {
self.entity_ids.len() self.entity_ids.len()
} }
pub fn take_entity_ids(&mut self, amount: usize) -> Option<Vec<ItemEntityId>> {
if amount <= self.count() {
Some(self.entity_ids.drain(..amount).collect())
}
else {
None
}
}
} }
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
@ -126,6 +135,26 @@ impl BankItem {
} }
} }
pub struct BankItemHandle<'a> {
bank: &'a mut CharacterBank,
index: usize
}
impl<'a> BankItemHandle<'a> {
pub fn item(&'a self) -> Option<&'a BankItem> {
self.bank.items.get(self.index)
}
pub fn item_mut(&mut self) -> Option<&mut BankItem> {
self.bank.items.get_mut(self.index)
}
pub fn remove_from_bank(self) {
self.bank.items.remove(self.index);
}
}
pub struct CharacterBank { pub struct CharacterBank {
item_id_counter: u32, item_id_counter: u32,
items: Vec<BankItem> items: Vec<BankItem>
@ -147,6 +176,19 @@ impl CharacterBank {
self.item_id_counter = base_item_id + self.items.len() as u32 + 1; self.item_id_counter = base_item_id + self.items.len() as u32 + 1;
} }
pub fn get_item_handle_by_id<'a>(&'a mut self, item_id: ClientItemId) -> Option<BankItemHandle<'a>> {
let (index, _) = self.items.iter()
.enumerate()
.filter(|(_, item)| {
item.item_id() == item_id
})
.nth(0)?;
Some(BankItemHandle {
bank: self,
index: index,
})
}
pub fn as_client_bank_items(&self) -> character::Bank { pub fn as_client_bank_items(&self) -> character::Bank {
self.items.iter() self.items.iter()
.enumerate() .enumerate()

119
src/ship/items/inventory.rs

@ -3,9 +3,11 @@ use thiserror::Error;
use libpso::character::character;//::InventoryItem; use libpso::character::character;//::InventoryItem;
use crate::entity::item::{ItemEntityId, ItemDetail, ItemType}; use crate::entity::item::{ItemEntityId, ItemDetail, ItemType};
use crate::entity::item::tool::Tool; use crate::entity::item::tool::Tool;
use crate::ship::items::ClientItemId;
use crate::ship::items::{ClientItemId, BankItem, BankItemHandle};
use crate::ship::items::floor::{IndividualFloorItem, StackedFloorItem}; use crate::ship::items::floor::{IndividualFloorItem, StackedFloorItem};
const INVENTORY_CAPACITY: usize = 30;
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub struct InventorySlot(pub usize); pub struct InventorySlot(pub usize);
@ -233,15 +235,15 @@ pub struct InventoryItemHandle<'a> {
impl<'a> InventoryItemHandle<'a> { impl<'a> InventoryItemHandle<'a> {
pub fn item(&'a self) -> Option<&'a InventoryItem> { pub fn item(&'a self) -> Option<&'a InventoryItem> {
self.inventory.0.get(self.slot)
self.inventory.items.get(self.slot)
} }
pub fn item_mut(&mut self) -> Option<&mut InventoryItem> { pub fn item_mut(&mut self) -> Option<&mut InventoryItem> {
self.inventory.0.get_mut(self.slot)
self.inventory.items.get_mut(self.slot)
} }
pub fn remove_from_inventory(self) { pub fn remove_from_inventory(self) {
self.inventory.0.remove(self.slot);
self.inventory.items.remove(self.slot);
} }
pub fn consume(self, amount: usize) -> Result<ConsumedItem, InventoryItemConsumeError> { pub fn consume(self, amount: usize) -> Result<ConsumedItem, InventoryItemConsumeError> {
@ -250,7 +252,7 @@ impl<'a> InventoryItemHandle<'a> {
Partial(Tool), Partial(Tool),
} }
let inventory_item = self.inventory.0.get(self.slot).ok_or(InventoryItemConsumeError::InconsistentState)?;
let inventory_item = self.inventory.items.get(self.slot).ok_or(InventoryItemConsumeError::InconsistentState)?;
let remove_method = match inventory_item { let remove_method = match inventory_item {
InventoryItem::Individual(individual_inventory_item) => { InventoryItem::Individual(individual_inventory_item) => {
RemoveMethod::EntireThing(ConsumedItem::Individual(IndividualConsumedItem { RemoveMethod::EntireThing(ConsumedItem::Individual(IndividualConsumedItem {
@ -278,11 +280,11 @@ impl<'a> InventoryItemHandle<'a> {
match remove_method { match remove_method {
RemoveMethod::EntireThing(consumed_item) => { RemoveMethod::EntireThing(consumed_item) => {
self.inventory.0.remove(self.slot);
self.inventory.items.remove(self.slot);
Ok(consumed_item) Ok(consumed_item)
}, },
RemoveMethod::Partial(tool) => { RemoveMethod::Partial(tool) => {
let entity_ids = self.inventory.0.get_mut(self.slot)
let entity_ids = self.inventory.items.get_mut(self.slot)
.and_then(|item| { .and_then(|item| {
if let InventoryItem::Stacked(stacked_inventory_item) = item { if let InventoryItem::Stacked(stacked_inventory_item) = item {
Some(stacked_inventory_item.entity_ids.drain(..amount).collect::<Vec<_>>()) Some(stacked_inventory_item.entity_ids.drain(..amount).collect::<Vec<_>>())
@ -305,21 +307,28 @@ impl<'a> InventoryItemHandle<'a> {
#[derive(Debug)] #[derive(Debug)]
pub struct CharacterInventory(Vec<InventoryItem>);
pub struct CharacterInventory {
item_id_counter: u32,
items: Vec<InventoryItem>,
}
impl CharacterInventory { impl CharacterInventory {
pub fn new(items: Vec<InventoryItem>) -> CharacterInventory { pub fn new(items: Vec<InventoryItem>) -> CharacterInventory {
CharacterInventory(items)
CharacterInventory{
item_id_counter: 0,
items: items,
}
} }
pub fn initialize_item_ids(&mut self, base_item_id: u32) { pub fn initialize_item_ids(&mut self, base_item_id: u32) {
for (i, item) in self.0.iter_mut().enumerate() {
for (i, item) in self.items.iter_mut().enumerate() {
item.set_item_id(ClientItemId(base_item_id + i as u32)); item.set_item_id(ClientItemId(base_item_id + i as u32));
} }
self.item_id_counter = base_item_id + self.items.len() as u32 + 1;
} }
pub fn as_client_inventory_items(&self) -> [character::InventoryItem; 30] { pub fn as_client_inventory_items(&self) -> [character::InventoryItem; 30] {
self.0.iter()
self.items.iter()
.enumerate() .enumerate()
.fold([character::InventoryItem::default(); 30], |mut inventory, (slot, item)| { .fold([character::InventoryItem::default(); 30], |mut inventory, (slot, item)| {
let bytes = item.as_client_bytes(); let bytes = item.as_client_bytes();
@ -335,15 +344,15 @@ impl CharacterInventory {
} }
pub fn slot(&self, slot: usize) -> Option<&InventoryItem> { pub fn slot(&self, slot: usize) -> Option<&InventoryItem> {
self.0.get(slot)
self.items.get(slot)
} }
pub fn count(&self) -> usize { pub fn count(&self) -> usize {
self.0.len()
self.items.len()
} }
pub fn get_item_handle_by_id<'a>(&'a mut self, item_id: ClientItemId) -> Option<InventoryItemHandle<'a>> { pub fn get_item_handle_by_id<'a>(&'a mut self, item_id: ClientItemId) -> Option<InventoryItemHandle<'a>> {
let (slot, _) = self.0.iter()
let (slot, _) = self.items.iter()
.enumerate() .enumerate()
.filter(|(_, item)| { .filter(|(_, item)| {
item.item_id() == item_id item.item_id() == item_id
@ -356,7 +365,7 @@ impl CharacterInventory {
} }
pub fn get_item_by_id(&self, item_id: ClientItemId) -> Option<&InventoryItem> { pub fn get_item_by_id(&self, item_id: ClientItemId) -> Option<&InventoryItem> {
self.0.iter()
self.items.iter()
.filter(|item| { .filter(|item| {
item.item_id() == item_id item.item_id() == item_id
}) })
@ -364,14 +373,14 @@ impl CharacterInventory {
} }
pub fn take_item_by_id(&mut self, item_id: ClientItemId) -> Option<InventoryItem> { pub fn take_item_by_id(&mut self, item_id: ClientItemId) -> Option<InventoryItem> {
self.0
self.items
.drain_filter(|i| i.item_id() == item_id) .drain_filter(|i| i.item_id() == item_id)
.nth(0) .nth(0)
} }
pub fn add_item(&mut self, item: InventoryItem) -> Result<(), ()> { // TODO: errors pub fn add_item(&mut self, item: InventoryItem) -> Result<(), ()> { // TODO: errors
// TODO: check slot conflict? // TODO: check slot conflict?
self.0.push(item);
self.items.push(item);
Ok(()) Ok(())
} }
@ -381,14 +390,14 @@ impl CharacterInventory {
return None; return None;
} }
self.0.push(InventoryItem::Individual(IndividualInventoryItem {
self.items.push(InventoryItem::Individual(IndividualInventoryItem {
entity_id: floor_item.entity_id, entity_id: floor_item.entity_id,
item_id: floor_item.item_id, item_id: floor_item.item_id,
item: floor_item.item.clone(), item: floor_item.item.clone(),
equipped: false, equipped: false,
})); }));
if let Some(InventoryItem::Individual(new_item)) = self.0.last() {
if let Some(InventoryItem::Individual(new_item)) = self.items.last() {
Some((new_item, InventorySlot(self.count()))) Some((new_item, InventorySlot(self.count())))
} }
else { else {
@ -398,7 +407,7 @@ impl CharacterInventory {
// TODO: can be simplified using find instead of position // TODO: can be simplified using find instead of position
pub fn pick_up_stacked_floor_item(&mut self, floor_item: &StackedFloorItem) -> Option<(&StackedInventoryItem, InventorySlot)> { pub fn pick_up_stacked_floor_item(&mut self, floor_item: &StackedFloorItem) -> Option<(&StackedInventoryItem, InventorySlot)> {
let existing_stack_position = self.0.iter()
let existing_stack_position = self.items.iter()
.position(|inventory_item| { .position(|inventory_item| {
if let InventoryItem::Stacked(stacked_inventory_item) = inventory_item { if let InventoryItem::Stacked(stacked_inventory_item) = inventory_item {
if stacked_inventory_item.tool == floor_item.tool { if stacked_inventory_item.tool == floor_item.tool {
@ -409,7 +418,7 @@ impl CharacterInventory {
}); });
if let Some(existing_stack_position) = existing_stack_position { if let Some(existing_stack_position) = existing_stack_position {
if let Some(InventoryItem::Stacked(stacked_item)) = self.0.get_mut(existing_stack_position) {
if let Some(InventoryItem::Stacked(stacked_item)) = self.items.get_mut(existing_stack_position) {
if stacked_item.count() + floor_item.count() <= stacked_item.tool.max_stack() { if stacked_item.count() + floor_item.count() <= stacked_item.tool.max_stack() {
stacked_item.entity_ids.append(&mut floor_item.entity_ids.clone()); stacked_item.entity_ids.append(&mut floor_item.entity_ids.clone());
Some((stacked_item, InventorySlot(existing_stack_position))) Some((stacked_item, InventorySlot(existing_stack_position)))
@ -429,8 +438,8 @@ impl CharacterInventory {
tool: floor_item.tool, tool: floor_item.tool,
}); });
self.0.push(new_stacked_item);
if let Some(InventoryItem::Stacked(new_item)) = self.0.last() {
self.items.push(new_stacked_item);
if let Some(InventoryItem::Stacked(new_item)) = self.items.last() {
Some((new_item, InventorySlot(self.count()))) Some((new_item, InventorySlot(self.count())))
} }
else { else {
@ -438,5 +447,69 @@ impl CharacterInventory {
} }
} }
} }
pub fn withdraw_item(&mut self, mut bank_item: BankItemHandle, amount: usize) -> Option<(&InventoryItem, usize)> {
let (remove, slot) = match bank_item.item_mut()? {
BankItem::Individual(individual_bank_item) => {
if self.items.len() >= INVENTORY_CAPACITY {
return None
}
self.items.push(InventoryItem::Individual(IndividualInventoryItem {
entity_id: individual_bank_item.entity_id,
item_id: individual_bank_item.item_id,
item: individual_bank_item.item.clone(),
equipped: false,
}));
(true, self.count())
},
BankItem::Stacked(stacked_bank_item) => {
let existing_inventory_item = self.items.iter_mut()
.enumerate()
.find_map(|(index, item)| {
if let InventoryItem::Stacked(stacked_inventory_item) = item {
if stacked_inventory_item.tool == stacked_inventory_item.tool {
return Some((index, stacked_inventory_item))
}
}
None
});
let slot = match existing_inventory_item {
Some((slot, stacked_inventory_item)) => {
if stacked_inventory_item.count() + stacked_bank_item.count() > stacked_bank_item.tool.max_stack() {
return None
}
let mut withdrawn_entity_ids = stacked_bank_item.take_entity_ids(amount)?;
stacked_inventory_item.entity_ids.append(&mut withdrawn_entity_ids);
slot
}
None => {
if self.items.len() >= INVENTORY_CAPACITY {
return None
}
let withdrawn_entity_ids = stacked_bank_item.take_entity_ids(amount)?;
self.item_id_counter += 1; // oh no
self.items.push(InventoryItem::Stacked(StackedInventoryItem {
entity_ids: withdrawn_entity_ids,
item_id: ClientItemId(self.item_id_counter),
tool: stacked_bank_item.tool,
}));
self.count()
}
};
(stacked_bank_item.count() == 0, slot)
}
};
if remove {
bank_item.remove_from_bank();
}
self.items.last().map(|item| {
(item, slot)
})
}
} }

41
src/ship/items/manager.rs

@ -558,12 +558,41 @@ impl ItemManager {
} }
pub async fn player_withdraws_item<EG: EntityGateway>(&mut self, pub async fn player_withdraws_item<EG: EntityGateway>(&mut self,
_entity_gateway: &mut EG,
_character: &CharacterEntity,
_item_id: ClientItemId,
_amount: usize)
-> Result<(), ItemManagerError> {
entity_gateway: &mut EG,
character: &CharacterEntity,
item_id: ClientItemId,
amount: usize)
-> Result<&InventoryItem, ItemManagerError> {
Ok(())
let inventory = self.character_inventory.get_mut(&character.id).ok_or(ItemManagerError::NoCharacter(character.id))?;
let bank = self.character_bank
.get_mut(&character.id)
.ok_or(ItemManagerError::NoCharacter(character.id))?;
let item_to_withdraw = bank.get_item_handle_by_id(item_id).ok_or(ItemManagerError::NoSuchItemId(item_id))?;
let inventory_item = inventory.withdraw_item(item_to_withdraw, amount).ok_or(ItemManagerError::Idunnoman)?;
match inventory_item {
(InventoryItem::Individual(individual_inventory_item), slot) => {
entity_gateway.change_item_location(&individual_inventory_item.entity_id,
ItemLocation::Inventory {
character_id: character.id,
slot: slot,
equipped: false,
}).await;
},
(InventoryItem::Stacked(stacked_inventory_item), slot) => {
for entity_id in &stacked_inventory_item.entity_ids {
entity_gateway.change_item_location(entity_id,
ItemLocation::Inventory {
character_id: character.id,
slot: slot,
equipped: false,
}).await;
}
}
}
Ok(inventory_item.0)
} }
} }

14
src/ship/packet/builder/message.rs

@ -2,7 +2,7 @@ use libpso::packet::messages::*;
use libpso::packet::ship::*; use libpso::packet::ship::*;
use crate::common::leveltable::CharacterStats; use crate::common::leveltable::CharacterStats;
use crate::ship::ship::{ShipError}; use crate::ship::ship::{ShipError};
use crate::ship::items::{ClientItemId, StackedFloorItem, FloorItem, CharacterBank};
use crate::ship::items::{ClientItemId, InventoryItem, StackedFloorItem, FloorItem, CharacterBank};
use crate::ship::location::AreaClient; use crate::ship::location::AreaClient;
use std::convert::TryInto; use std::convert::TryInto;
@ -37,6 +37,18 @@ pub fn create_item(area_client: AreaClient, item: &FloorItem) -> Result<CreateIt
}) })
} }
pub fn create_withdrawn_inventory_item(area_client: AreaClient, item: &InventoryItem) -> Result<CreateItem, ShipError> {
let bytes = item.as_client_bytes();
Ok(CreateItem {
client: area_client.local_client.id(),
target: 0,
item_data: bytes[0..12].try_into()?,
item_id: item.item_id().0,
item_data2: bytes[12..16].try_into()?,
unknown: 0,
})
}
pub fn remove_item_from_floor(area_client: AreaClient, item: &FloorItem) -> Result<RemoveItemFromFloor, ShipError> { pub fn remove_item_from_floor(area_client: AreaClient, item: &FloorItem) -> Result<RemoveItemFromFloor, ShipError> {
Ok(RemoveItemFromFloor { Ok(RemoveItemFromFloor {
client: area_client.local_client.id(), client: area_client.local_client.id(),

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

@ -249,8 +249,9 @@ where
Vec::new() Vec::new()
} }
else { else {
item_manager.player_withdraws_item(entity_gateway, &client.character, ClientItemId(bank_interaction.item_id), bank_interaction.item_amount as usize).await?;
Vec::new()
let item_added_to_inventory = item_manager.player_withdraws_item(entity_gateway, &client.character, ClientItemId(bank_interaction.item_id), bank_interaction.item_amount as usize).await?;
let item_created = builder::message::create_withdrawn_inventory_item(area_client, &item_added_to_inventory)?;
vec![SendShipPacket::Message(Message::new(GameMessage::CreateItem(item_created)))]
} }
}, },
_ => { _ => {

785
tests/test_bank.rs

@ -41,7 +41,7 @@ async fn test_bank_items_sent_in_character_login() {
menu: BLOCK_MENU_ID, menu: BLOCK_MENU_ID,
item: 1, item: 1,
})).await.unwrap().collect::<Vec<_>>(); })).await.unwrap().collect::<Vec<_>>();
assert!(matches!(&packets[0], (_, SendShipPacket::FullCharacter(fc)) if fc.character.bank.items[0].data1[0..3] == [0x00, 0x08, 0x04] )); assert!(matches!(&packets[0], (_, SendShipPacket::FullCharacter(fc)) if fc.character.bank.items[0].data1[0..3] == [0x00, 0x08, 0x04] ));
} }
@ -278,7 +278,7 @@ async fn test_deposit_individual_item() {
if player_no_longer_has_item.item_id == 0x10001 if player_no_longer_has_item.item_id == 0x10001
&& player_no_longer_has_item.amount == 0 && player_no_longer_has_item.amount == 0
)); ));
let items = entity_gateway.get_items_by_character(&char1).await; let items = entity_gateway.get_items_by_character(&char1).await;
let bank_item_ids = items.iter() let bank_item_ids = items.iter()
.filter_map(|item| { .filter_map(|item| {
@ -345,7 +345,7 @@ async fn test_deposit_stacked_item() {
if player_no_longer_has_item.item_id == 0x10000 if player_no_longer_has_item.item_id == 0x10000
&& player_no_longer_has_item.amount == 3 && player_no_longer_has_item.amount == 3
)); ));
let items = entity_gateway.get_items_by_character(&char1).await; let items = entity_gateway.get_items_by_character(&char1).await;
let bank_item_ids = items.iter() let bank_item_ids = items.iter()
.filter_map(|item| { .filter_map(|item| {
@ -412,7 +412,7 @@ async fn test_deposit_partial_stacked_item() {
if player_no_longer_has_item.item_id == 0x10000 if player_no_longer_has_item.item_id == 0x10000
&& player_no_longer_has_item.amount == 2 && player_no_longer_has_item.amount == 2
)); ));
let items = entity_gateway.get_items_by_character(&char1).await; let items = entity_gateway.get_items_by_character(&char1).await;
let bank_item_ids = items.iter() let bank_item_ids = items.iter()
.filter_map(|item| { .filter_map(|item| {
@ -505,7 +505,7 @@ async fn test_deposit_stacked_item_with_stack_already_in_bank() {
if player_no_longer_has_item.item_id == 0x10000 if player_no_longer_has_item.item_id == 0x10000
&& player_no_longer_has_item.amount == 2 && player_no_longer_has_item.amount == 2
)); ));
let items = entity_gateway.get_items_by_character(&char1).await; let items = entity_gateway.get_items_by_character(&char1).await;
let bank_item_ids = items.iter() let bank_item_ids = items.iter()
.filter_map(|item| { .filter_map(|item| {
@ -579,7 +579,7 @@ async fn test_deposit_stacked_item_with_full_stack_in_bank() {
})))).await; })))).await;
assert!(packets.is_err()); assert!(packets.is_err());
let items = entity_gateway.get_items_by_character(&char1).await; let items = entity_gateway.get_items_by_character(&char1).await;
let bank_item_ids = items.iter() let bank_item_ids = items.iter()
.filter_map(|item| { .filter_map(|item| {
@ -671,7 +671,7 @@ async fn test_deposit_individual_item_in_full_bank() {
})))).await; })))).await;
assert!(packets.is_err()); assert!(packets.is_err());
let items = entity_gateway.get_items_by_character(&char1).await; let items = entity_gateway.get_items_by_character(&char1).await;
let bank_item_ids = items.iter() let bank_item_ids = items.iter()
.filter_map(|item| { .filter_map(|item| {
@ -761,7 +761,7 @@ async fn test_deposit_stacked_item_in_full_bank() {
})))).await; })))).await;
assert!(packets.is_err()); assert!(packets.is_err());
let items = entity_gateway.get_items_by_character(&char1).await; let items = entity_gateway.get_items_by_character(&char1).await;
let bank_item_ids = items.iter() let bank_item_ids = items.iter()
.filter_map(|item| { .filter_map(|item| {
@ -995,7 +995,774 @@ async fn test_deposit_meseta_when_bank_is_maxed() {
let characters = entity_gateway.get_characters_by_user(&user1).await; let characters = entity_gateway.get_characters_by_user(&user1).await;
let char = characters[0].as_ref().unwrap(); let char = characters[0].as_ref().unwrap();
println!("meseta {}", char.meseta);
assert!(char.meseta == 300); assert!(char.meseta == 300);
assert!(char.bank_meseta == 999999); assert!(char.bank_meseta == 999999);
} }
#[async_std::test]
async fn test_withdraw_individual_item() {
let mut entity_gateway = InMemoryGateway::new();
let (_user1, char1) = new_user_character(&mut entity_gateway, "a1", "a").await;
let (_user2, _char2) = new_user_character(&mut entity_gateway, "a2", "a").await;
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,
}
),
location: item::ItemLocation::Bank {
character_id: char1.id,
name: item::BankName("".to_string())
}
}).await;
let mut ship = ShipServerState::new(entity_gateway.clone());
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;
ship.handle(ClientId(1), &RecvShipPacket::DirectMessage(DirectMessage::new(0, GameMessage::BankRequest(BankRequest {
client: 0,
target: 0,
unknown: 0,
})))).await.unwrap().for_each(drop);
let packets = ship.handle(ClientId(1), &RecvShipPacket::DirectMessage(DirectMessage::new(0, GameMessage::BankInteraction(BankInteraction {
client: 0,
target: 0,
item_id: 0x20000,
action: 1,
item_amount: 0,
meseta_amount: 0,
unknown: 0,
})))).await.unwrap().collect::<Vec<_>>();
assert!(packets.len() == 1);
assert!(matches!(&packets[0], (ClientId(2), SendShipPacket::Message(Message {msg: GameMessage::CreateItem(create_item)}))
if create_item.item_id == 0x20000
));
let items = entity_gateway.get_items_by_character(&char1).await;
let inventory_item_ids = items.iter()
.filter_map(|item| {
if let item::ItemLocation::Inventory{..} = item.location {
Some(item.id)
}
else {
None
}
})
.collect::<Vec<_>>();
assert!(inventory_item_ids == vec![item::ItemEntityId(1)]);
}
#[async_std::test]
async fn test_withdraw_stacked_item() {
let mut entity_gateway = InMemoryGateway::new();
let (_user1, char1) = new_user_character(&mut entity_gateway, "a1", "a").await;
let (_user2, _char2) = new_user_character(&mut entity_gateway, "a2", "a").await;
for _ in 0..3 {
entity_gateway.create_item(
item::NewItemEntity {
item: item::ItemDetail::Tool(
item::tool::Tool {
tool: item::tool::ToolType::Monomate,
}
),
location: item::ItemLocation::Bank {
character_id: char1.id,
name: item::BankName("".to_string())
}
}).await;
}
let mut ship = ShipServerState::new(entity_gateway.clone());
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;
ship.handle(ClientId(1), &RecvShipPacket::DirectMessage(DirectMessage::new(0, GameMessage::BankRequest(BankRequest {
client: 0,
target: 0,
unknown: 0,
})))).await.unwrap().for_each(drop);
let packets = ship.handle(ClientId(1), &RecvShipPacket::DirectMessage(DirectMessage::new(0, GameMessage::BankInteraction(BankInteraction {
client: 0,
target: 0,
item_id: 0x20000,
action: 1,
item_amount: 3,
meseta_amount: 0,
unknown: 0,
})))).await.unwrap().collect::<Vec<_>>();
assert!(packets.len() == 1);
assert!(matches!(&packets[0], (ClientId(2), SendShipPacket::Message(Message {msg: GameMessage::CreateItem(create_item)}))
if create_item.item_id == 0x10002
));
let items = entity_gateway.get_items_by_character(&char1).await;
let inventory_item_ids = items.iter()
.filter_map(|item| {
if let item::ItemLocation::Inventory {..} = item.location {
Some(item.id)
}
else {
None
}
})
.collect::<Vec<_>>();
assert!(inventory_item_ids == vec![item::ItemEntityId(1), item::ItemEntityId(2), item::ItemEntityId(3)]);
}
#[async_std::test]
async fn test_withdraw_partial_stacked_item() {
let mut entity_gateway = InMemoryGateway::new();
let (_user1, char1) = new_user_character(&mut entity_gateway, "a1", "a").await;
let (_user2, _char2) = new_user_character(&mut entity_gateway, "a2", "a").await;
for _ in 0..3 {
entity_gateway.create_item(
item::NewItemEntity {
item: item::ItemDetail::Tool(
item::tool::Tool {
tool: item::tool::ToolType::Monomate,
}
),
location: item::ItemLocation::Bank {
character_id: char1.id,
name: item::BankName("".into())
}
}).await;
}
let mut ship = ShipServerState::new(entity_gateway.clone());
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;
ship.handle(ClientId(1), &RecvShipPacket::DirectMessage(DirectMessage::new(0, GameMessage::BankRequest(BankRequest {
client: 0,
target: 0,
unknown: 0,
})))).await.unwrap().for_each(drop);
let packets = ship.handle(ClientId(1), &RecvShipPacket::DirectMessage(DirectMessage::new(0, GameMessage::BankInteraction(BankInteraction {
client: 0,
target: 0,
item_id: 0x20000,
action: 1,
item_amount: 2,
meseta_amount: 0,
unknown: 0,
})))).await.unwrap().collect::<Vec<_>>();
assert!(packets.len() == 1);
assert!(matches!(&packets[0], (ClientId(2), SendShipPacket::Message(Message {msg: GameMessage::CreateItem(create_item)}))
if create_item.item_id == 0x10002
));
let items = entity_gateway.get_items_by_character(&char1).await;
let bank_item_ids = items.iter()
.filter_map(|item| {
if let item::ItemLocation::Bank {..} = item.location {
Some(item.id)
}
else {
None
}
})
.collect::<Vec<_>>();
assert!(bank_item_ids == vec![item::ItemEntityId(3)]);
let inventory_item_ids = items.iter()
.filter_map(|item| {
if let item::ItemLocation::Inventory {..} = item.location {
Some(item.id)
}
else {
None
}
})
.collect::<Vec<_>>();
assert!(inventory_item_ids == vec![item::ItemEntityId(1), item::ItemEntityId(2)]);
}
#[async_std::test]
async fn test_withdraw_stacked_item_with_stack_already_in_inventory() {
let mut entity_gateway = InMemoryGateway::new();
let (_user1, char1) = new_user_character(&mut entity_gateway, "a1", "a").await;
let (_user2, _char2) = new_user_character(&mut entity_gateway, "a2", "a").await;
for _ in 0..2 {
entity_gateway.create_item(
item::NewItemEntity {
item: item::ItemDetail::Tool(
item::tool::Tool {
tool: item::tool::ToolType::Monomate,
}
),
location: item::ItemLocation::Inventory {
character_id: char1.id,
slot: 0,
equipped: false,
}
}).await;
entity_gateway.create_item(
item::NewItemEntity {
item: item::ItemDetail::Tool(
item::tool::Tool {
tool: item::tool::ToolType::Monomate,
}
),
location: item::ItemLocation::Bank {
character_id: char1.id,
name: item::BankName("".into()),
}
}).await;
}
let mut ship = ShipServerState::new(entity_gateway.clone());
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;
ship.handle(ClientId(1), &RecvShipPacket::DirectMessage(DirectMessage::new(0, GameMessage::BankRequest(BankRequest {
client: 0,
target: 0,
unknown: 0,
})))).await.unwrap().for_each(drop);
let packets = ship.handle(ClientId(1), &RecvShipPacket::DirectMessage(DirectMessage::new(0, GameMessage::BankInteraction(BankInteraction {
client: 0,
target: 0,
item_id: 0x20000,
action: 1,
item_amount: 2,
meseta_amount: 0,
unknown: 0,
})))).await.unwrap().collect::<Vec<_>>();
assert!(packets.len() == 1);
assert!(matches!(&packets[0], (ClientId(2), SendShipPacket::Message(Message {msg: GameMessage::CreateItem(create_item)}))
if create_item.item_id == 0x10000
));
let items = entity_gateway.get_items_by_character(&char1).await;
let inventory_item_ids = items.iter()
.filter_map(|item| {
if let item::ItemLocation::Inventory {..} = item.location {
Some(item.id)
}
else {
None
}
})
.collect::<Vec<_>>();
assert!(inventory_item_ids == vec![item::ItemEntityId(1), item::ItemEntityId(2), item::ItemEntityId(3), item::ItemEntityId(4)]);
}
#[async_std::test]
async fn test_withdraw_stacked_item_with_full_stack_in_inventory() {
let mut entity_gateway = InMemoryGateway::new();
let (_user1, char1) = new_user_character(&mut entity_gateway, "a1", "a").await;
for _ in 0..2 {
entity_gateway.create_item(
item::NewItemEntity {
item: item::ItemDetail::Tool(
item::tool::Tool {
tool: item::tool::ToolType::Monomate,
}
),
location: item::ItemLocation::Bank {
character_id: char1.id,
name: item::BankName("".into()),
}
}).await;
}
for _ in 0..10 {
entity_gateway.create_item(
item::NewItemEntity {
item: item::ItemDetail::Tool(
item::tool::Tool {
tool: item::tool::ToolType::Monomate,
}
),
location: item::ItemLocation::Inventory {
character_id: char1.id,
slot: 0,
equipped: false,
}
}).await;
}
let mut ship = ShipServerState::new(entity_gateway.clone());
log_in_char(&mut ship, ClientId(1), "a1", "a").await;
join_lobby(&mut ship, ClientId(1)).await;
create_room(&mut ship, ClientId(1), "room", "").await;
ship.handle(ClientId(1), &RecvShipPacket::DirectMessage(DirectMessage::new(0, GameMessage::BankRequest(BankRequest {
client: 0,
target: 0,
unknown: 0,
})))).await.unwrap().for_each(drop);
let packets = ship.handle(ClientId(1), &RecvShipPacket::DirectMessage(DirectMessage::new(0, GameMessage::BankInteraction(BankInteraction {
client: 0,
target: 0,
item_id: 0x20000,
action: 1,
item_amount: 2,
meseta_amount: 0,
unknown: 0,
})))).await;
assert!(packets.is_err());
let items = entity_gateway.get_items_by_character(&char1).await;
let bank_item_ids = items.iter()
.filter_map(|item| {
if let item::ItemLocation::Bank {..} = item.location {
Some(item.id)
}
else {
None
}
})
.collect::<Vec<_>>();
assert!(bank_item_ids == vec![item::ItemEntityId(1), item::ItemEntityId(2)]);
let inventory_item_ids = items.iter()
.filter_map(|item| {
if let item::ItemLocation::Inventory {..} = item.location {
Some(item.id)
}
else {
None
}
})
.collect::<Vec<_>>();
assert!(inventory_item_ids.len() == 10);
}
#[async_std::test]
async fn test_withdraw_individual_item_in_full_inventory() {
let mut entity_gateway = InMemoryGateway::new();
let (_user1, char1) = new_user_character(&mut entity_gateway, "a1", "a").await;
entity_gateway.create_item(
item::NewItemEntity {
item: item::ItemDetail::Weapon(
item::weapon::Weapon {
weapon: item::weapon::WeaponType::Vulcan,
grind: 0,
special: None,
attrs: [None, None, None],
tekked: true,
}
),
location: item::ItemLocation::Bank {
character_id: char1.id,
name: item::BankName("".to_string())
}
}).await;
for i in 0..30 {
entity_gateway.create_item(
item::NewItemEntity {
item: item::ItemDetail::Weapon(
item::weapon::Weapon {
weapon: item::weapon::WeaponType::Vulcan,
grind: 0,
special: None,
attrs: [None, None, None],
tekked: true,
}
),
location: item::ItemLocation::Inventory {
character_id: char1.id,
slot: i,
equipped: false,
}
}).await;
}
let mut ship = ShipServerState::new(entity_gateway.clone());
log_in_char(&mut ship, ClientId(1), "a1", "a").await;
join_lobby(&mut ship, ClientId(1)).await;
create_room(&mut ship, ClientId(1), "room", "").await;
ship.handle(ClientId(1), &RecvShipPacket::DirectMessage(DirectMessage::new(0, GameMessage::BankRequest(BankRequest {
client: 0,
target: 0,
unknown: 0,
})))).await.unwrap().for_each(drop);
let packets = ship.handle(ClientId(1), &RecvShipPacket::DirectMessage(DirectMessage::new(0, GameMessage::BankInteraction(BankInteraction {
client: 0,
target: 0,
item_id: 0x20000,
action: 1,
item_amount: 0,
meseta_amount: 0,
unknown: 0,
})))).await;
assert!(packets.is_err());
let items = entity_gateway.get_items_by_character(&char1).await;
let bank_item_ids = items.iter()
.filter_map(|item| {
if let item::ItemLocation::Bank {..} = item.location {
Some(item.id)
}
else {
None
}
})
.collect::<Vec<_>>();
assert!(bank_item_ids == vec![item::ItemEntityId(1)]);
let inventory_item_ids = items.iter()
.filter_map(|item| {
if let item::ItemLocation::Inventory {..} = item.location {
Some(item.id)
}
else {
None
}
})
.collect::<Vec<_>>();
assert!(inventory_item_ids.len() == 30);
}
#[async_std::test]
async fn test_withdraw_stacked_item_in_full_inventory() {
let mut entity_gateway = InMemoryGateway::new();
let (_user1, char1) = new_user_character(&mut entity_gateway, "a1", "a").await;
for _ in 0..2 {
entity_gateway.create_item(
item::NewItemEntity {
item: item::ItemDetail::Tool(
item::tool::Tool {
tool: item::tool::ToolType::Monomate,
}
),
location: item::ItemLocation::Bank {
character_id: char1.id,
name: item::BankName("".to_string())
}
}).await;
}
for i in 0..30 {
entity_gateway.create_item(
item::NewItemEntity {
item: item::ItemDetail::Weapon(
item::weapon::Weapon {
weapon: item::weapon::WeaponType::Vulcan,
grind: 0,
special: None,
attrs: [None, None, None],
tekked: true,
}
),
location: item::ItemLocation::Inventory {
character_id: char1.id,
slot: i,
equipped: false,
}
}).await;
}
let mut ship = ShipServerState::new(entity_gateway.clone());
log_in_char(&mut ship, ClientId(1), "a1", "a").await;
join_lobby(&mut ship, ClientId(1)).await;
create_room(&mut ship, ClientId(1), "room", "").await;
ship.handle(ClientId(1), &RecvShipPacket::DirectMessage(DirectMessage::new(0, GameMessage::BankRequest(BankRequest {
client: 0,
target: 0,
unknown: 0,
})))).await.unwrap().for_each(drop);
let packets = ship.handle(ClientId(1), &RecvShipPacket::DirectMessage(DirectMessage::new(0, GameMessage::BankInteraction(BankInteraction {
client: 0,
target: 0,
item_id: 0x20000,
action: 1,
item_amount: 2,
meseta_amount: 0,
unknown: 0,
})))).await;
assert!(packets.is_err());
let items = entity_gateway.get_items_by_character(&char1).await;
let bank_item_ids = items.iter()
.filter_map(|item| {
if let item::ItemLocation::Bank {..} = item.location {
Some(item.id)
}
else {
None
}
})
.collect::<Vec<_>>();
assert!(bank_item_ids == vec![item::ItemEntityId(1), item::ItemEntityId(2)]);
let inventory_item_ids = items.iter()
.filter_map(|item| {
if let item::ItemLocation::Inventory {..} = item.location {
Some(item.id)
}
else {
None
}
})
.collect::<Vec<_>>();
assert!(inventory_item_ids.len() == 30);
}
#[async_std::test]
async fn test_withdraw_stacked_item_in_full_inventory_with_partial_stack() {
let mut entity_gateway = InMemoryGateway::new();
let (_user1, char1) = new_user_character(&mut entity_gateway, "a1", "a").await;
for _ in 0..2 {
entity_gateway.create_item(
item::NewItemEntity {
item: item::ItemDetail::Tool(
item::tool::Tool {
tool: item::tool::ToolType::Monomate,
}
),
location: item::ItemLocation::Bank {
character_id: char1.id,
name: item::BankName("".to_string())
}
}).await;
}
for i in 0..29 {
entity_gateway.create_item(
item::NewItemEntity {
item: item::ItemDetail::Weapon(
item::weapon::Weapon {
weapon: item::weapon::WeaponType::Vulcan,
grind: 0,
special: None,
attrs: [None, None, None],
tekked: true,
}
),
location: item::ItemLocation::Inventory {
character_id: char1.id,
slot: i,
equipped: false,
}
}).await;
}
for _ in 0..2 {
entity_gateway.create_item(
item::NewItemEntity {
item: item::ItemDetail::Tool(
item::tool::Tool {
tool: item::tool::ToolType::Monomate,
}
),
location: item::ItemLocation::Inventory {
character_id: char1.id,
slot: 29,
equipped: false,
}
}).await;
}
let mut ship = ShipServerState::new(entity_gateway.clone());
log_in_char(&mut ship, ClientId(1), "a1", "a").await;
join_lobby(&mut ship, ClientId(1)).await;
create_room(&mut ship, ClientId(1), "room", "").await;
ship.handle(ClientId(1), &RecvShipPacket::DirectMessage(DirectMessage::new(0, GameMessage::BankRequest(BankRequest {
client: 0,
target: 0,
unknown: 0,
})))).await.unwrap().for_each(drop);
ship.handle(ClientId(1), &RecvShipPacket::DirectMessage(DirectMessage::new(0, GameMessage::BankInteraction(BankInteraction {
client: 0,
target: 0,
item_id: 0x20000,
action: 1,
item_amount: 2,
meseta_amount: 0,
unknown: 0,
})))).await.unwrap().for_each(drop);
let items = entity_gateway.get_items_by_character(&char1).await;
let bank_item_ids = items.iter()
.filter_map(|item| {
if let item::ItemLocation::Bank {..} = item.location {
Some(item.id)
}
else {
None
}
})
.collect::<Vec<_>>();
assert!(bank_item_ids.len() == 0);
let inventory_item_ids = items.iter()
.filter_map(|item| {
if let item::ItemLocation::Inventory {..} = item.location {
Some(item.id)
}
else {
None
}
})
.collect::<Vec<_>>();
assert!(inventory_item_ids.len() == 33);
}
#[async_std::test]
async fn test_withdraw_meseta() {
let mut entity_gateway = InMemoryGateway::new();
let (user1, mut char1) = new_user_character(&mut entity_gateway, "a1", "a").await;
char1.bank_meseta = 300;
entity_gateway.save_character(&char1).await;
let mut ship = ShipServerState::new(entity_gateway.clone());
log_in_char(&mut ship, ClientId(1), "a1", "a").await;
join_lobby(&mut ship, ClientId(1)).await;
create_room(&mut ship, ClientId(1), "room", "").await;
ship.handle(ClientId(1), &RecvShipPacket::DirectMessage(DirectMessage::new(0, GameMessage::BankRequest(BankRequest {
client: 0,
target: 0,
unknown: 0,
})))).await.unwrap().for_each(drop);
ship.handle(ClientId(1), &RecvShipPacket::DirectMessage(DirectMessage::new(0, GameMessage::BankInteraction(BankInteraction {
client: 0,
target: 0,
item_id: 0xFFFFFFFF,
action: 1,
item_amount: 0,
meseta_amount: 23,
unknown: 0,
})))).await.unwrap().for_each(drop);
let characters = entity_gateway.get_characters_by_user(&user1).await;
let char = characters[0].as_ref().unwrap();
assert!(char.meseta == 23);
assert!(char.bank_meseta == 277);
}
#[async_std::test]
async fn test_withdraw_too_much_meseta() {
let mut entity_gateway = InMemoryGateway::new();
let (user1, mut char1) = new_user_character(&mut entity_gateway, "a1", "a").await;
char1.meseta = 999980;
char1.bank_meseta = 300;
entity_gateway.save_character(&char1).await;
let mut ship = ShipServerState::new(entity_gateway.clone());
log_in_char(&mut ship, ClientId(1), "a1", "a").await;
join_lobby(&mut ship, ClientId(1)).await;
create_room(&mut ship, ClientId(1), "room", "").await;
ship.handle(ClientId(1), &RecvShipPacket::DirectMessage(DirectMessage::new(0, GameMessage::BankRequest(BankRequest {
client: 0,
target: 0,
unknown: 0,
})))).await.unwrap().for_each(drop);
ship.handle(ClientId(1), &RecvShipPacket::DirectMessage(DirectMessage::new(0, GameMessage::BankInteraction(BankInteraction {
client: 0,
target: 0,
item_id: 0xFFFFFFFF,
action: 1,
item_amount: 0,
meseta_amount: 23,
unknown: 0,
})))).await.unwrap().for_each(drop);
let characters = entity_gateway.get_characters_by_user(&user1).await;
let char = characters[0].as_ref().unwrap();
assert!(char.meseta == 999980);
assert!(char.bank_meseta == 300);
}
#[async_std::test]
async fn test_withdraw_meseta_inventory_is_maxed() {
let mut entity_gateway = InMemoryGateway::new();
let (user1, mut char1) = new_user_character(&mut entity_gateway, "a1", "a").await;
char1.meseta = 999999;
char1.bank_meseta = 300;
entity_gateway.save_character(&char1).await;
let mut ship = ShipServerState::new(entity_gateway.clone());
log_in_char(&mut ship, ClientId(1), "a1", "a").await;
join_lobby(&mut ship, ClientId(1)).await;
create_room(&mut ship, ClientId(1), "room", "").await;
ship.handle(ClientId(1), &RecvShipPacket::DirectMessage(DirectMessage::new(0, GameMessage::BankRequest(BankRequest {
client: 0,
target: 0,
unknown: 0,
})))).await.unwrap().for_each(drop);
ship.handle(ClientId(1), &RecvShipPacket::DirectMessage(DirectMessage::new(0, GameMessage::BankInteraction(BankInteraction {
client: 0,
target: 0,
item_id: 0xFFFFFFFF,
action: 1,
item_amount: 0,
meseta_amount: 23,
unknown: 0,
})))).await.unwrap().for_each(drop);
let characters = entity_gateway.get_characters_by_user(&user1).await;
let char = characters[0].as_ref().unwrap();
assert!(char.meseta == 999999);
assert!(char.bank_meseta == 300);
}
Loading…
Cancel
Save