From b80f30ef9de18fd38e98dfd0aeb23f4e8bf733a6 Mon Sep 17 00:00:00 2001 From: jake Date: Fri, 15 Oct 2021 12:17:37 -0600 Subject: [PATCH] initial actual trading logic --- src/ship/items/manager.rs | 192 +++++++++++++++++++++++++++++ src/ship/packet/builder/message.rs | 2 + src/ship/packet/handler/trade.rs | 90 +++++++++----- 3 files changed, 253 insertions(+), 31 deletions(-) diff --git a/src/ship/items/manager.rs b/src/ship/items/manager.rs index 47154e9..773819c 100644 --- a/src/ship/items/manager.rs +++ b/src/ship/items/manager.rs @@ -960,6 +960,125 @@ impl ItemManager { Ok(weapon) } + pub async fn trade_items(&mut self, + entity_gateway: &mut EG, + p1: (&AreaClient, &CharacterEntity, &Vec), + p2: (&AreaClient, &CharacterEntity, &Vec)) + -> Result, anyhow::Error> { + let it = ItemTransaction::new(&self, (p1, p2)) + .act(|it, (p1, p2)| -> Result<_, anyhow::Error> { + let p1_inventory = it.manager.get_character_inventory(p1.1)?; + let p2_inventory = it.manager.get_character_inventory(p2.1)?; + + //TODO: inv-selftrade+othertrade <= 30 + //if p1_inventory + + let trade_items = [(p1, p2, p1_inventory), (p2, p1, p2_inventory)] + .map(|((src_client, dest_client, src_inventory))| { + src_client.2.iter() + .map(|item| -> Option<(Option, Vec>>)> { + match item { + TradeItem::Individual(item_id) => { + let item = src_inventory.get_item_by_id(*item_id)?.individual()?; + Some(( + Some(ItemToTrade { + add_to: *dest_client.0, + remove_from: *src_client.0, + item_id: *item_id, + item_detail: ItemToTradeDetail::Individual(item.item.clone()) + }), + vec![ + Box::new(AddIndividualItemToInventory { + character_id: dest_client.1.id, + item_id: item.entity_id, + }), + Box::new(RemoveIndividualItemFromInventory { + character_id: src_client.1.id, + item_id: item.entity_id, + }) + ] + )) + }, + TradeItem::Stacked(item_id, amount) => { + let item = src_inventory.get_item_by_id(*item_id)?.stacked()?; + if item.count() < *amount { + None + } + else { + Some(( + Some(ItemToTrade { + add_to: *dest_client.0, + remove_from: *src_client.0, + item_id: *item_id, + item_detail: ItemToTradeDetail::Stacked(item.tool, *amount) + }), + vec![ + Box::new(AddStackedItemToInventory { + character_id: dest_client.1.id, + item_ids: item.entity_ids.iter().cloned().take(*amount).collect(), + }), + Box::new(RemoveStackedItemFromInventory { + character_id: src_client.1.id, + item_ids: item.entity_ids.iter().cloned().take(*amount).collect(), + }), + ] + )) + } + }, + TradeItem::Meseta(amount) => { + Some((None, + vec![ + Box::new(AddMesetaToInventory { + character_id: dest_client.1.id, + amount: *amount, + }), + Box::new(RemoveMesetaFromInventory { + character_id: src_client.1.id, + amount: *amount, + }), + ] + )) + } + } + }) + .collect::>>() + }); + + if let [Some(p1_trades), Some(p2_trades)] = trade_items { + let (p1_item_trades, p1_item_actions): (Vec>, Vec>>>) = p1_trades.into_iter().unzip(); + let (p2_item_trades, p2_item_actions): (Vec>, Vec>>>) = p2_trades.into_iter().unzip(); + let item_trades = p1_item_trades.into_iter().flatten().chain(p2_item_trades.into_iter().flatten()); + let item_actions = p1_item_actions.into_iter().flatten().chain(p2_item_actions.into_iter().flatten()); + + for action in item_actions { + it.action(action); + } + + Ok(item_trades.collect()) + } + else { + Err(ItemManagerError::InvalidTrade.into()) + } + + }); + it.commit(self, entity_gateway) + .await + .map_err(|err| err.into()) + } +} + +pub enum ItemToTradeDetail { + Individual(ItemDetail), + Stacked(Tool, usize), +} + +pub struct ItemToTrade { + pub add_to: AreaClient, + pub remove_from: AreaClient, + pub item_id: ClientItemId, + pub item_detail: ItemToTradeDetail, +} + struct RemoveFromLocalFloor { character_id: CharacterEntityId, @@ -1050,3 +1169,76 @@ impl ItemAction for AddMesetaFloorItemToInventory { Ok(()) } } + + +struct AddIndividualItemToInventory { + character_id: CharacterEntityId, + item_id: ItemEntityId, +} + +#[async_trait::async_trait] +impl ItemAction for AddIndividualItemToInventory { + async fn commit(&self, _item_manager: &mut ItemManager, entity_gateway: &mut EG) -> Result<(), TransactionCommitError> { + Ok(()) + } +} + +struct AddStackedItemToInventory { + character_id: CharacterEntityId, + item_ids: Vec, +} + +#[async_trait::async_trait] +impl ItemAction for AddStackedItemToInventory { + async fn commit(&self, _item_manager: &mut ItemManager, entity_gateway: &mut EG) -> Result<(), TransactionCommitError> { + Ok(()) + } +} + +struct RemoveIndividualItemFromInventory { + character_id: CharacterEntityId, + item_id: ItemEntityId, +} + +#[async_trait::async_trait] +impl ItemAction for RemoveIndividualItemFromInventory { + async fn commit(&self, _item_manager: &mut ItemManager, entity_gateway: &mut EG) -> Result<(), TransactionCommitError> { + Ok(()) + } +} + +struct RemoveStackedItemFromInventory { + character_id: CharacterEntityId, + item_ids: Vec, +} + +#[async_trait::async_trait] +impl ItemAction for RemoveStackedItemFromInventory { + async fn commit(&self, _item_manager: &mut ItemManager, entity_gateway: &mut EG) -> Result<(), TransactionCommitError> { + Ok(()) + } +} + +struct AddMesetaToInventory { + character_id: CharacterEntityId, + amount: usize, +} + +#[async_trait::async_trait] +impl ItemAction for AddMesetaToInventory { + async fn commit(&self, _item_manager: &mut ItemManager, entity_gateway: &mut EG) -> Result<(), TransactionCommitError> { + Ok(()) + } +} + +struct RemoveMesetaFromInventory { + character_id: CharacterEntityId, + amount: usize, +} + +#[async_trait::async_trait] +impl ItemAction for RemoveMesetaFromInventory { + async fn commit(&self, _item_manager: &mut ItemManager, entity_gateway: &mut EG) -> Result<(), TransactionCommitError> { + Ok(()) + } +} diff --git a/src/ship/packet/builder/message.rs b/src/ship/packet/builder/message.rs index 9517736..01c434c 100644 --- a/src/ship/packet/builder/message.rs +++ b/src/ship/packet/builder/message.rs @@ -27,6 +27,7 @@ pub fn item_drop(client: u8, target: u8, item_drop: &FloorItem) -> Result Result { let bytes = item.as_client_bytes(); Ok(CreateItem { @@ -39,6 +40,7 @@ pub fn create_individual_item(area_client: AreaClient, item_id: ClientItemId, it }) } +// TODO: this doesn't need to be a Result, just unwrap try_intos they are guaranteed to succeed pub fn create_stacked_item(area_client: AreaClient, item_id: ClientItemId, tool: &item::tool::Tool, amount: usize) -> Result { let bytes = tool.as_stacked_bytes(amount); Ok(CreateItem { diff --git a/src/ship/packet/handler/trade.rs b/src/ship/packet/handler/trade.rs index 618dfa0..d67d28c 100644 --- a/src/ship/packet/handler/trade.rs +++ b/src/ship/packet/handler/trade.rs @@ -9,7 +9,7 @@ use crate::common::serverstate::ClientId; use crate::ship::ship::{SendShipPacket, ShipError, Clients, Rooms, ItemShops, TradeItem, TradeState, TradeStatus}; use crate::ship::location::{ClientLocation, ClientLocationError}; use crate::ship::drops::ItemDrop; -use crate::ship::items::{ItemManager, ItemManagerError, ClientItemId, TriggerCreateItem, FloorItem, FloorType}; +use crate::ship::items::{ItemManager, ItemManagerError, ClientItemId, TriggerCreateItem, FloorItem, FloorType, ItemToTradeDetail}; use crate::ship::items::inventory::InventoryItem; use crate::entity::gateway::EntityGateway; use crate::entity::item; @@ -125,7 +125,6 @@ where } } -// this function is a shitshow due to not thinking of what would happen if I needed more than 1 client at a time pub async fn trade_confirmed(id: ClientId, entity_gateway: &mut EG, client_location: &ClientLocation, @@ -135,47 +134,76 @@ pub async fn trade_confirmed(id: ClientId, where EG: EntityGateway { - let (this_client_confirmed, other_client_id) = { - let client = clients.get(&id).ok_or(ShipError::ClientNotFound(id))?; - (client.confirmed_trade, client.trade.as_ref().ok_or(TradeError::NotInTradeMenu)?.0) - }; - let other_client_confirmed = { - let client = clients.get(&other_client_id).ok_or(ShipError::ClientNotFound(id))?; - client.confirmed_trade - }; - { let this_client = clients.get_mut(&id).ok_or(ShipError::ClientNotFound(id))?; - this_client.confirmed_trade = true; + this_client.trade.as_mut().ok_or(TradeError::NotInTradeMenu)?.status = TradeStatus::Confirmed; } let both_confirmed = { let this_client = clients.get(&id).ok_or(ShipError::ClientNotFound(id))?; - let other_client = clients.get(&other_client_id).ok_or(ShipError::ClientNotFound(id))?; - this_client.confirmed_trade && other_client.confirmed_trade + let other_client_id = this_client.trade.as_ref().ok_or(TradeError::NotInTradeMenu)?.other_client; + let other_client = clients.get(&other_client_id).ok_or(ShipError::ClientNotFound(other_client_id))?; + + let this_client_trade = this_client.trade.as_ref().ok_or(TradeError::NotInTradeMenu)?; + let other_client_trade = other_client.trade.as_ref().ok_or(TradeError::NotInTradeMenu)?; + this_client_trade.status == TradeStatus::Confirmed && other_client_trade.status == TradeStatus::Confirmed }; - + if both_confirmed { - { - let this_client = clients.get(&id).ok_or(ShipError::ClientNotFound(id))?; - let other_client = clients.get(&other_client_id).ok_or(ShipError::ClientNotFound(id))?; - - let this_character_items = &this_client.trade.as_ref().ok_or(TradeError::NotInTradeMenu)?.1; - item_manager.send_items_to_other_player(entity_gateway, &this_client.character, &other_client.character, this_character_items).await?; - - let other_character_items = &other_client.trade.as_ref().ok_or(TradeError::NotInTradeMenu)?.1; - item_manager.send_items_to_other_player(entity_gateway, &other_client.character, &this_client.character, other_character_items).await?; - } - { + let this_client_trade = { let this_client = clients.get_mut(&id).ok_or(ShipError::ClientNotFound(id))?; + let this_client_trade = this_client.trade.as_ref().ok_or(TradeError::NotInTradeMenu)?.clone(); this_client.trade = None; - } - { - let other_client = clients.get_mut(&other_client_id).ok_or(ShipError::ClientNotFound(id))?; + this_client_trade + }; + let other_client_trade = { + let other_client = clients.get_mut(&this_client_trade.other_client).ok_or(ShipError::ClientNotFound(this_client_trade.other_client))?; + let other_client_trade = other_client.trade.as_ref().ok_or(TradeError::NotInTradeMenu)?.clone(); other_client.trade = None; - } + other_client_trade + }; + let this_client = clients.get(&id).ok_or(ShipError::ClientNotFound(id))?; + let other_client = clients.get(&this_client_trade.other_client).ok_or(ShipError::ClientNotFound(this_client_trade.other_client))?; - Ok(Box::new(None.into_iter())) + let this_local_client = client_location.get_local_client(id)?; + let other_local_client = client_location.get_local_client(this_client_trade.other_client)?; + + let traded_items = item_manager.trade_items( + entity_gateway, + (&this_local_client, &this_client.character, &this_client_trade.items), + (&other_local_client, &other_client.character, &other_client_trade.items) + ).await?; + + let clients_in_room = client_location.get_all_clients_by_client(id)?; + let traded_item_packets = traded_items + .into_iter() + .map(|item| { + match item.item_detail { + ItemToTradeDetail::Individual(item_detail) => { + [ + GameMessage::CreateItem(builder::message::create_individual_item(item.add_to, item.item_id, &item_detail).unwrap()), + GameMessage::PlayerNoLongerHasItem(builder::message::player_no_longer_has_item(item.remove_from, item.item_id, 1)) // TODO: amount = ? + ] + }, + ItemToTradeDetail::Stacked(tool, amount) => { + [ + GameMessage::CreateItem(builder::message::create_stacked_item(item.add_to, item.item_id, &tool, amount).unwrap()), + GameMessage::PlayerNoLongerHasItem(builder::message::player_no_longer_has_item(item.remove_from, item.item_id, amount as u32)) + ] + }, + } + }) + .flatten() + .map(move |packet| { + clients_in_room + .clone() + .into_iter() + .map(move |client| { + (client.client, SendShipPacket::Message(Message::new(packet.clone()))) + }) + }) + .flatten(); + Ok(Box::new(traded_item_packets)) } else { Ok(Box::new(None.into_iter()))