Browse Source

initial actual trading logic

pull/80/head
jake 3 years ago
parent
commit
b80f30ef9d
  1. 192
      src/ship/items/manager.rs
  2. 2
      src/ship/packet/builder/message.rs
  3. 90
      src/ship/packet/handler/trade.rs

192
src/ship/items/manager.rs

@ -960,6 +960,125 @@ impl ItemManager {
Ok(weapon)
}
pub async fn trade_items<EG: EntityGateway>(&mut self,
entity_gateway: &mut EG,
p1: (&AreaClient, &CharacterEntity, &Vec<TradeItem>),
p2: (&AreaClient, &CharacterEntity, &Vec<TradeItem>))
-> Result<Vec<ItemToTrade>, 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<ItemToTrade>, Vec<Box<dyn ItemAction<EG>>>)> {
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::<Option<Vec<_>>>()
});
if let [Some(p1_trades), Some(p2_trades)] = trade_items {
let (p1_item_trades, p1_item_actions): (Vec<Option<ItemToTrade>>, Vec<Vec<Box<dyn ItemAction<EG>>>>) = p1_trades.into_iter().unzip();
let (p2_item_trades, p2_item_actions): (Vec<Option<ItemToTrade>>, Vec<Vec<Box<dyn ItemAction<EG>>>>) = 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<EG: EntityGateway> ItemAction<EG> for AddMesetaFloorItemToInventory {
Ok(())
}
}
struct AddIndividualItemToInventory {
character_id: CharacterEntityId,
item_id: ItemEntityId,
}
#[async_trait::async_trait]
impl<EG: EntityGateway> ItemAction<EG> 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<ItemEntityId>,
}
#[async_trait::async_trait]
impl<EG: EntityGateway> ItemAction<EG> 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<EG: EntityGateway> ItemAction<EG> 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<ItemEntityId>,
}
#[async_trait::async_trait]
impl<EG: EntityGateway> ItemAction<EG> 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<EG: EntityGateway> ItemAction<EG> 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<EG: EntityGateway> ItemAction<EG> for RemoveMesetaFromInventory {
async fn commit(&self, _item_manager: &mut ItemManager, entity_gateway: &mut EG) -> Result<(), TransactionCommitError> {
Ok(())
}
}

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

@ -27,6 +27,7 @@ pub fn item_drop(client: u8, target: u8, item_drop: &FloorItem) -> Result<ItemDr
})
}
// TODO: this doesn't need to be a Result, just unwrap try_intos they are guaranteed to succeed
pub fn create_individual_item(area_client: AreaClient, item_id: ClientItemId, item: &item::ItemDetail) -> Result<CreateItem, ShipError> {
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<CreateItem, ShipError> {
let bytes = tool.as_stacked_bytes(amount);
Ok(CreateItem {

90
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<EG>(id: ClientId,
entity_gateway: &mut EG,
client_location: &ClientLocation,
@ -135,47 +134,76 @@ pub async fn trade_confirmed<EG>(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()))

Loading…
Cancel
Save