use std::collections::HashMap; use crate::common::serverstate::ClientId; use crate::ship::items; use async_std::sync::{Mutex, MutexGuard}; use futures::future::Future; #[derive(Debug, Clone)] pub enum TradeItem { Individual(items::ClientItemId), Stacked(items::ClientItemId, usize), } impl TradeItem { pub fn stacked(&self) -> Option<(items::ClientItemId, usize)> { match self { TradeItem::Stacked(item_id, amount) => Some((*item_id, *amount)), _ => None } } pub fn stacked_mut(&mut self) -> Option<(items::ClientItemId, &mut usize)> { match self { TradeItem::Stacked(item_id, ref mut amount) => Some((*item_id, amount)), _ => None } } pub fn item_id(&self) -> items::ClientItemId { match self { TradeItem::Individual(item_id) => *item_id, TradeItem::Stacked(item_id, _) => *item_id, } } pub fn amount(&self) -> usize { match self { TradeItem::Individual(_) => 1, TradeItem::Stacked(_, amount) => *amount, } } } #[derive(Debug, Clone, Eq, PartialEq)] pub enum TradeStatus { SentRequest, ReceivedRequest, Trading, Confirmed, FinalConfirm, ItemsChecked, TradeComplete, } #[derive(Debug, Clone)] pub struct ClientTradeState { client: ClientId, other_client: ClientId, pub items: Vec, pub meseta: usize, pub status: TradeStatus, } impl ClientTradeState { pub fn client(&self) -> ClientId { self.client } pub fn other_client(&self) -> ClientId { self.other_client } } #[derive(thiserror::Error, Debug)] pub enum TradeStateError { #[error("client not in trade {0}")] ClientNotInTrade(ClientId), #[error("mismatched trade {0} {1}")] MismatchedTrade(ClientId, ClientId), } #[derive(Default, Debug)] pub struct TradeState { trades: HashMap>, } impl TradeState { pub fn new_trade(&mut self, sender: &ClientId, receiver: &ClientId) { let state = ClientTradeState { client: *sender, other_client: *receiver, items: Default::default(), meseta: 0, status: TradeStatus::SentRequest, }; self.trades.insert(*sender, Mutex::new(state)); let state = ClientTradeState { client: *receiver, other_client: *sender, items: Default::default(), meseta: 0, status: TradeStatus::ReceivedRequest, }; self.trades.insert(*receiver, Mutex::new(state)); } pub fn in_trade(&self, client: &ClientId) -> bool { self.trades.contains_key(client) } pub async fn with<'a, T, F, Fut> (&'a self, client: &ClientId, func: F) -> Result where F: FnOnce(MutexGuard<'a, ClientTradeState>, MutexGuard<'a, ClientTradeState>) -> Fut + 'a, Fut: Future { let c1 = self.trades.get(client).ok_or(TradeStateError::ClientNotInTrade(*client))?.lock().await; let c2 = self.trades.get(&c1.other_client).ok_or(TradeStateError::ClientNotInTrade(c1.other_client))?.lock().await; // sanity check if c1.client != c2.other_client { return Err(TradeStateError::MismatchedTrade(c1.client, c2.client)); } Ok(func(c1, c2).await) } // TODO: is it possible for this to not return Options? pub fn remove_trade(&mut self, client: &ClientId) -> (Option, Option) { let c1 = self.trades.remove(client).map(|c| c.into_inner()); let c2 = if let Some(ref state) = c1 { self.trades.remove(&state.other_client).map(|c| c.into_inner()) } else { None }; (c1, c2) } }