From 72d72801e1199974335c2098d22e637ebf237489 Mon Sep 17 00:00:00 2001 From: jake Date: Sun, 12 Dec 2021 22:55:08 -0700 Subject: [PATCH] trade meseta --- src/ship/items/manager.rs | 38 ++++--- src/ship/packet/builder/message.rs | 12 ++ src/ship/packet/handler/trade.rs | 174 ++++++++++++++++++----------- src/ship/trade.rs | 7 +- 4 files changed, 145 insertions(+), 86 deletions(-) diff --git a/src/ship/items/manager.rs b/src/ship/items/manager.rs index 37808a4..efd9a64 100644 --- a/src/ship/items/manager.rs +++ b/src/ship/items/manager.rs @@ -15,7 +15,7 @@ use crate::ship::trade::TradeItem; use crate::ship::drops::{ItemDrop, ItemDropType}; use crate::ship::location::{AreaClient, RoomId}; use crate::ship::shops::ShopItem; -use crate::ship::packet::handler::trade::TradeError; +use crate::ship::packet::handler::trade::{TradeError, OTHER_MESETA_ITEM_ID}; use crate::ship::items::bank::*; use crate::ship::items::floor::*; @@ -938,8 +938,8 @@ impl ItemManager { pub async fn trade_items(&mut self, entity_gateway: &mut EG, room_id: RoomId, - p1: (&AreaClient, &CharacterEntity, &Vec), - p2: (&AreaClient, &CharacterEntity, &Vec)) + p1: (&AreaClient, &CharacterEntity, &Vec, usize), + p2: (&AreaClient, &CharacterEntity, &Vec, usize)) -> Result, anyhow::Error> { let it = ItemTransaction::new(&self, (p1, p2, room_id)) .act(|it, (p1, p2, room_id)| -> Result<_, anyhow::Error> { @@ -980,9 +980,6 @@ impl ItemManager { Err(TradeError::NoInventorySpace) }, } - }, - TradeItem::Meseta(..) => { - Ok(acc) } } }) @@ -1038,18 +1035,28 @@ impl ItemManager { }), )) } - }, - TradeItem::Meseta(amount) => { - Some((None, // is there a packet that informs other clients about meseta changes? - Box::new(TradeMeseta { - src_character_id: src_client.1.id, - dest_character_id: dest_client.1.id, - amount: *amount, - }), - )) } } }) + .chain( + if src_client.3 > 0 { + Box::new(std::iter::once(Some( + (Some(ItemToTrade { + add_to: *dest_client.0, + remove_from: *src_client.0, + current_item_id: OTHER_MESETA_ITEM_ID, + new_item_id: OTHER_MESETA_ITEM_ID, + item_detail: ItemToTradeDetail::Meseta(src_client.3) + }), + Box::new(TradeMeseta { + src_character_id: src_client.1.id, + dest_character_id: dest_client.1.id, + amount: src_client.3, + }) as Box>)))) as Box> + } + else { + Box::new(std::iter::empty()) as Box> + }) .collect::>>() }); @@ -1082,6 +1089,7 @@ impl ItemManager { pub enum ItemToTradeDetail { Individual(ItemDetail), Stacked(Tool, usize), + Meseta(usize), } #[derive(Debug)] diff --git a/src/ship/packet/builder/message.rs b/src/ship/packet/builder/message.rs index 1361b1d..186e637 100644 --- a/src/ship/packet/builder/message.rs +++ b/src/ship/packet/builder/message.rs @@ -53,6 +53,18 @@ pub fn create_stacked_item(area_client: AreaClient, item_id: ClientItemId, tool: }) } +pub fn create_meseta(area_client: AreaClient, amount: usize) -> CreateItem { + let bytes: [u8; 12] = [4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]; + CreateItem { + client: area_client.local_client.id(), + target: 0, + item_data: bytes, + item_id: 0xFFFFFFFF, + item_data2: u32::to_le_bytes(amount as u32), + unknown: 0, + } +} + pub fn create_withdrawn_inventory_item(area_client: AreaClient, item: &InventoryItem) -> Result { let bytes = item.as_client_bytes(); Ok(CreateItem { diff --git a/src/ship/packet/handler/trade.rs b/src/ship/packet/handler/trade.rs index bd4134a..8300bb9 100644 --- a/src/ship/packet/handler/trade.rs +++ b/src/ship/packet/handler/trade.rs @@ -18,6 +18,10 @@ use libpso::utf8_to_utf16_array; use crate::ship::packet::builder; use crate::ship::shops::{ShopItem, ToolShopItem, ArmorShopItem}; +pub const MESETA_ITEM_ID: ClientItemId = ClientItemId(0xFFFFFF01); +pub const OTHER_MESETA_ITEM_ID: ClientItemId = ClientItemId(0xFFFFFFFF); + + #[derive(thiserror::Error, Debug, PartialEq, Eq)] pub enum TradeError { #[error("no partner")] @@ -36,6 +40,8 @@ pub enum TradeError { NoInventorySpace, #[error("no space in stack")] NoStackSpace, + #[error("invalid meseta amount")] + InvalidMeseta, } @@ -60,7 +66,7 @@ where let trade_partner = client_location.get_client_neighbors(id)? .into_iter() .filter(|ac| { - ac.local_client.id() == trade_request.client + ac.local_client.id() == target as u8 //trade_request.client }) .next() .ok_or(TradeError::CouldNotFindTradePartner)?; @@ -107,18 +113,23 @@ where if this.status == TradeStatus::Trading && other.status == TradeStatus::Trading { let client = clients.get(&this.client()).ok_or(ShipError::ClientNotFound(this.client()))?; let inventory = item_manager.get_character_inventory(&client.character)?; - let item = inventory.get_item_by_id(ClientItemId(item_id)).ok_or(ItemManagerError::NoSuchItemId(ClientItemId(item_id)))?; + if ClientItemId(item_id) == MESETA_ITEM_ID { + this.meseta += amount as usize; + } + else { + let item = inventory.get_item_by_id(ClientItemId(item_id)).ok_or(ItemManagerError::NoSuchItemId(ClientItemId(item_id)))?; - match item { - InventoryItem::Individual(_) => { - this.items.push(TradeItem::Individual(ClientItemId(item_id))); - }, - InventoryItem::Stacked(stacked_item) => { - if stacked_item.count() < amount as usize { - return Err(TradeError::InvalidStackAmount(ClientItemId(item_id), amount as usize).into()); - } - this.items.push(TradeItem::Stacked(ClientItemId(item_id), amount as usize)); - }, + match item { + InventoryItem::Individual(_) => { + this.items.push(TradeItem::Individual(ClientItemId(item_id))); + }, + InventoryItem::Stacked(stacked_item) => { + if stacked_item.count() < amount as usize { + return Err(TradeError::InvalidStackAmount(ClientItemId(item_id), amount as usize).into()); + } + this.items.push(TradeItem::Stacked(ClientItemId(item_id), amount as usize)); + }, + } } let trade_request = trade_request.clone(); @@ -148,33 +159,38 @@ where if this.status == TradeStatus::Trading && other.status == TradeStatus::Trading { let client = clients.get(&this.client())?; //.ok_or(ShipError::ClientNotFound(id)).ok()?; let inventory = item_manager.get_character_inventory(&client.character).ok()?; - let item = inventory.get_item_by_id(ClientItemId(item_id))?; + if ClientItemId(item_id) == MESETA_ITEM_ID { + this.meseta -= amount as usize; + } + else { + let item = inventory.get_item_by_id(ClientItemId(item_id))?; - match item { - InventoryItem::Individual(_) => { - this.items.retain(|item| { - item.item_id() != ClientItemId(item_id) - }) - //this.items.push(TradeItem::Individual(ClientItemId(item_id))); - }, - InventoryItem::Stacked(stacked_item) => { - let trade_item_index = this.items.iter() - .position(|item| { - item.item_id() == ClientItemId(item_id) - })?; + match item { + InventoryItem::Individual(_) => { + this.items.retain(|item| { + item.item_id() != ClientItemId(item_id) + }) + //this.items.push(TradeItem::Individual(ClientItemId(item_id))); + }, + InventoryItem::Stacked(stacked_item) => { + let trade_item_index = this.items.iter() + .position(|item| { + item.item_id() == ClientItemId(item_id) + })?; - match this.items[trade_item_index].stacked()?.1.cmp(&(amount as usize)) { - std::cmp::Ordering::Greater => { - this.items[trade_item_index].stacked()?.1 -= amount as usize; - }, - std::cmp::Ordering::Equal => { - this.items.remove(trade_item_index); - }, - std::cmp::Ordering::Less => { - return None + match this.items[trade_item_index].stacked()?.1.cmp(&(amount as usize)) { + std::cmp::Ordering::Greater => { + this.items[trade_item_index].stacked()?.1 -= amount as usize; + }, + std::cmp::Ordering::Equal => { + this.items.remove(trade_item_index); + }, + std::cmp::Ordering::Less => { + return None + } } - } - }, + }, + } } let trade_request = trade_request.clone(); Some(Box::new(client_location.get_all_clients_by_client(id).unwrap().into_iter() @@ -277,7 +293,6 @@ where Ok(trades .with(&id, |this, other| -> Result + Send>, anyhow::Error> { if status_is_not(&this.status, &[TradeStatus::FinalConfirm]) || status_is_not(&other.status, &[TradeStatus::FinalConfirm, TradeStatus::ItemsChecked]) { - //if this.status != TradeStatus::FinalConfirm || (other.status != TradeStatus::FinalConfirm || other.status != TradeStatus::ItemsChecked) { return Err(TradeError::MismatchedStatus.into()) } @@ -285,37 +300,48 @@ where let inventory = item_manager.get_character_inventory(&client.character)?; let item_blobs = items_to_trade.items.iter().take(items_to_trade.count as usize); - let trade_items = item_blobs + item_blobs .map(|item| { - // TOOD: meseta? - let real_item = inventory.get_item_by_id(ClientItemId(item.item_id)) - .ok_or(ItemManagerError::NoSuchItemId(ClientItemId(item.item_id)))?; - let trade_item_bytes: [u8; 16] = item.item_data.iter() - .chain(item.item_data2.iter()) - .cloned().collect::>() - .try_into() - .unwrap(); - match real_item { - InventoryItem::Individual(individual_inventory_item) => { - if real_item.as_client_bytes() == trade_item_bytes { - Ok(TradeItem::Individual(individual_inventory_item.item_id)) - } - else { - Err(TradeError::ClientItemIdDidNotMatchItem(ClientItemId(item.item_id), trade_item_bytes).into()) - } - }, - InventoryItem::Stacked(stacked_inventory_item) => { - if real_item.as_client_bytes()[0..4] == trade_item_bytes[0..4] { - let amount = trade_item_bytes[5] as usize; - if amount <= stacked_inventory_item.entity_ids.len() { - Ok(TradeItem::Stacked(stacked_inventory_item.item_id, amount)) + if ClientItemId(item.item_id) == OTHER_MESETA_ITEM_ID { + if item.item_data[0] != 4 { + return Err(TradeError::InvalidItemId(ClientItemId(item.item_id)).into()) + } + let amount = u32::from_le_bytes(item.item_data2); + if amount > client.character.meseta { + return Err(TradeError::InvalidMeseta.into()) + } + Ok(()) + } + else { + let real_item = inventory.get_item_by_id(ClientItemId(item.item_id)) + .ok_or(ItemManagerError::NoSuchItemId(ClientItemId(item.item_id)))?; + let trade_item_bytes: [u8; 16] = item.item_data.iter() + .chain(item.item_data2.iter()) + .cloned().collect::>() + .try_into() + .unwrap(); + match real_item { + InventoryItem::Individual(individual_inventory_item) => { + if real_item.as_client_bytes() == trade_item_bytes { + Ok(()) } else { - Err(TradeError::InvalidStackAmount(stacked_inventory_item.item_id, amount).into()) + Err(TradeError::ClientItemIdDidNotMatchItem(ClientItemId(item.item_id), trade_item_bytes).into()) + } + }, + InventoryItem::Stacked(stacked_inventory_item) => { + if real_item.as_client_bytes()[0..4] == trade_item_bytes[0..4] { + let amount = trade_item_bytes[5] as usize; + if amount <= stacked_inventory_item.entity_ids.len() { + Ok(()) + } + else { + Err(TradeError::InvalidStackAmount(stacked_inventory_item.item_id, amount).into()) + } + } + else { + Err(TradeError::ClientItemIdDidNotMatchItem(ClientItemId(item.item_id), trade_item_bytes).into()) } - } - else { - Err(TradeError::ClientItemIdDidNotMatchItem(ClientItemId(item.item_id), trade_item_bytes).into()) } } } @@ -323,7 +349,15 @@ where .collect::, anyhow::Error>>()?; this.status = TradeStatus::ItemsChecked; - Ok(Box::new(std::iter::once((other.client(), SendShipPacket::AcknowledgeTrade(AcknowledgeTrade {}))))) + if this.status == TradeStatus::ItemsChecked && other.status == TradeStatus::ItemsChecked { + Ok(Box::new(vec![ + (this.client(), SendShipPacket::AcknowledgeTrade(AcknowledgeTrade {})), + (other.client(), SendShipPacket::AcknowledgeTrade(AcknowledgeTrade {})), + ].into_iter())) + } + else { + Ok(Box::new(None.into_iter())) + } })? .unwrap_or_else(|err| { log::warn!("trade error: {:?}", err); @@ -414,8 +448,8 @@ where TradeReady::BothPlayers(room_id, (this_local_client, this_client, this), (other_local_client, other_client, other)) => { let traded_items = item_manager.trade_items(entity_gateway, room_id, - (&this_local_client, &this_client.character, &this.items), - (&other_local_client, &other_client.character, &other.items)).await?; + (&this_local_client, &this_client.character, &this.items, this.meseta), + (&other_local_client, &other_client.character, &other.items, other.meseta)).await?; let clients_in_room = client_location.get_all_clients_by_client(id)?; let traded_item_packets = traded_items @@ -434,6 +468,12 @@ where GameMessage::PlayerNoLongerHasItem(builder::message::player_no_longer_has_item(item.remove_from, item.current_item_id, amount as u32)) ] }, + ItemToTradeDetail::Meseta(amount) => { + [ + GameMessage::CreateItem(builder::message::create_meseta(item.add_to, amount)), + GameMessage::PlayerNoLongerHasItem(builder::message::player_no_longer_has_item(item.remove_from, item.current_item_id, amount as u32)) + ] + }, } }) .flatten() diff --git a/src/ship/trade.rs b/src/ship/trade.rs index a3a3b0a..e6e1ae3 100644 --- a/src/ship/trade.rs +++ b/src/ship/trade.rs @@ -5,13 +5,10 @@ use async_std::sync::{Arc, Mutex}; use crate::common::serverstate::ClientId; use crate::ship::items; -pub const MESETA_ITEM_ID: items::ClientItemId = items::ClientItemId(0xFFFFFF01); - #[derive(Debug, Clone)] pub enum TradeItem { Individual(items::ClientItemId), Stacked(items::ClientItemId, usize), - Meseta(usize), } impl TradeItem { @@ -26,7 +23,6 @@ impl TradeItem { match self { TradeItem::Individual(item_id) => *item_id, TradeItem::Stacked(item_id, _) => *item_id, - TradeItem::Meseta(_) => MESETA_ITEM_ID, } } } @@ -49,6 +45,7 @@ pub struct ClientTradeState { client: ClientId, other_client: ClientId, pub items: Vec, + pub meseta: usize, pub status: TradeStatus, } @@ -113,6 +110,7 @@ impl TradeState { client: *sender, other_client: *receiver, items: Default::default(), + meseta: 0, status: TradeStatus::SentRequest, }; self.trades.insert(*sender, RefCell::new(state)); @@ -121,6 +119,7 @@ impl TradeState { client: *receiver, other_client: *sender, items: Default::default(), + meseta: 0, status: TradeStatus::ReceivedRequest, }; self.trades.insert(*receiver, RefCell::new(state));