Browse Source

add bank depositing

pbs
jake 4 years ago
parent
commit
1867fee78b
  1. 80
      src/ship/items/bank.rs
  2. 4
      src/ship/items/floor.rs
  3. 46
      src/ship/items/inventory.rs
  4. 66
      src/ship/items/manager.rs
  5. 11
      src/ship/packet/builder/message.rs
  6. 107
      src/ship/packet/handler/direct_message.rs
  7. 12
      src/ship/packet/handler/message.rs
  8. 6
      src/ship/ship.rs
  9. 788
      tests/test_bank.rs
  10. 11
      tests/test_item_pickup.rs

80
src/ship/items/bank.rs

@ -2,6 +2,9 @@ use crate::ship::items::ClientItemId;
use libpso::character::character;//::InventoryItem; use libpso::character::character;//::InventoryItem;
use crate::entity::item::{ItemEntityId, ItemDetail}; use crate::entity::item::{ItemEntityId, ItemDetail};
use crate::entity::item::tool::Tool; use crate::entity::item::tool::Tool;
use crate::ship::items::inventory::{InventoryItemHandle, InventoryItem};
const BANK_CAPACITY: usize = 200;
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub struct IndividualBankItem { pub struct IndividualBankItem {
@ -123,22 +126,29 @@ impl BankItem {
} }
} }
pub struct CharacterBank(Vec<BankItem>);
pub struct CharacterBank {
item_id_counter: u32,
items: Vec<BankItem>
}
impl CharacterBank { impl CharacterBank {
pub fn new(mut items: Vec<BankItem>) -> CharacterBank { pub fn new(mut items: Vec<BankItem>) -> CharacterBank {
items.sort(); items.sort();
CharacterBank(items)
CharacterBank {
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_bank_items(&self) -> character::Bank { pub fn as_client_bank_items(&self) -> character::Bank {
self.0.iter()
self.items.iter()
.enumerate() .enumerate()
.fold(character::Bank::default(), |mut bank, (slot, item)| { .fold(character::Bank::default(), |mut bank, (slot, item)| {
bank.item_count = (slot + 1) as u32; bank.item_count = (slot + 1) as u32;
@ -152,7 +162,7 @@ impl CharacterBank {
} }
pub fn as_client_bank_request(&self) -> Vec<character::BankItem> { pub fn as_client_bank_request(&self) -> Vec<character::BankItem> {
self.0.iter()
self.items.iter()
.map(|item| { .map(|item| {
let bytes = item.as_client_bytes(); let bytes = item.as_client_bytes();
let mut data1 = [0; 12]; let mut data1 = [0; 12];
@ -179,7 +189,65 @@ impl CharacterBank {
} }
pub fn count(&self) -> usize { pub fn count(&self) -> usize {
self.0.len()
self.items.len()
}
pub fn deposit_item(&mut self, mut inventory_item: InventoryItemHandle, amount: usize) -> Option<&BankItem> {
let remove = match inventory_item.item_mut()? {
InventoryItem::Individual(individual_inventory_item) => {
if self.items.len() >= BANK_CAPACITY {
return None
}
self.items.push(BankItem::Individual(IndividualBankItem {
entity_id: individual_inventory_item.entity_id,
item_id: individual_inventory_item.item_id,
item: individual_inventory_item.item.clone(),
}));
true
},
InventoryItem::Stacked(stacked_inventory_item) => {
let existing_bank_item = self.items.iter_mut()
.find_map(|item| {
if let BankItem::Stacked(stacked_bank_item) = item {
if stacked_bank_item.tool == stacked_inventory_item.tool {
return Some(stacked_bank_item)
}
}
None
});
match existing_bank_item {
Some(stacked_bank_item) => {
if stacked_bank_item.count() + stacked_inventory_item.count() > stacked_inventory_item.tool.max_stack() {
return None
}
let mut deposited_entity_ids = stacked_inventory_item.take_entity_ids(amount)?;
stacked_bank_item.entity_ids.append(&mut deposited_entity_ids);
}
None => {
if self.items.len() >= BANK_CAPACITY {
return None
}
let deposited_entity_ids = stacked_inventory_item.take_entity_ids(amount)?;
self.item_id_counter += 1;
self.items.push(BankItem::Stacked(StackedBankItem {
entity_ids: deposited_entity_ids,
item_id: ClientItemId(self.item_id_counter),
tool: stacked_inventory_item.tool,
}))
}
}
stacked_inventory_item.count() == 0
}
};
if remove {
inventory_item.remove_from_inventory();
}
self.items.last()
} }
} }

4
src/ship/items/floor.rs

@ -228,9 +228,9 @@ impl RoomFloorItems {
pub fn drop_partial_stacked_inventory_item(&mut self, inventory_item: InventoryItemHandle, amount: usize, new_item_id: ClientItemId, item_drop_location: (MapArea, f32, f32, f32)) -> Option<&StackedFloorItem> { pub fn drop_partial_stacked_inventory_item(&mut self, inventory_item: InventoryItemHandle, amount: usize, new_item_id: ClientItemId, item_drop_location: (MapArea, f32, f32, f32)) -> Option<&StackedFloorItem> {
let consumed_item = inventory_item.consume(amount).ok()?; let consumed_item = inventory_item.consume(amount).ok()?;
if let ItemDetail::Tool(tool) = consumed_item.item {
if let ItemDetail::Tool(tool) = consumed_item.item() {
self.0.push(FloorItem::Stacked(StackedFloorItem { self.0.push(FloorItem::Stacked(StackedFloorItem {
entity_ids: consumed_item.entity_ids,
entity_ids: consumed_item.entity_ids(),
item_id: new_item_id, item_id: new_item_id,
tool: tool, tool: tool,
map_area: item_drop_location.0, map_area: item_drop_location.0,

46
src/ship/items/inventory.rs

@ -1,7 +1,7 @@
use std::cmp::Ordering; use std::cmp::Ordering;
use thiserror::Error; use thiserror::Error;
use libpso::character::character;//::InventoryItem; use libpso::character::character;//::InventoryItem;
use crate::entity::item::{ItemEntityId, ItemDetail};
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;
use crate::ship::items::floor::{IndividualFloorItem, StackedFloorItem}; use crate::ship::items::floor::{IndividualFloorItem, StackedFloorItem};
@ -29,6 +29,15 @@ impl StackedInventoryItem {
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)]
@ -68,6 +77,18 @@ impl InventoryItem {
} }
} }
pub fn item_type(&self) -> ItemType {
match self {
InventoryItem::Individual(individual_inventory_item) => {
individual_inventory_item.item.item_type()
},
InventoryItem::Stacked(stacked_inventory_item) => {
ItemType::Tool(stacked_inventory_item.tool.tool)
}
}
}
// TOOD: delete?
pub fn are_same_stackable_tool(&self, other_stacked_item: &StackedFloorItem) -> bool { pub fn are_same_stackable_tool(&self, other_stacked_item: &StackedFloorItem) -> bool {
match self { match self {
InventoryItem::Stacked(self_stacked_item) => { InventoryItem::Stacked(self_stacked_item) => {
@ -78,6 +99,7 @@ impl InventoryItem {
} }
} }
// TOOD: delete?
pub fn can_combine_stacks(&self, other_stacked_item: &StackedFloorItem) -> bool { pub fn can_combine_stacks(&self, other_stacked_item: &StackedFloorItem) -> bool {
match self { match self {
InventoryItem::Stacked(self_stacked_item) => { InventoryItem::Stacked(self_stacked_item) => {
@ -90,6 +112,7 @@ impl InventoryItem {
} }
// TODO: result // TODO: result
// TOOD: delete?
pub fn combine_stacks(&mut self, other_stacked_item: &mut StackedFloorItem) { pub fn combine_stacks(&mut self, other_stacked_item: &mut StackedFloorItem) {
match self { match self {
InventoryItem::Stacked(self_stacked_item) => { InventoryItem::Stacked(self_stacked_item) => {
@ -164,7 +187,7 @@ pub enum InventoryItemConsumeError {
} }
pub struct IndividualConsumedItem { pub struct IndividualConsumedItem {
pub entity_ids: ItemEntityId,
pub entity_id: ItemEntityId,
pub item: ItemDetail, pub item: ItemDetail,
} }
@ -185,7 +208,18 @@ impl ConsumedItem {
vec![individual_consumed_item.entity_id] vec![individual_consumed_item.entity_id]
}, },
ConsumedItem::Stacked(stacked_consumed_item) => { ConsumedItem::Stacked(stacked_consumed_item) => {
stacked_consumed_item.entity_ids
stacked_consumed_item.entity_ids.clone()
}
}
}
pub fn item(&self) -> ItemDetail {
match self {
ConsumedItem::Individual(individual_consumed_item) => {
individual_consumed_item.item.clone()
},
ConsumedItem::Stacked(stacked_consumed_item) => {
ItemDetail::Tool(stacked_consumed_item.tool)
} }
} }
} }
@ -202,6 +236,10 @@ impl<'a> InventoryItemHandle<'a> {
self.inventory.0.get(self.slot) self.inventory.0.get(self.slot)
} }
pub fn item_mut(&mut self) -> Option<&mut InventoryItem> {
self.inventory.0.get_mut(self.slot)
}
pub fn remove_from_inventory(self) { pub fn remove_from_inventory(self) {
self.inventory.0.remove(self.slot); self.inventory.0.remove(self.slot);
} }
@ -216,7 +254,7 @@ impl<'a> InventoryItemHandle<'a> {
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 {
entity_ids: individual_inventory_item.entity_id,
entity_id: individual_inventory_item.entity_id,
item: individual_inventory_item.item.clone() item: individual_inventory_item.item.clone()
})) }))
}, },

66
src/ship/items/manager.rs

@ -32,15 +32,18 @@ pub enum ItemManagerError {
Idunnoman, Idunnoman,
CouldNotSplitItem(ClientItemId), CouldNotSplitItem(ClientItemId),
CouldNotDropMeseta, CouldNotDropMeseta,
InvalidBankName(BankName),
NotEnoughTools(Tool, usize, usize), // have, expected NotEnoughTools(Tool, usize, usize), // have, expected
InventoryItemConsumeError(#[from] InventoryItemConsumeError), InventoryItemConsumeError(#[from] InventoryItemConsumeError),
BankFull,
} }
pub struct ItemManager { pub struct ItemManager {
id_counter: u32, id_counter: u32,
character_inventory: HashMap<CharacterEntityId, CharacterInventory>, character_inventory: HashMap<CharacterEntityId, CharacterInventory>,
character_bank: HashMap<CharacterEntityId, BTreeMap<BankName, CharacterBank>>,
//character_bank: HashMap<CharacterEntityId, BTreeMap<BankName, CharacterBank>>,
character_bank: HashMap<CharacterEntityId, CharacterBank>,
character_floor: HashMap<CharacterEntityId, RoomFloorItems>, character_floor: HashMap<CharacterEntityId, RoomFloorItems>,
character_room: HashMap<CharacterEntityId, RoomId>, character_room: HashMap<CharacterEntityId, RoomId>,
@ -101,7 +104,8 @@ impl ItemManager {
acc acc
}); });
let bank_items = items.into_iter()
// TODO: not using BankName anymore, clean this up
let mut bank_items = items.into_iter()
.filter_map(|item| { .filter_map(|item| {
match item.location { match item.location {
ItemLocation::Bank{name, ..} => Some((item.id, item.item, name)), ItemLocation::Bank{name, ..} => Some((item.id, item.item, name)),
@ -158,7 +162,7 @@ impl ItemManager {
.collect::<BTreeMap<_, _>>(); .collect::<BTreeMap<_, _>>();
let inventory = CharacterInventory::new(inventory_items.into_iter().map(|(_k, v)| v).take(30).collect()); let inventory = CharacterInventory::new(inventory_items.into_iter().map(|(_k, v)| v).take(30).collect());
self.character_inventory.insert(character.id, inventory); self.character_inventory.insert(character.id, inventory);
self.character_bank.insert(character.id, bank_items);
self.character_bank.insert(character.id, bank_items.remove(&BankName("".to_string())).unwrap_or(CharacterBank::new(Vec::new())));
} }
pub fn add_character_to_room(&mut self, room_id: RoomId, character: &CharacterEntity, area_client: AreaClient) { pub fn add_character_to_room(&mut self, room_id: RoomId, character: &CharacterEntity, area_client: AreaClient) {
@ -166,7 +170,7 @@ impl ItemManager {
let inventory = self.character_inventory.get_mut(&character.id).unwrap(); let inventory = self.character_inventory.get_mut(&character.id).unwrap();
inventory.initialize_item_ids(base_inventory_id); inventory.initialize_item_ids(base_inventory_id);
let base_bank_id = ((area_client.local_client.id() as u32) << 21) | 0x20000; let base_bank_id = ((area_client.local_client.id() as u32) << 21) | 0x20000;
let default_bank = self.character_bank.get_mut(&character.id).unwrap().get_mut(&BankName("".to_string()));
let default_bank = self.character_bank.get_mut(&character.id);//.unwrap().get_mut(&BankName("".to_string()));
match default_bank { match default_bank {
Some(default_bank) => { Some(default_bank) => {
default_bank.initialize_item_ids(base_bank_id); default_bank.initialize_item_ids(base_bank_id);
@ -192,11 +196,20 @@ impl ItemManager {
pub fn get_character_bank(&self, character: &CharacterEntity) -> Result<&CharacterBank, ItemManagerError> { pub fn get_character_bank(&self, character: &CharacterEntity) -> Result<&CharacterBank, ItemManagerError> {
Ok(self.character_bank Ok(self.character_bank
.get(&character.id) .get(&character.id)
.ok_or(ItemManagerError::NoCharacter(character.id))?
.get(&BankName("".to_string()))
.unwrap()) // TODO: make an error
.ok_or(ItemManagerError::NoCharacter(character.id))?)
//.get(&BankName("".to_string()))
//.ok_or(ItemManagerError::InvalidBankName(BankName("".to_string())))?)
} }
/*pub fn get_character_bank_mut(&mut self, character: &CharacterEntity) -> Result<&CharacterBank, ItemManagerError> {
Ok(self.character_bank
.get_mut(&character.id)
.ok_or(ItemManagerError::NoCharacter(character.id))?
.entry(BankName("".to_string()))
.or_insert(CharacterBank::new(Vec::new())))
//.ok_or(ItemManagerError::InvalidBankName(BankName("".to_string())))?)
}*/
pub fn remove_character_from_room(&mut self, character: &CharacterEntity) { pub fn remove_character_from_room(&mut self, character: &CharacterEntity) {
self.character_inventory.remove(&character.id); self.character_inventory.remove(&character.id);
self.character_floor.remove(&character.id); self.character_floor.remove(&character.id);
@ -256,7 +269,6 @@ impl ItemManager {
}, },
Some(FloorItem::Stacked(stacked_floor_item)) => { Some(FloorItem::Stacked(stacked_floor_item)) => {
let new_inventory_item = inventory.pick_up_stacked_floor_item(&stacked_floor_item); let new_inventory_item = inventory.pick_up_stacked_floor_item(&stacked_floor_item);
println!("new inv item! {:?}", new_inventory_item);
match new_inventory_item { match new_inventory_item {
Some((new_inventory_item, slot)) => { Some((new_inventory_item, slot)) => {
@ -506,15 +518,42 @@ impl ItemManager {
ItemLocation::Consumed).await; ItemLocation::Consumed).await;
} }
Ok(consumed_item.item)
Ok(consumed_item.item())
} }
pub async fn player_deposits_item<EG: EntityGateway>(&mut self, pub async fn player_deposits_item<EG: EntityGateway>(&mut self,
_entity_gateway: &mut EG,
_character: &CharacterEntity,
_item_id: ClientItemId,
_amount: usize)
entity_gateway: &mut EG,
character: &CharacterEntity,
item_id: ClientItemId,
amount: usize)
-> Result<(), ItemManagerError> { -> Result<(), ItemManagerError> {
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_deposit = inventory.get_item_handle_by_id(item_id).ok_or(ItemManagerError::NoSuchItemId(item_id))?;
let bank_item = bank.deposit_item(item_to_deposit, amount).ok_or(ItemManagerError::Idunnoman)?;
match bank_item {
BankItem::Individual(individual_bank_item) => {
entity_gateway.change_item_location(&individual_bank_item.entity_id,
ItemLocation::Bank {
character_id: character.id,
name: BankName("".to_string())
}).await;
},
BankItem::Stacked(stacked_bank_item) => {
for entity_id in &stacked_bank_item.entity_ids {
entity_gateway.change_item_location(entity_id,
ItemLocation::Bank {
character_id: character.id,
name: BankName("".to_string())
}).await;
}
}
}
Ok(()) Ok(())
} }
@ -524,6 +563,7 @@ impl ItemManager {
_item_id: ClientItemId, _item_id: ClientItemId,
_amount: usize) _amount: usize)
-> Result<(), ItemManagerError> { -> Result<(), ItemManagerError> {
Ok(()) Ok(())
} }
} }

11
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::{StackedFloorItem, FloorItem, CharacterBank};
use crate::ship::items::{ClientItemId, StackedFloorItem, FloorItem, CharacterBank};
use crate::ship::location::AreaClient; use crate::ship::location::AreaClient;
use std::convert::TryInto; use std::convert::TryInto;
@ -118,3 +118,12 @@ pub fn bank_item_list(bank: &CharacterBank) -> BankItemList {
items: bank.as_client_bank_request() items: bank.as_client_bank_request()
} }
} }
pub fn player_no_longer_has_item(area_client: AreaClient, item_id: ClientItemId, amount: u32) -> PlayerNoLongerHasItem {
PlayerNoLongerHasItem {
client: area_client.local_client.id(),
target: 0,
item_id: item_id.0,
amount: amount,
}
}

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

@ -72,45 +72,6 @@ where
let clients_in_area = client_location.get_clients_in_room(room_id).map_err(|err| -> ClientLocationError { err.into() })?; let clients_in_area = client_location.get_clients_in_room(room_id).map_err(|err| -> ClientLocationError { err.into() })?;
/*
let item_drop_packets = clients_in_area.into_iter()
.filter_map(|area_client| {
room.drop_table.get_drop(&monster.map_area, &monster.monster).map(|item_drop_type| {
warn!("drop is? {:?}", item_drop_type);
(area_client, item_drop_type)
})
})
.map(|(area_client, item_drop_type)| async {
let item_drop = ItemDrop {
map_area: monster.map_area,
x: request_item.x,
y: request_item.y,
z: request_item.z,
item: item_drop_type,
};
let client = clients.get_mut(&area_client.client).ok_or(ShipError::ClientNotFound(area_client.client))?;
let floor_item = item_manager.enemy_drop_item_on_local_floor(entity_gateway, &client.character, item_drop).await.unwrap(); // TODO: unwrap
let item_drop_msg = builder::message::item_drop(request_item.client, request_item.target, &floor_item)?;
// I am not able to manually specify a closure return type when also using the async keyword
let result: Result<(ClientId, SendShipPacket), ShipError> = Ok((area_client.client, SendShipPacket::Message(Message::new(GameMessage::ItemDrop(item_drop_msg)))));
result
})
.map(|item_drop_pkt| async {
item_drop_pkt.await
});
let item_drop_packets = join_all(item_drop_packets).await.into_iter()
.filter_map(|item_drop_pkt| {
// TODO: log errors here
item_drop_pkt.ok()
});
//.collect::<Vec<_>>(); // TODO: can EntityGateway be Sync?
Ok(Box::new(item_drop_packets))
*/
let client_and_drop = clients_in_area.into_iter() let client_and_drop = clients_in_area.into_iter()
.filter_map(|area_client| { .filter_map(|area_client| {
room.drop_table.get_drop(&monster.map_area, &monster.monster).map(|item_drop_type| { room.drop_table.get_drop(&monster.map_area, &monster.monster).map(|item_drop_type| {
@ -209,41 +170,6 @@ EG: EntityGateway
let clients_in_area = client_location.get_clients_in_room(room_id).map_err(|err| -> ClientLocationError { err.into() })?; let clients_in_area = client_location.get_clients_in_room(room_id).map_err(|err| -> ClientLocationError { err.into() })?;
/*let item_drop_packets = clients_in_area.into_iter()
.filter_map(|area_client| {
room.drop_table.get_box_drop(&box_object.map, &box_object).map(|item_drop_type| {
warn!("drop is? {:?}", item_drop_type);
(area_client, item_drop_type)
})
})
.map(async move |(area_client, item_drop_type)| -> Result<_, ShipError> {
let item_drop = ItemDrop {
map_area: box_object.map,
x: box_drop_request.x,
y: 0.0,
z: box_drop_request.z,
item: item_drop_type,
};
let client = clients.get_mut(&area_client.client).ok_or(ShipError::ClientNotFound(area_client.client))?;
let floor_item = item_manager.enemy_drop_item_on_local_floor(entity_gateway, &client.character, item_drop).await.unwrap(); // TODO: unwrap
let item_drop_msg = builder::message::item_drop(box_drop_request.client, box_drop_request.target, &floor_item)?;
Ok((area_client.client, SendShipPacket::Message(Message::new(GameMessage::ItemDrop(item_drop_msg)))))
})
/*.filter_map(|item_drop_pkt| {
// TODO: log errors here
item_drop_pkt.ok()
})
.collect::<Vec<_>>(); // TODO: can EntityGateway be Sync?*/
;
let item_drop_packets = join_all(item_drop_packets).await.into_iter()
.filter_map(|item_drop_pkt| {
// TODO: log errors here
item_drop_pkt.ok()
});
Ok(Box::new(item_drop_packets))
*/
let client_and_drop = clients_in_area.into_iter() let client_and_drop = clients_in_area.into_iter()
.filter_map(|area_client| { .filter_map(|area_client| {
room.drop_table.get_box_drop(&box_object.map, &box_object).map(|item_drop_type| { room.drop_table.get_box_drop(&box_object.map, &box_object).map(|item_drop_type| {
@ -287,6 +213,7 @@ pub async fn send_bank_list(id: ClientId,
pub async fn bank_interaction<EG>(id: ClientId, pub async fn bank_interaction<EG>(id: ClientId,
bank_interaction: &BankInteraction, bank_interaction: &BankInteraction,
entity_gateway: &mut EG, entity_gateway: &mut EG,
client_location: &ClientLocation,
clients: &mut Clients, clients: &mut Clients,
item_manager: &mut ItemManager) item_manager: &mut ItemManager)
-> Result<Box<dyn Iterator<Item = (ClientId, SendShipPacket)> + Send>, ShipError> -> Result<Box<dyn Iterator<Item = (ClientId, SendShipPacket)> + Send>, ShipError>
@ -294,32 +221,50 @@ where
EG: EntityGateway EG: EntityGateway
{ {
let client = clients.get_mut(&id).ok_or(ShipError::ClientNotFound(id))?; let client = clients.get_mut(&id).ok_or(ShipError::ClientNotFound(id))?;
match bank_interaction.action {
let area_client = client_location.get_local_client(id).map_err(|err| -> ClientLocationError { err.into() })?;
let clients_in_area = client_location.get_all_clients_by_client(id).map_err(|err| -> ClientLocationError { err.into() })?;
let bank_action_pkts = match bank_interaction.action {
BANK_ACTION_DEPOSIT => { BANK_ACTION_DEPOSIT => {
if bank_interaction.item_id == 0xFFFFFFFF { if bank_interaction.item_id == 0xFFFFFFFF {
if client.character.meseta < bank_interaction.meseta_amount && client.character.bank_meseta <= 999999 {
client.character.meseta += bank_interaction.meseta_amount;
if client.character.meseta > bank_interaction.meseta_amount && (bank_interaction.meseta_amount + client.character.bank_meseta) <= 999999 {
client.character.meseta -= bank_interaction.meseta_amount;
client.character.bank_meseta += bank_interaction.meseta_amount;
entity_gateway.save_character(&client.character).await; entity_gateway.save_character(&client.character).await;
} }
Vec::new()
} }
else { else {
//let inventory_item = item_manager.get_inventory_item_by_id(&client.character, ClientItemId(bank_interaction.item_id))?;
item_manager.player_deposits_item(entity_gateway, &client.character, ClientItemId(bank_interaction.item_id), bank_interaction.item_amount as usize).await?; item_manager.player_deposits_item(entity_gateway, &client.character, ClientItemId(bank_interaction.item_id), bank_interaction.item_amount as usize).await?;
let player_no_longer_has_item = builder::message::player_no_longer_has_item(area_client, ClientItemId(bank_interaction.item_id), bank_interaction.item_amount as u32);
vec![SendShipPacket::Message(Message::new(GameMessage::PlayerNoLongerHasItem(player_no_longer_has_item)))]
} }
}, },
BANK_ACTION_WITHDRAW => { BANK_ACTION_WITHDRAW => {
if bank_interaction.item_id == 0xFFFFFFFF { if bank_interaction.item_id == 0xFFFFFFFF {
if client.character.meseta + bank_interaction.meseta_amount <= 999999 { if client.character.meseta + bank_interaction.meseta_amount <= 999999 {
client.character.meseta += bank_interaction.meseta_amount; client.character.meseta += bank_interaction.meseta_amount;
client.character.bank_meseta -= bank_interaction.meseta_amount;
entity_gateway.save_character(&client.character).await; entity_gateway.save_character(&client.character).await;
} }
Vec::new()
} }
else { else {
//let bank_item = item_manager.get_bank_item_by_id(&client.character, ClientItemId(bank_interaction.item_id))?;
item_manager.player_withdraws_item(entity_gateway, &client.character, ClientItemId(bank_interaction.item_id), bank_interaction.item_amount as usize).await?; item_manager.player_withdraws_item(entity_gateway, &client.character, ClientItemId(bank_interaction.item_id), bank_interaction.item_amount as usize).await?;
Vec::new()
} }
}, },
_ => {}
_ => {
Vec::new()
} }
Ok(Box::new(None.into_iter()))
};
Ok(Box::new(clients_in_area.into_iter()
.map(move |c| {
bank_action_pkts.clone().into_iter()
.map(move |pkt| {
(c.client, pkt)
})
})
.flatten()
))
} }

12
src/ship/packet/handler/message.rs

@ -117,7 +117,7 @@ pub fn drop_coordinates(id: ClientId,
} }
pub async fn split_item_stack<EG>(id: ClientId, pub async fn split_item_stack<EG>(id: ClientId,
split_item_stack: &PlayerSplitItemStack,
no_longer_has_item: &PlayerNoLongerHasItem,
entity_gateway: &mut EG, entity_gateway: &mut EG,
client_location: &ClientLocation, client_location: &ClientLocation,
clients: &mut Clients, clients: &mut Clients,
@ -131,12 +131,12 @@ where
let room_id = client_location.get_room(id).map_err(|err| -> ClientLocationError { err.into() })?; let room_id = client_location.get_room(id).map_err(|err| -> ClientLocationError { err.into() })?;
let drop_location = client.item_drop_location.ok_or(ShipError::ItemDropLocationNotSet)?; let drop_location = client.item_drop_location.ok_or(ShipError::ItemDropLocationNotSet)?;
if drop_location.item_id.0 != split_item_stack.item_id {
return Err(ShipError::DropInvalidItemId(split_item_stack.item_id));
if drop_location.item_id.0 != no_longer_has_item.item_id {
return Err(ShipError::DropInvalidItemId(no_longer_has_item.item_id));
} }
if split_item_stack.item_id == 0xFFFFFFFF {
let dropped_meseta = item_manager.player_drops_meseta_on_shared_floor(entity_gateway, &mut client.character, drop_location, split_item_stack.amount as u32).await?;
if no_longer_has_item.item_id == 0xFFFFFFFF {
let dropped_meseta = item_manager.player_drops_meseta_on_shared_floor(entity_gateway, &mut client.character, drop_location, no_longer_has_item.amount as u32).await?;
let dropped_meseta_pkt = builder::message::drop_split_meseta_stack(area_client, &dropped_meseta)?; let dropped_meseta_pkt = builder::message::drop_split_meseta_stack(area_client, &dropped_meseta)?;
client.item_drop_location = None; client.item_drop_location = None;
@ -148,7 +148,7 @@ where
}))) })))
} }
else { else {
let dropped_item = item_manager.player_drops_partial_stack_on_shared_floor(entity_gateway, &client.character, drop_location.item_id, drop_location, split_item_stack.amount as usize).await?;
let dropped_item = item_manager.player_drops_partial_stack_on_shared_floor(entity_gateway, &client.character, drop_location.item_id, drop_location, no_longer_has_item.amount as usize).await?;
let dropped_item_pkt = builder::message::drop_split_stack(area_client, dropped_item)?; let dropped_item_pkt = builder::message::drop_split_stack(area_client, dropped_item)?;
client.item_drop_location = None; client.item_drop_location = None;

6
src/ship/ship.rs

@ -283,8 +283,8 @@ impl<EG: EntityGateway> ShipServerState<EG> {
GameMessage::DropCoordinates(drop_coordinates) => { GameMessage::DropCoordinates(drop_coordinates) => {
handler::message::drop_coordinates(id, drop_coordinates, &self.client_location, &mut self.clients, &self.rooms) handler::message::drop_coordinates(id, drop_coordinates, &self.client_location, &mut self.clients, &self.rooms)
}, },
GameMessage::PlayerSplitItemStack(split_item_stack) => {
handler::message::split_item_stack(id, split_item_stack, &mut self.entity_gateway, &mut self.client_location, &mut self.clients, &mut self.item_manager).await
GameMessage::PlayerNoLongerHasItem(no_longer_has_item) => {
handler::message::split_item_stack(id, no_longer_has_item, &mut self.entity_gateway, &mut self.client_location, &mut self.clients, &mut self.item_manager).await
}, },
GameMessage::PlayerChangedMap(_) | GameMessage::PlayerChangedMap2(_) | GameMessage::TellOtherPlayerMyLocation(_) | GameMessage::PlayerChangedMap(_) | GameMessage::PlayerChangedMap2(_) | GameMessage::TellOtherPlayerMyLocation(_) |
GameMessage::PlayerWarpingToFloor(_) | GameMessage::PlayerTeleported(_) | GameMessage::PlayerStopped(_) | GameMessage::PlayerWarpingToFloor(_) | GameMessage::PlayerTeleported(_) | GameMessage::PlayerStopped(_) |
@ -327,7 +327,7 @@ impl<EG: EntityGateway> ShipServerState<EG> {
handler::direct_message::send_bank_list(id, &self.clients, &mut self.item_manager).await handler::direct_message::send_bank_list(id, &self.clients, &mut self.item_manager).await
}, },
GameMessage::BankInteraction(bank_interaction) => { GameMessage::BankInteraction(bank_interaction) => {
handler::direct_message::bank_interaction(id, bank_interaction, &mut self.entity_gateway, &mut self.clients, &mut self.item_manager).await
handler::direct_message::bank_interaction(id, bank_interaction, &mut self.entity_gateway, &self.client_location, &mut self.clients, &mut self.item_manager).await
}, },
_ => { _ => {
let cmsg = msg.clone(); let cmsg = msg.clone();

788
tests/test_bank.rs

@ -197,7 +197,6 @@ async fn test_request_bank_items_sorted() {
unknown: 0, unknown: 0,
})))).await.unwrap().collect::<Vec<_>>(); })))).await.unwrap().collect::<Vec<_>>();
println!("{:?}", packets);
assert!(matches!(&packets[0], (_, SendShipPacket::BankItemList (bank_item_list)) assert!(matches!(&packets[0], (_, SendShipPacket::BankItemList (bank_item_list))
if bank_item_list.item_count == 3 if bank_item_list.item_count == 3
&& bank_item_list.size == 0x18 * 3 + 0x14 && bank_item_list.size == 0x18 * 3 + 0x14
@ -208,12 +207,781 @@ async fn test_request_bank_items_sorted() {
} }
//test_deposit_individual_item
//test_deposit_stacked_item
//test_deposit_stacked_item_with_stack_already_in_bank
//test_deposit_stacked_item_when_full_stack_in_bank
//test_deposit_individual_item_in_full_bank
//test_deposit_stacked_item_in_full_bank
//test_deposit_meseta
//test_deposit_too_much_meseta
//test_deposit_when_bank_has_max_meseta
#[async_std::test]
async fn test_deposit_individual_item() {
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::Saber,
grind: 0,
special: None,
attrs: [None, None, None],
tekked: true,
}
),
location: item::ItemLocation::Inventory {
character_id: char1.id,
slot: 0,
equipped: false,
}
}).await;
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,
}
),
location: item::ItemLocation::Inventory {
character_id: char1.id,
slot: 1,
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: 0x10001,
action: 0,
item_amount: 0,
meseta_amount: 0,
unknown: 0,
})))).await.unwrap().collect::<Vec<_>>();
assert!(packets.len() == 1);
assert!(matches!(&packets[0], (_, SendShipPacket::Message(Message {msg: GameMessage::PlayerNoLongerHasItem(player_no_longer_has_item)}))
if player_no_longer_has_item.item_id == 0x10001
&& player_no_longer_has_item.amount == 0
));
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(2)]);
}
#[async_std::test]
async fn test_deposit_stacked_item() {
let mut entity_gateway = InMemoryGateway::new();
let (_user1, char1) = new_user_character(&mut entity_gateway, "a1", "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::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: 0x10000,
action: 0,
item_amount: 3,
meseta_amount: 0,
unknown: 0,
})))).await.unwrap().collect::<Vec<_>>();
assert!(packets.len() == 1);
assert!(matches!(&packets[0], (_, SendShipPacket::Message(Message {msg: GameMessage::PlayerNoLongerHasItem(player_no_longer_has_item)}))
if player_no_longer_has_item.item_id == 0x10000
&& player_no_longer_has_item.amount == 3
));
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), item::ItemEntityId(3)]);
}
#[async_std::test]
async fn test_deposit_partial_stacked_item() {
let mut entity_gateway = InMemoryGateway::new();
let (_user1, char1) = new_user_character(&mut entity_gateway, "a1", "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::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: 0x10000,
action: 0,
item_amount: 2,
meseta_amount: 0,
unknown: 0,
})))).await.unwrap().collect::<Vec<_>>();
assert!(packets.len() == 1);
assert!(matches!(&packets[0], (_, SendShipPacket::Message(Message {msg: GameMessage::PlayerNoLongerHasItem(player_no_longer_has_item)}))
if player_no_longer_has_item.item_id == 0x10000
&& player_no_longer_has_item.amount == 2
));
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 == vec![item::ItemEntityId(3)]);
}
#[async_std::test]
async fn test_deposit_stacked_item_with_stack_already_in_bank() {
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::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;
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: 0x10000,
action: 0,
item_amount: 2,
meseta_amount: 0,
unknown: 0,
})))).await.unwrap().collect::<Vec<_>>();
assert!(packets.len() == 1);
assert!(matches!(&packets[0], (_, SendShipPacket::Message(Message {msg: GameMessage::PlayerNoLongerHasItem(player_no_longer_has_item)}))
if player_no_longer_has_item.item_id == 0x10000
&& player_no_longer_has_item.amount == 2
));
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), item::ItemEntityId(3), item::ItemEntityId(4)]);
}
#[async_std::test]
async fn test_deposit_stacked_item_with_full_stack_in_bank() {
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::Inventory {
character_id: char1.id,
slot: 0,
equipped: false,
}
}).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::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;
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: 0x10000,
action: 0,
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.len() == 10);
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_deposit_individual_item_in_full_bank() {
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::Inventory {
character_id: char1.id,
slot: 0,
equipped: false,
}
}).await;
for _ in 0..200 {
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;
}
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: 0x10000,
action: 0,
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.len() == 200);
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_deposit_stacked_item_in_full_bank() {
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::Inventory {
character_id: char1.id,
slot: 0,
equipped: false,
}
}).await;
}
for _ in 0..200 {
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;
}
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: 0x10000,
action: 0,
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.len() == 200);
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_deposit_stacked_item_in_full_bank_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::Inventory {
character_id: char1.id,
slot: 0,
equipped: false,
}
}).await;
}
for _ in 0..199 {
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 _ 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;
}
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: 0x10000,
action: 0,
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() == 203);
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() == 0);
}
#[async_std::test]
async fn test_deposit_meseta() {
let mut entity_gateway = InMemoryGateway::new();
let (user1, mut char1) = new_user_character(&mut entity_gateway, "a1", "a").await;
char1.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: 0,
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();
println!("meseta {}", char.meseta);
assert!(char.meseta == 277);
assert!(char.bank_meseta == 23);
}
#[async_std::test]
async fn test_deposit_too_much_meseta() {
let mut entity_gateway = InMemoryGateway::new();
let (user1, mut char1) = new_user_character(&mut entity_gateway, "a1", "a").await;
char1.meseta = 300;
char1.bank_meseta = 999980;
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: 0,
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();
println!("meseta {}", char.meseta);
assert!(char.meseta == 300);
assert!(char.bank_meseta == 999980);
}
#[async_std::test]
async fn test_deposit_meseta_when_bank_is_maxed() {
let mut entity_gateway = InMemoryGateway::new();
let (user1, mut char1) = new_user_character(&mut entity_gateway, "a1", "a").await;
char1.meseta = 300;
char1.bank_meseta = 999999;
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: 0,
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();
println!("meseta {}", char.meseta);
assert!(char.meseta == 300);
assert!(char.bank_meseta == 999999);
}

11
tests/test_item_pickup.rs

@ -205,7 +205,7 @@ async fn test_pick_up_meseta_when_inventory_full() {
z: 0.0, z: 0.0,
})))).await.unwrap().for_each(drop); })))).await.unwrap().for_each(drop);
ship.handle(ClientId(2), &RecvShipPacket::Message(Message::new(GameMessage::PlayerSplitItemStack(PlayerSplitItemStack {
ship.handle(ClientId(2), &RecvShipPacket::Message(Message::new(GameMessage::PlayerNoLongerHasItem(PlayerNoLongerHasItem {
client: 0, client: 0,
target: 0, target: 0,
item_id: 0xFFFFFFFF, item_id: 0xFFFFFFFF,
@ -445,7 +445,7 @@ async fn test_can_not_drop_more_meseta_than_is_held() {
z: 0.0, z: 0.0,
})))).await.unwrap().for_each(drop); })))).await.unwrap().for_each(drop);
let split_attempt = ship.handle(ClientId(1), &RecvShipPacket::Message(Message::new(GameMessage::PlayerSplitItemStack(PlayerSplitItemStack {
let split_attempt = ship.handle(ClientId(1), &RecvShipPacket::Message(Message::new(GameMessage::PlayerNoLongerHasItem(PlayerNoLongerHasItem {
client: 0, client: 0,
target: 0, target: 0,
item_id: 0xFFFFFFFF, item_id: 0xFFFFFFFF,
@ -565,7 +565,7 @@ async fn test_can_not_pick_up_meseta_when_full() {
z: 0.0, z: 0.0,
})))).await.unwrap().for_each(drop); })))).await.unwrap().for_each(drop);
ship.handle(ClientId(2), &RecvShipPacket::Message(Message::new(GameMessage::PlayerSplitItemStack(PlayerSplitItemStack {
ship.handle(ClientId(2), &RecvShipPacket::Message(Message::new(GameMessage::PlayerNoLongerHasItem(PlayerNoLongerHasItem {
client: 0, client: 0,
target: 0, target: 0,
item_id: 0xFFFFFFFF, item_id: 0xFFFFFFFF,
@ -579,7 +579,6 @@ async fn test_can_not_pick_up_meseta_when_full() {
map_area: 0, map_area: 0,
unknown: [0; 3] unknown: [0; 3]
})))).await.unwrap().collect::<Vec<_>>(); })))).await.unwrap().collect::<Vec<_>>();
println!("pkts {:?}", packets);
assert!(packets.len() == 0); assert!(packets.len() == 0);
let characters1 = entity_gateway.get_characters_by_user(&user1).await; let characters1 = entity_gateway.get_characters_by_user(&user1).await;
@ -622,7 +621,7 @@ async fn test_meseta_caps_at_999999_when_trying_to_pick_up_more() {
z: 0.0, z: 0.0,
})))).await.unwrap().for_each(drop); })))).await.unwrap().for_each(drop);
ship.handle(ClientId(2), &RecvShipPacket::Message(Message::new(GameMessage::PlayerSplitItemStack(PlayerSplitItemStack {
ship.handle(ClientId(2), &RecvShipPacket::Message(Message::new(GameMessage::PlayerNoLongerHasItem(PlayerNoLongerHasItem {
client: 0, client: 0,
target: 0, target: 0,
item_id: 0xFFFFFFFF, item_id: 0xFFFFFFFF,
@ -688,7 +687,7 @@ async fn test_player_drops_partial_stack_and_other_player_picks_it_up() {
z: 0.0, z: 0.0,
})))).await.unwrap().for_each(drop); })))).await.unwrap().for_each(drop);
ship.handle(ClientId(1), &RecvShipPacket::Message(Message::new(GameMessage::PlayerSplitItemStack(PlayerSplitItemStack {
ship.handle(ClientId(1), &RecvShipPacket::Message(Message::new(GameMessage::PlayerNoLongerHasItem(PlayerNoLongerHasItem {
client: 0, client: 0,
target: 0, target: 0,
item_id: 0x10000, item_id: 0x10000,

Loading…
Cancel
Save