|
|
@ -13,6 +13,7 @@ use crate::ship::packet::builder; |
|
|
|
use crate::ship::items::tasks::trade_items;
|
|
|
|
use crate::ship::location::{AreaClient, RoomId};
|
|
|
|
use crate::entity::item::Meseta;
|
|
|
|
use crate::ship::trade::ClientTradeState;
|
|
|
|
|
|
|
|
pub const MESETA_ITEM_ID: ClientItemId = ClientItemId(0xFFFFFF01);
|
|
|
|
pub const OTHER_MESETA_ITEM_ID: ClientItemId = ClientItemId(0xFFFFFFFF);
|
|
|
@ -48,15 +49,47 @@ pub enum TradeError { |
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
pub async fn do_trade_action<F>(id: ClientId,
|
|
|
|
pkt: TradeRequest,
|
|
|
|
client_location: &ClientLocation,
|
|
|
|
target: u32,
|
|
|
|
this: &mut ClientTradeState,
|
|
|
|
other: &mut ClientTradeState,
|
|
|
|
action: F) -> Result<Box<dyn Iterator<Item = (ClientId, SendShipPacket)> + Send>, ShipError>
|
|
|
|
where
|
|
|
|
F: Fn(&mut ClientTradeState, &mut ClientTradeState) -> Result<(), ShipError>,
|
|
|
|
{
|
|
|
|
Ok(match action(this, other) {
|
|
|
|
Ok(_) => {
|
|
|
|
Box::new(client_location.get_all_clients_by_client(id).await?.into_iter()
|
|
|
|
.filter(move |client| client.local_client.id() == target as u8)
|
|
|
|
.map(move |client| {
|
|
|
|
(client.client, SendShipPacket::DirectMessage(DirectMessage::new(target, GameMessage::TradeRequest(pkt.clone()))))
|
|
|
|
}))
|
|
|
|
},
|
|
|
|
Err(_) => {
|
|
|
|
// TODO: some sort of error logging?
|
|
|
|
Box::new(client_location.get_all_clients_by_client(id).await?.into_iter()
|
|
|
|
.filter(move |client| client.local_client.id() == target as u8)
|
|
|
|
.map(move |client| {
|
|
|
|
(client.client, SendShipPacket::CancelTrade(CancelTrade {}))
|
|
|
|
})
|
|
|
|
.chain(std::iter::once((id, SendShipPacket::CancelTrade(CancelTrade {})))))
|
|
|
|
}
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// TODO: remove target
|
|
|
|
pub async fn trade_request(id: ClientId,
|
|
|
|
trade_request: &TradeRequest,
|
|
|
|
target: u32,
|
|
|
|
client_location: &ClientLocation,
|
|
|
|
clients: &mut Clients,
|
|
|
|
item_state: &mut ItemState,
|
|
|
|
trades: &mut TradeState)
|
|
|
|
-> Result<Box<dyn Iterator<Item = (ClientId, SendShipPacket)> + Send>, ShipError>
|
|
|
|
trade_request: &TradeRequest,
|
|
|
|
target: u32,
|
|
|
|
client_location: &ClientLocation,
|
|
|
|
clients: &mut Clients,
|
|
|
|
item_state: &mut ItemState,
|
|
|
|
trades: &mut TradeState)
|
|
|
|
-> Result<Box<dyn Iterator<Item = (ClientId, SendShipPacket)> + Send>, ShipError>
|
|
|
|
{
|
|
|
|
let trade_request = trade_request.clone(); // TODO: make this function take ownership of packet
|
|
|
|
match trade_request.trade {
|
|
|
@ -66,7 +99,7 @@ pub async fn trade_request(id: ClientId, |
|
|
|
if trades.in_trade(&id) {
|
|
|
|
return Err(TradeError::ClientAlreadyInTrade.into())
|
|
|
|
}
|
|
|
|
let trade_partner = client_location.get_client_neighbors(id)?
|
|
|
|
let trade_partner = client_location.get_client_neighbors(id).await?
|
|
|
|
.into_iter()
|
|
|
|
.find(|ac| {
|
|
|
|
ac.local_client.id() == target as u8 //trade_request.client
|
|
|
@ -76,204 +109,155 @@ pub async fn trade_request(id: ClientId, |
|
|
|
return Err(TradeError::OtherAlreadyInTrade.into())
|
|
|
|
}
|
|
|
|
trades.new_trade(&id, &trade_partner.client);
|
|
|
|
Ok(Box::new(client_location.get_all_clients_by_client(id)?.into_iter()
|
|
|
|
Ok(Box::new(client_location.get_all_clients_by_client(id).await?.into_iter()
|
|
|
|
.filter(move |client| client.local_client.id() == target as u8)
|
|
|
|
.map(move |client| {
|
|
|
|
(client.client, SendShipPacket::DirectMessage(DirectMessage::new(target, GameMessage::TradeRequest(trade_request.clone()))))
|
|
|
|
})))
|
|
|
|
},
|
|
|
|
TradeRequestInitializeCommand::Respond => {
|
|
|
|
Ok(trades
|
|
|
|
.with(&id, |this, other| -> Option<Box<dyn Iterator<Item = (ClientId, SendShipPacket)> + Send>> {
|
|
|
|
if this.status == TradeStatus::ReceivedRequest && other.status == TradeStatus::SentRequest {
|
|
|
|
this.status = TradeStatus::Trading;
|
|
|
|
other.status = TradeStatus::Trading;
|
|
|
|
|
|
|
|
let trade_request = trade_request.clone();
|
|
|
|
Some(Box::new(client_location.get_all_clients_by_client(id).ok()?.into_iter()
|
|
|
|
.filter(move |client| client.local_client.id() == target as u8)
|
|
|
|
.map(move |client| {
|
|
|
|
(client.client, SendShipPacket::DirectMessage(DirectMessage::new(target, GameMessage::TradeRequest(trade_request.clone()))))
|
|
|
|
})))
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
None
|
|
|
|
}
|
|
|
|
})?
|
|
|
|
.unwrap_or_else(|| -> Box<dyn Iterator<Item = (ClientId, SendShipPacket)> + Send> {
|
|
|
|
trades.remove_trade(&id);
|
|
|
|
Box::new(client_location.get_all_clients_by_client(id).unwrap().into_iter()
|
|
|
|
.filter(move |client| client.local_client.id() == target as u8)
|
|
|
|
.map(move |client| {
|
|
|
|
(client.client, SendShipPacket::CancelTrade(CancelTrade {}))
|
|
|
|
})
|
|
|
|
.chain(std::iter::once((id, SendShipPacket::CancelTrade(CancelTrade {})))))
|
|
|
|
}))
|
|
|
|
trades
|
|
|
|
.with(&id, |mut this, mut other| {
|
|
|
|
let trade_request = trade_request.clone();
|
|
|
|
async move {
|
|
|
|
do_trade_action(id, trade_request, client_location, target, &mut this, &mut other, |this, other| {
|
|
|
|
if this.status == TradeStatus::ReceivedRequest && other.status == TradeStatus::SentRequest {
|
|
|
|
this.status = TradeStatus::Trading;
|
|
|
|
other.status = TradeStatus::Trading;
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
Err(TradeError::MismatchedStatus.into())
|
|
|
|
}
|
|
|
|
}).await
|
|
|
|
}}).await?
|
|
|
|
}
|
|
|
|
}
|
|
|
|
},
|
|
|
|
TradeRequestCommand::AddItem(item_id, amount) => {
|
|
|
|
Ok(trades
|
|
|
|
.with(&id, |this, other| -> Result<Box<dyn Iterator<Item = (ClientId, SendShipPacket)> + Send>, anyhow::Error> {
|
|
|
|
if this.status == TradeStatus::Trading && other.status == TradeStatus::Trading {
|
|
|
|
let client = clients.get(&this.client()).ok_or_else(|| ShipError::ClientNotFound(this.client()))?;
|
|
|
|
let inventory = item_state.get_character_inventory(&client.character)?;
|
|
|
|
if ClientItemId(item_id) == MESETA_ITEM_ID {
|
|
|
|
this.meseta += amount as usize;
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
let item = inventory.get_by_client_id(&ClientItemId(item_id)).ok_or(ItemStateError::InvalidItemId(ClientItemId(item_id)))?;
|
|
|
|
|
|
|
|
match &item.item {
|
|
|
|
InventoryItemDetail::Individual(_) => {
|
|
|
|
this.items.push(TradeItem::Individual(ClientItemId(item_id)));
|
|
|
|
},
|
|
|
|
InventoryItemDetail::Stacked(stacked_item) => {
|
|
|
|
if stacked_item.count() < amount as usize {
|
|
|
|
return Err(TradeError::InvalidStackAmount(ClientItemId(item_id), amount as usize).into());
|
|
|
|
trades
|
|
|
|
.with(&id, |mut this, mut other| {
|
|
|
|
let trade_request = trade_request.clone();
|
|
|
|
async move {
|
|
|
|
do_trade_action(id, trade_request, client_location, target, &mut this, &mut other, |this, other| {
|
|
|
|
if this.status == TradeStatus::Trading && other.status == TradeStatus::Trading {
|
|
|
|
let client = clients.get(&this.client()).ok_or_else(|| ShipError::ClientNotFound(this.client()))?;
|
|
|
|
let inventory = item_state.get_character_inventory(&client.character)?;
|
|
|
|
if ClientItemId(item_id) == MESETA_ITEM_ID {
|
|
|
|
this.meseta += amount as usize;
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
let item = inventory.get_by_client_id(&ClientItemId(item_id)).ok_or(ItemStateError::InvalidItemId(ClientItemId(item_id)))?;
|
|
|
|
|
|
|
|
match &item.item {
|
|
|
|
InventoryItemDetail::Individual(_) => {
|
|
|
|
this.items.push(TradeItem::Individual(ClientItemId(item_id)));
|
|
|
|
},
|
|
|
|
InventoryItemDetail::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));
|
|
|
|
},
|
|
|
|
}
|
|
|
|
this.items.push(TradeItem::Stacked(ClientItemId(item_id), amount as usize));
|
|
|
|
},
|
|
|
|
}
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
let trade_request = trade_request.clone();
|
|
|
|
Ok(Box::new(client_location.get_all_clients_by_client(id)?.into_iter()
|
|
|
|
.filter(move |client| client.local_client.id() == target as u8)
|
|
|
|
.map(move |client| {
|
|
|
|
(client.client, SendShipPacket::DirectMessage(DirectMessage::new(target, GameMessage::TradeRequest(trade_request.clone()))))
|
|
|
|
})))
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
Err(TradeError::MismatchedStatus.into())
|
|
|
|
}
|
|
|
|
})?
|
|
|
|
.unwrap_or_else(|_err| {
|
|
|
|
trades.remove_trade(&id);
|
|
|
|
Box::new(client_location.get_all_clients_by_client(id).unwrap().into_iter()
|
|
|
|
.filter(move |client| client.local_client.id() == target as u8)
|
|
|
|
.map(move |client| {
|
|
|
|
(client.client, SendShipPacket::CancelTrade(CancelTrade {}))
|
|
|
|
})
|
|
|
|
.chain(std::iter::once((id, SendShipPacket::CancelTrade(CancelTrade {})))))
|
|
|
|
}))
|
|
|
|
else {
|
|
|
|
Err(TradeError::MismatchedStatus.into())
|
|
|
|
}
|
|
|
|
}).await
|
|
|
|
}}).await?
|
|
|
|
},
|
|
|
|
TradeRequestCommand::RemoveItem(item_id, amount) => {
|
|
|
|
Ok(trades
|
|
|
|
.with(&id, |this, other| -> Option<Box<dyn Iterator<Item = (ClientId, SendShipPacket)> + Send>> {
|
|
|
|
if this.status == TradeStatus::Trading && other.status == TradeStatus::Trading {
|
|
|
|
let client = clients.get(&this.client())?; //.ok_or(ShipError::ClientNotFound(id)).ok()?;
|
|
|
|
let inventory = item_state.get_character_inventory(&client.character).ok()?;
|
|
|
|
if ClientItemId(item_id) == MESETA_ITEM_ID {
|
|
|
|
this.meseta -= amount as usize;
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
let item = inventory.get_by_client_id(&ClientItemId(item_id))?;
|
|
|
|
|
|
|
|
match &item.item {
|
|
|
|
InventoryItemDetail::Individual(_) => {
|
|
|
|
this.items.retain(|item| {
|
|
|
|
item.item_id() != ClientItemId(item_id)
|
|
|
|
})
|
|
|
|
},
|
|
|
|
InventoryItemDetail::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_mut()?.1 -= amount as usize;
|
|
|
|
trades
|
|
|
|
.with(&id, |mut this, mut other| {
|
|
|
|
let trade_request = trade_request.clone();
|
|
|
|
async move {
|
|
|
|
do_trade_action(id, trade_request, client_location, target, &mut this, &mut other, |this, other| {
|
|
|
|
if this.status == TradeStatus::Trading && other.status == TradeStatus::Trading {
|
|
|
|
let client = clients.get(&this.client()).ok_or_else(|| ShipError::ClientNotFound(this.client()))?;
|
|
|
|
let inventory = item_state.get_character_inventory(&client.character)?;
|
|
|
|
if ClientItemId(item_id) == MESETA_ITEM_ID {
|
|
|
|
this.meseta -= amount as usize;
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
let item = inventory.get_by_client_id(&ClientItemId(item_id)).ok_or(ItemStateError::InvalidItemId(ClientItemId(item_id)))?;
|
|
|
|
|
|
|
|
match &item.item {
|
|
|
|
InventoryItemDetail::Individual(_) => {
|
|
|
|
this.items.retain(|item| {
|
|
|
|
item.item_id() != ClientItemId(item_id)
|
|
|
|
})
|
|
|
|
},
|
|
|
|
std::cmp::Ordering::Equal => {
|
|
|
|
this.items.remove(trade_item_index);
|
|
|
|
InventoryItemDetail::Stacked(_stacked_item) => {
|
|
|
|
let trade_item_index = this.items.iter()
|
|
|
|
.position(|item| {
|
|
|
|
item.item_id() == ClientItemId(item_id)
|
|
|
|
})
|
|
|
|
.ok_or(TradeError::InvalidItemId(ClientItemId(item_id)))?;
|
|
|
|
|
|
|
|
match this.items[trade_item_index].stacked().ok_or(ItemStateError::InvalidItemId(ClientItemId(item_id)))?.1.cmp(&(amount as usize)) {
|
|
|
|
std::cmp::Ordering::Greater => {
|
|
|
|
*this.items[trade_item_index].stacked_mut().ok_or(ItemStateError::InvalidItemId(ClientItemId(item_id)))?.1 -= amount as usize;
|
|
|
|
},
|
|
|
|
std::cmp::Ordering::Equal => {
|
|
|
|
this.items.remove(trade_item_index);
|
|
|
|
},
|
|
|
|
std::cmp::Ordering::Less => {
|
|
|
|
return Err(TradeError::SketchyTrade.into())
|
|
|
|
}
|
|
|
|
}
|
|
|
|
},
|
|
|
|
std::cmp::Ordering::Less => {
|
|
|
|
return None
|
|
|
|
}
|
|
|
|
}
|
|
|
|
},
|
|
|
|
}
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
}
|
|
|
|
let trade_request = trade_request.clone();
|
|
|
|
Some(Box::new(client_location.get_all_clients_by_client(id).unwrap().into_iter()
|
|
|
|
.filter(move |client| client.local_client.id() == target as u8)
|
|
|
|
.map(move |client| {
|
|
|
|
(client.client, SendShipPacket::DirectMessage(DirectMessage::new(target, GameMessage::TradeRequest(trade_request.clone()))))
|
|
|
|
})))
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
None
|
|
|
|
else {
|
|
|
|
Err(TradeError::MismatchedStatus.into())
|
|
|
|
}
|
|
|
|
|
|
|
|
}).await
|
|
|
|
}
|
|
|
|
})?
|
|
|
|
.unwrap_or_else(|| {
|
|
|
|
trades.remove_trade(&id);
|
|
|
|
Box::new(client_location.get_all_clients_by_client(id).unwrap().into_iter()
|
|
|
|
.filter(move |client| client.local_client.id() == target as u8)
|
|
|
|
.map(move |client| {
|
|
|
|
(client.client, SendShipPacket::CancelTrade(CancelTrade {}))
|
|
|
|
})
|
|
|
|
.chain(std::iter::once((id, SendShipPacket::CancelTrade(CancelTrade {})))))
|
|
|
|
}))
|
|
|
|
}).await?
|
|
|
|
},
|
|
|
|
TradeRequestCommand::Confirm => {
|
|
|
|
Ok(trades
|
|
|
|
.with(&id, |this, other| -> Option<Box<dyn Iterator<Item = (ClientId, SendShipPacket)> + Send>> {
|
|
|
|
if status_is(&this.status, &[TradeStatus::Trading]) && status_is(&other.status, &[TradeStatus::Trading, TradeStatus::Confirmed]) {
|
|
|
|
this.status = TradeStatus::Confirmed;
|
|
|
|
|
|
|
|
let trade_request = trade_request.clone();
|
|
|
|
Some(Box::new(client_location.get_all_clients_by_client(id).unwrap().into_iter()
|
|
|
|
.filter(move |client| client.local_client.id() == target as u8)
|
|
|
|
.map(move |client| {
|
|
|
|
(client.client, SendShipPacket::DirectMessage(DirectMessage::new(target, GameMessage::TradeRequest(trade_request.clone()))))
|
|
|
|
})))
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
None
|
|
|
|
trades
|
|
|
|
.with(&id, |mut this, mut other| {
|
|
|
|
let trade_request = trade_request.clone();
|
|
|
|
async move {
|
|
|
|
do_trade_action(id, trade_request, client_location, target, &mut this, &mut other, |this, other| {
|
|
|
|
if status_is(&this.status, &[TradeStatus::Trading]) && status_is(&other.status, &[TradeStatus::Trading, TradeStatus::Confirmed]) {
|
|
|
|
this.status = TradeStatus::Confirmed;
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
Err(TradeError::MismatchedStatus.into())
|
|
|
|
}
|
|
|
|
}).await
|
|
|
|
}
|
|
|
|
})?
|
|
|
|
.unwrap_or_else(|| {
|
|
|
|
trades.remove_trade(&id);
|
|
|
|
Box::new(client_location.get_all_clients_by_client(id).unwrap().into_iter()
|
|
|
|
.filter(move |client| client.local_client.id() == target as u8)
|
|
|
|
.map(move |client| {
|
|
|
|
(client.client, SendShipPacket::CancelTrade(CancelTrade {}))
|
|
|
|
})
|
|
|
|
.chain(std::iter::once((id, SendShipPacket::CancelTrade(CancelTrade {})))))
|
|
|
|
}))
|
|
|
|
}).await?
|
|
|
|
},
|
|
|
|
TradeRequestCommand::FinalConfirm => {
|
|
|
|
Ok(trades
|
|
|
|
.with(&id, |this, other| -> Option<Box<dyn Iterator<Item = (ClientId, SendShipPacket)> + Send>> {
|
|
|
|
if this.status == TradeStatus::Confirmed && (other.status == TradeStatus::Confirmed || other.status == TradeStatus::FinalConfirm) {
|
|
|
|
this.status = TradeStatus::FinalConfirm;
|
|
|
|
|
|
|
|
let trade_request = trade_request.clone();
|
|
|
|
Some(Box::new(client_location.get_all_clients_by_client(id).unwrap().into_iter()
|
|
|
|
.filter(move |client| client.local_client.id() == target as u8)
|
|
|
|
.map(move |client| {
|
|
|
|
(client.client, SendShipPacket::DirectMessage(DirectMessage::new(target, GameMessage::TradeRequest(trade_request.clone()))))
|
|
|
|
})))
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
None
|
|
|
|
trades
|
|
|
|
.with(&id, |mut this, mut other| {
|
|
|
|
let trade_request = trade_request.clone();
|
|
|
|
async move {
|
|
|
|
do_trade_action(id, trade_request, client_location, target, &mut this, &mut other, |this, other| {
|
|
|
|
if this.status == TradeStatus::Confirmed && (other.status == TradeStatus::Confirmed || other.status == TradeStatus::FinalConfirm) {
|
|
|
|
this.status = TradeStatus::FinalConfirm;
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
Err(TradeError::MismatchedStatus.into())
|
|
|
|
}
|
|
|
|
}).await
|
|
|
|
}
|
|
|
|
})?
|
|
|
|
.unwrap_or_else(|| {
|
|
|
|
trades.remove_trade(&id);
|
|
|
|
Box::new(client_location.get_all_clients_by_client(id).unwrap().into_iter()
|
|
|
|
.filter(move |client| client.local_client.id() == target as u8)
|
|
|
|
.map(move |client| {
|
|
|
|
(client.client, SendShipPacket::CancelTrade(CancelTrade {}))
|
|
|
|
})
|
|
|
|
.chain(std::iter::once((id, SendShipPacket::CancelTrade(CancelTrade {})))))
|
|
|
|
}))
|
|
|
|
}).await?
|
|
|
|
},
|
|
|
|
TradeRequestCommand::Cancel => {
|
|
|
|
trades.remove_trade(&id);
|
|
|
|
Ok(Box::new(client_location.get_all_clients_by_client(id).unwrap().into_iter()
|
|
|
|
Ok(Box::new(client_location.get_all_clients_by_client(id).await?.into_iter()
|
|
|
|
.filter(move |client| client.local_client.id() == target as u8)
|
|
|
|
.map(move |client| {
|
|
|
|
(client.client, SendShipPacket::CancelTrade(CancelTrade {}))
|
|
|
@ -298,10 +282,10 @@ pub async fn inner_items_to_trade(id: ClientId, |
|
|
|
clients: &mut Clients,
|
|
|
|
item_state: &mut ItemState,
|
|
|
|
trades: &mut TradeState)
|
|
|
|
-> Result<Box<dyn Iterator<Item = (ClientId, SendShipPacket)> + Send>, anyhow::Error>
|
|
|
|
-> Result<Box<dyn Iterator<Item = (ClientId, SendShipPacket)> + Send>, ShipError>
|
|
|
|
{
|
|
|
|
Ok(trades
|
|
|
|
.with(&id, |this, other| -> Result<Box<dyn Iterator<Item = (ClientId, SendShipPacket)> + Send>, anyhow::Error> {
|
|
|
|
let pkts: Result<Box<dyn Iterator<Item=_> + Send>, ShipError> = trades
|
|
|
|
.with(&id, |mut this, other| async move {
|
|
|
|
if status_is_not(&this.status, &[TradeStatus::FinalConfirm]) || status_is_not(&other.status, &[TradeStatus::FinalConfirm, TradeStatus::ItemsChecked]) {
|
|
|
|
return Err(TradeError::MismatchedStatus.into())
|
|
|
|
}
|
|
|
@ -379,29 +363,32 @@ pub async fn inner_items_to_trade(id: ClientId, |
|
|
|
}
|
|
|
|
}
|
|
|
|
})
|
|
|
|
.collect::<Result<Vec<_>, anyhow::Error>>()?;
|
|
|
|
.collect::<Result<Vec<_>, ShipError>>()?;
|
|
|
|
|
|
|
|
this.status = TradeStatus::ItemsChecked;
|
|
|
|
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()))
|
|
|
|
].into_iter()) as Box<dyn Iterator<Item=_> + Send>)
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
Ok(Box::new(None.into_iter()))
|
|
|
|
Ok(Box::new(Vec::new().into_iter()) as Box<dyn Iterator<Item=_> + Send>)
|
|
|
|
}
|
|
|
|
})?
|
|
|
|
.unwrap_or_else(|err| {
|
|
|
|
log::warn!("trade error: {:?}", err);
|
|
|
|
let (_this, other) = trades.remove_trade(&id);
|
|
|
|
Box::new(client_location.get_all_clients_by_client(id).unwrap().into_iter()
|
|
|
|
.filter(move |client| other.as_ref().map(|other| client.client == other.client() ).unwrap_or_else(|| false))
|
|
|
|
.map(move |client| {
|
|
|
|
(client.client, SendShipPacket::CancelTrade(CancelTrade {}))
|
|
|
|
})
|
|
|
|
.chain(std::iter::once((id, SendShipPacket::CancelTrade(CancelTrade {})))))
|
|
|
|
}))
|
|
|
|
}).await?;
|
|
|
|
match pkts {
|
|
|
|
Ok(pkts) => Ok(pkts),
|
|
|
|
Err(err) => {
|
|
|
|
log::warn!("trade error: {:?}", err);
|
|
|
|
let (_this, other) = trades.remove_trade(&id);
|
|
|
|
Ok(Box::new(client_location.get_all_clients_by_client(id).await?.into_iter()
|
|
|
|
.filter(move |client| other.as_ref().map(|other| client.client == other.client() ).unwrap_or_else(|| false))
|
|
|
|
.map(move |client| {
|
|
|
|
(client.client, SendShipPacket::CancelTrade(CancelTrade {}))
|
|
|
|
})
|
|
|
|
.chain(std::iter::once((id, SendShipPacket::CancelTrade(CancelTrade {}))))))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
pub async fn items_to_trade(id: ClientId,
|
|
|
@ -418,7 +405,7 @@ pub async fn items_to_trade(id: ClientId, |
|
|
|
Err(err) => {
|
|
|
|
log::warn!("atrade error: {:?}", err);
|
|
|
|
let (_this, other) = trades.remove_trade(&id);
|
|
|
|
Ok(Box::new(client_location.get_all_clients_by_client(id)?.into_iter()
|
|
|
|
Ok(Box::new(client_location.get_all_clients_by_client(id).await?.into_iter()
|
|
|
|
.filter(move |client| other.as_ref().map(|other| client.client == other.client()).unwrap_or_else(|| false))
|
|
|
|
.map(move |client| {
|
|
|
|
(client.client, SendShipPacket::CancelTrade(CancelTrade {}))
|
|
|
@ -428,13 +415,13 @@ pub async fn items_to_trade(id: ClientId, |
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
pub async fn trade_confirmed<EG>(id: ClientId,
|
|
|
|
mut entity_gateway: EG,
|
|
|
|
client_location: &ClientLocation,
|
|
|
|
clients: &mut Clients,
|
|
|
|
item_state: &mut ItemState,
|
|
|
|
trades: &mut TradeState)
|
|
|
|
-> Result<Box<dyn Iterator<Item = (ClientId, SendShipPacket)> + Send>, anyhow::Error>
|
|
|
|
pub async fn trade_confirmed_inner<EG>(id: ClientId,
|
|
|
|
mut entity_gateway: EG,
|
|
|
|
client_location: &ClientLocation,
|
|
|
|
clients: &mut Clients,
|
|
|
|
item_state: &mut ItemState,
|
|
|
|
trades: &mut TradeState)
|
|
|
|
-> Result<Box<dyn Iterator<Item = (ClientId, SendShipPacket)> + Send>, ShipError>
|
|
|
|
where
|
|
|
|
EG: EntityGateway
|
|
|
|
{
|
|
|
@ -445,123 +432,136 @@ where |
|
|
|
(AreaClient, &'a crate::ship::ship::ClientState, crate::ship::trade::ClientTradeState)),
|
|
|
|
}
|
|
|
|
|
|
|
|
let trade_instructions = trades
|
|
|
|
.with(&id, |this, other| -> Result<_, anyhow::Error> {
|
|
|
|
if status_is_not(&this.status, &[TradeStatus::ItemsChecked]) || status_is_not(&other.status, &[TradeStatus::ItemsChecked, TradeStatus::TradeComplete]) {
|
|
|
|
return Err(TradeError::MismatchedStatus.into())
|
|
|
|
}
|
|
|
|
this.status = TradeStatus::TradeComplete;
|
|
|
|
|
|
|
|
if this.status == TradeStatus::TradeComplete && other.status == TradeStatus::TradeComplete {
|
|
|
|
let this_client = clients.get(&this.client()).ok_or_else(|| ShipError::ClientNotFound(this.client()))?;
|
|
|
|
let other_client = clients.get(&other.client()).ok_or_else(|| ShipError::ClientNotFound(other.client()))?;
|
|
|
|
let this_local_client = client_location.get_local_client(this.client())?;
|
|
|
|
let other_local_client = client_location.get_local_client(other.client())?;
|
|
|
|
let room_id = client_location.get_room(id).map_err(|err| -> ClientLocationError { err.into() })?;
|
|
|
|
|
|
|
|
Ok(TradeReady::BothPlayers(room_id,
|
|
|
|
(this_local_client, this_client, this.clone()),
|
|
|
|
(other_local_client, other_client, other.clone())))
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
Ok(TradeReady::OnePlayer)
|
|
|
|
let trade = trades
|
|
|
|
.with(&id, |mut this, other| {
|
|
|
|
async move {
|
|
|
|
if status_is_not(&this.status, &[TradeStatus::ItemsChecked]) || status_is_not(&other.status, &[TradeStatus::ItemsChecked, TradeStatus::TradeComplete]) {
|
|
|
|
return Err(ShipError::TradeError(TradeError::MismatchedStatus))
|
|
|
|
}
|
|
|
|
this.status = TradeStatus::TradeComplete;
|
|
|
|
|
|
|
|
if this.status == TradeStatus::TradeComplete && other.status == TradeStatus::TradeComplete {
|
|
|
|
let this_client = clients.get(&this.client()).ok_or_else(|| ShipError::ClientNotFound(this.client()))?;
|
|
|
|
let other_client = clients.get(&other.client()).ok_or_else(|| ShipError::ClientNotFound(other.client()))?;
|
|
|
|
let this_local_client = client_location.get_local_client(this.client()).await?;
|
|
|
|
let other_local_client = client_location.get_local_client(other.client()).await?;
|
|
|
|
let room_id = client_location.get_room(id).await.map_err(|err| -> ClientLocationError { err.into() })?;
|
|
|
|
|
|
|
|
Ok(TradeReady::BothPlayers(room_id,
|
|
|
|
(this_local_client, this_client, this.clone()),
|
|
|
|
(other_local_client, other_client, other.clone())))
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
Ok(TradeReady::OnePlayer)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
|
|
|
// TODO: this match needs to handle errors better
|
|
|
|
match trade_instructions {
|
|
|
|
Ok(Ok(trade)) => {
|
|
|
|
match trade {
|
|
|
|
TradeReady::OnePlayer => {
|
|
|
|
Ok(Box::new(None.into_iter()) as Box<dyn Iterator<Item = (ClientId, SendShipPacket)> + Send>)
|
|
|
|
},
|
|
|
|
TradeReady::BothPlayers(_room_id, (this_local_client, this_client, this), (other_local_client, other_client, other)) => {
|
|
|
|
let remove_item_packets = this.items
|
|
|
|
}).await??;
|
|
|
|
|
|
|
|
match trade {
|
|
|
|
TradeReady::OnePlayer => {
|
|
|
|
Ok(Box::new(None.into_iter()) as Box<dyn Iterator<Item = (ClientId, SendShipPacket)> + Send>)
|
|
|
|
},
|
|
|
|
TradeReady::BothPlayers(_room_id, (this_local_client, this_client, this), (other_local_client, other_client, other)) => {
|
|
|
|
let remove_item_packets = this.items
|
|
|
|
.clone()
|
|
|
|
.into_iter()
|
|
|
|
.map(move |item| {
|
|
|
|
(this_local_client, item)
|
|
|
|
})
|
|
|
|
.chain(other.items
|
|
|
|
.clone()
|
|
|
|
.into_iter()
|
|
|
|
.map(move |item| {
|
|
|
|
(other_local_client, item)
|
|
|
|
}))
|
|
|
|
.map(|(client, item)| {
|
|
|
|
GameMessage::PlayerNoLongerHasItem(builder::message::player_no_longer_has_item(client, item.item_id(), item.amount() as u32))
|
|
|
|
});
|
|
|
|
|
|
|
|
let (this_new_items, other_new_items) = trade_items(item_state,
|
|
|
|
&mut entity_gateway,
|
|
|
|
(&this_local_client, &this_client.character, &this.items, Meseta(this.meseta as u32)),
|
|
|
|
(&other_local_client, &other_client.character, &other.items, Meseta(other.meseta as u32))).await?;
|
|
|
|
|
|
|
|
let create_item_packets = this_new_items
|
|
|
|
.into_iter()
|
|
|
|
.map(move |item| {
|
|
|
|
(this_local_client, item)
|
|
|
|
})
|
|
|
|
.chain(other_new_items
|
|
|
|
.into_iter()
|
|
|
|
.map(move |item| {
|
|
|
|
(other_local_client, item)
|
|
|
|
}))
|
|
|
|
.map(|(client, item)| {
|
|
|
|
match item.item {
|
|
|
|
InventoryItemDetail::Individual(individual_item) => {
|
|
|
|
GameMessage::CreateItem(builder::message::create_individual_item(client, item.item_id, &individual_item).unwrap())
|
|
|
|
},
|
|
|
|
InventoryItemDetail::Stacked(stacked_item) => {
|
|
|
|
GameMessage::CreateItem(builder::message::create_stacked_item(client, item.item_id, &stacked_item.tool, stacked_item.count()).unwrap())
|
|
|
|
}
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
|
|
|
let meseta_packets = vec![(this_local_client, other_local_client, this.meseta), (other_local_client, this_local_client, other.meseta)]
|
|
|
|
.into_iter()
|
|
|
|
.filter(|(_, _, meseta)| *meseta != 0)
|
|
|
|
.flat_map(|(this, other, meseta)| {
|
|
|
|
[
|
|
|
|
GameMessage::PlayerNoLongerHasItem(builder::message::player_no_longer_has_item(this, MESETA_ITEM_ID, meseta as u32)),
|
|
|
|
GameMessage::CreateItem(builder::message::create_meseta(other, meseta)),
|
|
|
|
]
|
|
|
|
});
|
|
|
|
|
|
|
|
let clients_in_room = client_location.get_all_clients_by_client(id).await?;
|
|
|
|
let traded_item_packets = remove_item_packets
|
|
|
|
.chain(create_item_packets)
|
|
|
|
.chain(meseta_packets)
|
|
|
|
.flat_map(move |packet| {
|
|
|
|
clients_in_room
|
|
|
|
.clone()
|
|
|
|
.into_iter()
|
|
|
|
.map(move |item| {
|
|
|
|
(this_local_client, item)
|
|
|
|
})
|
|
|
|
.chain(other.items
|
|
|
|
.clone()
|
|
|
|
.into_iter()
|
|
|
|
.map(move |item| {
|
|
|
|
(other_local_client, item)
|
|
|
|
}))
|
|
|
|
.map(|(client, item)| {
|
|
|
|
GameMessage::PlayerNoLongerHasItem(builder::message::player_no_longer_has_item(client, item.item_id(), item.amount() as u32))
|
|
|
|
});
|
|
|
|
|
|
|
|
let (this_new_items, other_new_items) = trade_items(item_state,
|
|
|
|
&mut entity_gateway,
|
|
|
|
(&this_local_client, &this_client.character, &this.items, Meseta(this.meseta as u32)),
|
|
|
|
(&other_local_client, &other_client.character, &other.items, Meseta(other.meseta as u32))).await?;
|
|
|
|
|
|
|
|
let create_item_packets = this_new_items
|
|
|
|
.into_iter()
|
|
|
|
.map(move |item| {
|
|
|
|
(this_local_client, item)
|
|
|
|
})
|
|
|
|
.chain(other_new_items
|
|
|
|
.into_iter()
|
|
|
|
.map(move |item| {
|
|
|
|
(other_local_client, item)
|
|
|
|
}))
|
|
|
|
.map(|(client, item)| {
|
|
|
|
match item.item {
|
|
|
|
InventoryItemDetail::Individual(individual_item) => {
|
|
|
|
GameMessage::CreateItem(builder::message::create_individual_item(client, item.item_id, &individual_item).unwrap())
|
|
|
|
},
|
|
|
|
InventoryItemDetail::Stacked(stacked_item) => {
|
|
|
|
GameMessage::CreateItem(builder::message::create_stacked_item(client, item.item_id, &stacked_item.tool, stacked_item.count()).unwrap())
|
|
|
|
.filter_map(move |client| {
|
|
|
|
match packet {
|
|
|
|
GameMessage::PlayerNoLongerHasItem(ref no_longer) => {
|
|
|
|
if client.local_client == no_longer.client {
|
|
|
|
None
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
Some((client.client, SendShipPacket::Message(Message::new(packet.clone()))))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
_ => Some((client.client, SendShipPacket::Message(Message::new(packet.clone()))))
|
|
|
|
}
|
|
|
|
});
|
|
|
|
})
|
|
|
|
});
|
|
|
|
|
|
|
|
let meseta_packets = vec![(this_local_client, other_local_client, this.meseta), (other_local_client, this_local_client, other.meseta)]
|
|
|
|
.into_iter()
|
|
|
|
.filter(|(_, _, meseta)| *meseta != 0)
|
|
|
|
.flat_map(|(this, other, meseta)| {
|
|
|
|
[
|
|
|
|
GameMessage::PlayerNoLongerHasItem(builder::message::player_no_longer_has_item(this, MESETA_ITEM_ID, meseta as u32)),
|
|
|
|
GameMessage::CreateItem(builder::message::create_meseta(other, meseta)),
|
|
|
|
]
|
|
|
|
});
|
|
|
|
|
|
|
|
let clients_in_room = client_location.get_all_clients_by_client(id)?;
|
|
|
|
let traded_item_packets = remove_item_packets
|
|
|
|
.chain(create_item_packets)
|
|
|
|
.chain(meseta_packets)
|
|
|
|
.flat_map(move |packet| {
|
|
|
|
clients_in_room
|
|
|
|
.clone()
|
|
|
|
.into_iter()
|
|
|
|
.filter_map(move |client| {
|
|
|
|
match packet {
|
|
|
|
GameMessage::PlayerNoLongerHasItem(ref no_longer) => {
|
|
|
|
if client.local_client == no_longer.client {
|
|
|
|
None
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
Some((client.client, SendShipPacket::Message(Message::new(packet.clone()))))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
_ => Some((client.client, SendShipPacket::Message(Message::new(packet.clone()))))
|
|
|
|
}
|
|
|
|
})
|
|
|
|
});
|
|
|
|
|
|
|
|
let close_trade = vec![
|
|
|
|
(this.client(), SendShipPacket::TradeSuccessful(TradeSuccessful::default())),
|
|
|
|
(other.client(), SendShipPacket::TradeSuccessful(TradeSuccessful::default()))
|
|
|
|
].into_iter();
|
|
|
|
Ok(Box::new(traded_item_packets.chain(close_trade)))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
},
|
|
|
|
_ => {
|
|
|
|
let close_trade = vec![
|
|
|
|
(this.client(), SendShipPacket::TradeSuccessful(TradeSuccessful::default())),
|
|
|
|
(other.client(), SendShipPacket::TradeSuccessful(TradeSuccessful::default()))
|
|
|
|
].into_iter();
|
|
|
|
Ok(Box::new(traded_item_packets.chain(close_trade)))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
pub async fn trade_confirmed<EG>(id: ClientId,
|
|
|
|
entity_gateway: EG,
|
|
|
|
client_location: &ClientLocation,
|
|
|
|
clients: &mut Clients,
|
|
|
|
item_state: &mut ItemState,
|
|
|
|
trades: &mut TradeState)
|
|
|
|
-> Result<Box<dyn Iterator<Item = (ClientId, SendShipPacket)> + Send>, ShipError>
|
|
|
|
where
|
|
|
|
EG: EntityGateway
|
|
|
|
{
|
|
|
|
match trade_confirmed_inner(id, entity_gateway, client_location, clients, item_state, trades).await {
|
|
|
|
Ok(result) => Ok(result),
|
|
|
|
Err(_err) => {
|
|
|
|
let (_this, other) = trades.remove_trade(&id);
|
|
|
|
Ok(Box::new(client_location.get_all_clients_by_client(id).unwrap().into_iter()
|
|
|
|
Ok(Box::new(client_location.get_all_clients_by_client(id).await?.into_iter()
|
|
|
|
.filter(move |client| other.as_ref().map(|other| client.client == other.client()).unwrap_or_else(|| false))
|
|
|
|
.map(move |client| {
|
|
|
|
(client.client, SendShipPacket::CancelTrade(CancelTrade {}))
|
|
|
|