142 lines
3.8 KiB
Rust
142 lines
3.8 KiB
Rust
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<TradeItem>,
|
|
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<ClientId, Mutex<ClientTradeState>>,
|
|
}
|
|
|
|
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<T, TradeStateError>
|
|
where
|
|
F: FnOnce(MutexGuard<'a, ClientTradeState>, MutexGuard<'a, ClientTradeState>) -> Fut + 'a,
|
|
Fut: Future<Output=T>
|
|
{
|
|
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<ClientTradeState>, Option<ClientTradeState>) {
|
|
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)
|
|
}
|
|
}
|