trades!
This commit is contained in:
parent
5d410f88f3
commit
b3b6dad6ad
@ -268,7 +268,6 @@ impl EntityGateway for InMemoryGateway {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async fn get_character_inventory(&mut self, char_id: &CharacterEntityId) -> Result<InventoryEntity, GatewayError> {
|
async fn get_character_inventory(&mut self, char_id: &CharacterEntityId) -> Result<InventoryEntity, GatewayError> {
|
||||||
println!("getting inv");
|
|
||||||
let inventories = self.inventories.lock().unwrap();
|
let inventories = self.inventories.lock().unwrap();
|
||||||
Ok(inventories
|
Ok(inventories
|
||||||
.iter()
|
.iter()
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
use log::warn;
|
||||||
use crate::ship::items::ClientItemId;
|
use crate::ship::items::ClientItemId;
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use std::cell::RefCell;
|
use std::cell::RefCell;
|
||||||
@ -9,7 +10,8 @@ use crate::entity::item::{Meseta, NewItemEntity, ItemEntity, ItemEntityId, Inven
|
|||||||
use crate::entity::item::tool::{Tool, ToolType};
|
use crate::entity::item::tool::{Tool, ToolType};
|
||||||
use crate::entity::item::weapon;
|
use crate::entity::item::weapon;
|
||||||
use crate::ship::map::MapArea;
|
use crate::ship::map::MapArea;
|
||||||
use crate::ship::ship::{TradeItem, ItemDropLocation};
|
use crate::ship::ship::ItemDropLocation;
|
||||||
|
use crate::ship::trade::TradeItem;
|
||||||
use crate::ship::drops::{ItemDrop, ItemDropType};
|
use crate::ship::drops::{ItemDrop, ItemDropType};
|
||||||
use crate::ship::location::{AreaClient, RoomId};
|
use crate::ship::location::{AreaClient, RoomId};
|
||||||
use crate::ship::shops::ShopItem;
|
use crate::ship::shops::ShopItem;
|
||||||
@ -921,41 +923,41 @@ impl ItemManager {
|
|||||||
|
|
||||||
pub async fn trade_items<EG: EntityGateway>(&mut self,
|
pub async fn trade_items<EG: EntityGateway>(&mut self,
|
||||||
entity_gateway: &mut EG,
|
entity_gateway: &mut EG,
|
||||||
|
room_id: RoomId,
|
||||||
p1: (&AreaClient, &CharacterEntity, &Vec<TradeItem>),
|
p1: (&AreaClient, &CharacterEntity, &Vec<TradeItem>),
|
||||||
p2: (&AreaClient, &CharacterEntity, &Vec<TradeItem>))
|
p2: (&AreaClient, &CharacterEntity, &Vec<TradeItem>))
|
||||||
-> Result<Vec<ItemToTrade>, anyhow::Error> {
|
-> Result<Vec<ItemToTrade>, anyhow::Error> {
|
||||||
let it = ItemTransaction::new(&self, (p1, p2))
|
let it = ItemTransaction::new(&self, (p1, p2, room_id))
|
||||||
.act(|it, (p1, p2)| -> Result<_, anyhow::Error> {
|
.act(|it, (p1, p2, room_id)| -> Result<_, anyhow::Error> {
|
||||||
let p1_inventory = it.manager.get_character_inventory(p1.1)?;
|
let p1_inventory = it.manager.get_character_inventory(p1.1)?;
|
||||||
let p2_inventory = it.manager.get_character_inventory(p2.1)?;
|
let p2_inventory = it.manager.get_character_inventory(p2.1)?;
|
||||||
|
|
||||||
//TODO: inv-selftrade+othertrade <= 30
|
//TODO: inv-selftrade+othertrade <= 30
|
||||||
//if p1_inventory
|
//if p1_inventory
|
||||||
|
|
||||||
let trade_items = [(p1, p2, p1_inventory), (p2, p1, p2_inventory)]
|
|
||||||
.map(|((src_client, dest_client, src_inventory))| {
|
let trade_items = [(p1, p2, p1_inventory, p2_inventory), (p2, p1, p2_inventory, p1_inventory)]
|
||||||
|
.map(|(src_client, dest_client, src_inventory, dest_inventory)| {
|
||||||
src_client.2.iter()
|
src_client.2.iter()
|
||||||
.map(|item| -> Option<(Option<ItemToTrade>, Vec<Box<dyn ItemAction<EG>>>)> {
|
.map(|item| -> Option<(Option<ItemToTrade>, Box<dyn ItemAction<EG>>)> {
|
||||||
match item {
|
match item {
|
||||||
TradeItem::Individual(item_id) => {
|
TradeItem::Individual(item_id) => {
|
||||||
let item = src_inventory.get_item_by_id(*item_id)?.individual()?;
|
let item = src_inventory.get_item_by_id(*item_id)?.individual()?;
|
||||||
|
let new_item_id = it.manager.room_item_id_counter.borrow_mut().get_mut(&room_id)?();
|
||||||
Some((
|
Some((
|
||||||
Some(ItemToTrade {
|
Some(ItemToTrade {
|
||||||
add_to: *dest_client.0,
|
add_to: *dest_client.0,
|
||||||
remove_from: *src_client.0,
|
remove_from: *src_client.0,
|
||||||
item_id: *item_id,
|
current_item_id: *item_id,
|
||||||
|
new_item_id: new_item_id,
|
||||||
item_detail: ItemToTradeDetail::Individual(item.item.clone())
|
item_detail: ItemToTradeDetail::Individual(item.item.clone())
|
||||||
}),
|
}),
|
||||||
vec![
|
Box::new(TradeIndividualItem {
|
||||||
Box::new(AddIndividualItemToInventory {
|
src_character_id: src_client.1.id,
|
||||||
character_id: dest_client.1.id,
|
dest_character_id: dest_client.1.id,
|
||||||
item_id: item.entity_id,
|
current_item_id: *item_id,
|
||||||
}),
|
new_item_id: new_item_id,
|
||||||
Box::new(RemoveIndividualItemFromInventory {
|
}),
|
||||||
character_id: src_client.1.id,
|
|
||||||
item_id: item.entity_id,
|
|
||||||
})
|
|
||||||
]
|
|
||||||
))
|
))
|
||||||
},
|
},
|
||||||
TradeItem::Stacked(item_id, amount) => {
|
TradeItem::Stacked(item_id, amount) => {
|
||||||
@ -964,38 +966,33 @@ impl ItemManager {
|
|||||||
None
|
None
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
|
let new_item_id = it.manager.room_item_id_counter.borrow_mut().get_mut(&room_id)?();
|
||||||
Some((
|
Some((
|
||||||
Some(ItemToTrade {
|
Some(ItemToTrade {
|
||||||
add_to: *dest_client.0,
|
add_to: *dest_client.0,
|
||||||
remove_from: *src_client.0,
|
remove_from: *src_client.0,
|
||||||
item_id: *item_id,
|
current_item_id: *item_id,
|
||||||
|
new_item_id: new_item_id,
|
||||||
item_detail: ItemToTradeDetail::Stacked(item.tool, *amount)
|
item_detail: ItemToTradeDetail::Stacked(item.tool, *amount)
|
||||||
}),
|
}),
|
||||||
vec![
|
Box::new(TradeStackedItem {
|
||||||
Box::new(AddStackedItemToInventory {
|
src_character_id: src_client.1.id,
|
||||||
character_id: dest_client.1.id,
|
dest_character_id: dest_client.1.id,
|
||||||
item_ids: item.entity_ids.iter().cloned().take(*amount).collect(),
|
//item_ids: item.entity_ids.iter().cloned().take(*amount).collect(),
|
||||||
}),
|
current_item_id: *item_id,
|
||||||
Box::new(RemoveStackedItemFromInventory {
|
new_item_id: new_item_id,
|
||||||
character_id: src_client.1.id,
|
amount: *amount,
|
||||||
item_ids: item.entity_ids.iter().cloned().take(*amount).collect(),
|
}),
|
||||||
}),
|
|
||||||
]
|
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
TradeItem::Meseta(amount) => {
|
TradeItem::Meseta(amount) => {
|
||||||
Some((None,
|
Some((None, // is there a packet that informs other clients about meseta changes?
|
||||||
vec![
|
Box::new(TradeMeseta {
|
||||||
Box::new(AddMesetaToInventory {
|
src_character_id: src_client.1.id,
|
||||||
character_id: dest_client.1.id,
|
dest_character_id: dest_client.1.id,
|
||||||
amount: *amount,
|
amount: *amount,
|
||||||
}),
|
}),
|
||||||
Box::new(RemoveMesetaFromInventory {
|
|
||||||
character_id: src_client.1.id,
|
|
||||||
amount: *amount,
|
|
||||||
}),
|
|
||||||
]
|
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1003,11 +1000,13 @@ impl ItemManager {
|
|||||||
.collect::<Option<Vec<_>>>()
|
.collect::<Option<Vec<_>>>()
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
if let [Some(p1_trades), Some(p2_trades)] = trade_items {
|
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 (p1_item_trades, p1_item_actions): (Vec<Option<ItemToTrade>>, 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 (p2_item_trades, p2_item_actions): (Vec<Option<ItemToTrade>>, 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_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());
|
let item_actions = p1_item_actions.into_iter().chain(p2_item_actions.into_iter());
|
||||||
|
|
||||||
for action in item_actions {
|
for action in item_actions {
|
||||||
it.action(action);
|
it.action(action);
|
||||||
@ -1020,25 +1019,30 @@ impl ItemManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
});
|
});
|
||||||
it.commit(self, entity_gateway)
|
Ok(it.commit(self, entity_gateway)
|
||||||
.await
|
.await
|
||||||
.map_err(|err| err.into())
|
//.map_err(|err| err.into())
|
||||||
|
.unwrap())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
pub enum ItemToTradeDetail {
|
pub enum ItemToTradeDetail {
|
||||||
Individual(ItemDetail),
|
Individual(ItemDetail),
|
||||||
Stacked(Tool, usize),
|
Stacked(Tool, usize),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
pub struct ItemToTrade {
|
pub struct ItemToTrade {
|
||||||
pub add_to: AreaClient,
|
pub add_to: AreaClient,
|
||||||
pub remove_from: AreaClient,
|
pub remove_from: AreaClient,
|
||||||
pub item_id: ClientItemId,
|
pub current_item_id: ClientItemId,
|
||||||
|
pub new_item_id: ClientItemId,
|
||||||
pub item_detail: ItemToTradeDetail,
|
pub item_detail: ItemToTradeDetail,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
struct RemoveFromLocalFloor {
|
struct RemoveFromLocalFloor {
|
||||||
character_id: CharacterEntityId,
|
character_id: CharacterEntityId,
|
||||||
item_id: ClientItemId,
|
item_id: ClientItemId,
|
||||||
@ -1046,7 +1050,7 @@ struct RemoveFromLocalFloor {
|
|||||||
|
|
||||||
#[async_trait::async_trait]
|
#[async_trait::async_trait]
|
||||||
impl<EG: EntityGateway> ItemAction<EG> for RemoveFromLocalFloor {
|
impl<EG: EntityGateway> ItemAction<EG> for RemoveFromLocalFloor {
|
||||||
async fn commit(&self, item_manager: &mut ItemManager, entity_gateway: &mut EG) -> Result<(), TransactionCommitError> {
|
async fn commit(&self, item_manager: &mut ItemManager, _entity_gateway: &mut EG) -> Result<(), TransactionCommitError> {
|
||||||
let local_floor = item_manager.character_floor.get_mut(&self.character_id).ok_or(ItemManagerError::NoCharacter(self.character_id))?;
|
let local_floor = item_manager.character_floor.get_mut(&self.character_id).ok_or(ItemManagerError::NoCharacter(self.character_id))?;
|
||||||
local_floor.remove_item(&self.item_id);
|
local_floor.remove_item(&self.item_id);
|
||||||
Ok(())
|
Ok(())
|
||||||
@ -1054,6 +1058,7 @@ impl<EG: EntityGateway> ItemAction<EG> for RemoveFromLocalFloor {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
struct RemoveFromSharedFloor {
|
struct RemoveFromSharedFloor {
|
||||||
room_id: RoomId,
|
room_id: RoomId,
|
||||||
item_id: ClientItemId,
|
item_id: ClientItemId,
|
||||||
@ -1061,7 +1066,7 @@ struct RemoveFromSharedFloor {
|
|||||||
|
|
||||||
#[async_trait::async_trait]
|
#[async_trait::async_trait]
|
||||||
impl<EG: EntityGateway> ItemAction<EG> for RemoveFromSharedFloor {
|
impl<EG: EntityGateway> ItemAction<EG> for RemoveFromSharedFloor {
|
||||||
async fn commit(&self, item_manager: &mut ItemManager, entity_gateway: &mut EG) -> Result<(), TransactionCommitError> {
|
async fn commit(&self, item_manager: &mut ItemManager, _entity_gateway: &mut EG) -> Result<(), TransactionCommitError> {
|
||||||
let shared_floor = item_manager.room_floor.get_mut(&self.room_id).ok_or(ItemManagerError::NoRoom(self.room_id))?;
|
let shared_floor = item_manager.room_floor.get_mut(&self.room_id).ok_or(ItemManagerError::NoRoom(self.room_id))?;
|
||||||
shared_floor.remove_item(&self.item_id);
|
shared_floor.remove_item(&self.item_id);
|
||||||
Ok(())
|
Ok(())
|
||||||
@ -1069,6 +1074,7 @@ impl<EG: EntityGateway> ItemAction<EG> for RemoveFromSharedFloor {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
struct AddIndividualFloorItemToInventory{
|
struct AddIndividualFloorItemToInventory{
|
||||||
character: CharacterEntity,
|
character: CharacterEntity,
|
||||||
item: IndividualFloorItem,
|
item: IndividualFloorItem,
|
||||||
@ -1080,7 +1086,6 @@ impl<EG: EntityGateway> ItemAction<EG> for AddIndividualFloorItemToInventory {
|
|||||||
let inventory = item_manager.character_inventory.get_mut(&self.character.id).ok_or(ItemManagerError::NoCharacter(self.character.id))?;
|
let inventory = item_manager.character_inventory.get_mut(&self.character.id).ok_or(ItemManagerError::NoCharacter(self.character.id))?;
|
||||||
let inv_item = inventory.add_individual_floor_item(&self.item);
|
let inv_item = inventory.add_individual_floor_item(&self.item);
|
||||||
|
|
||||||
|
|
||||||
entity_gateway.add_item_note(
|
entity_gateway.add_item_note(
|
||||||
&self.item.entity_id,
|
&self.item.entity_id,
|
||||||
ItemNote::Pickup {
|
ItemNote::Pickup {
|
||||||
@ -1098,6 +1103,7 @@ impl<EG: EntityGateway> ItemAction<EG> for AddIndividualFloorItemToInventory {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
struct AddStackedFloorItemToInventory{
|
struct AddStackedFloorItemToInventory{
|
||||||
character_id: CharacterEntityId,
|
character_id: CharacterEntityId,
|
||||||
item: StackedFloorItem,
|
item: StackedFloorItem,
|
||||||
@ -1115,6 +1121,7 @@ impl<EG: EntityGateway> ItemAction<EG> for AddStackedFloorItemToInventory {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
struct AddMesetaFloorItemToInventory{
|
struct AddMesetaFloorItemToInventory{
|
||||||
character: CharacterEntity,
|
character: CharacterEntity,
|
||||||
item: MesetaFloorItem,
|
item: MesetaFloorItem,
|
||||||
@ -1131,73 +1138,62 @@ impl<EG: EntityGateway> ItemAction<EG> for AddMesetaFloorItemToInventory {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
struct AddIndividualItemToInventory {
|
#[derive(Debug)]
|
||||||
character_id: CharacterEntityId,
|
struct TradeIndividualItem {
|
||||||
item_id: ItemEntityId,
|
src_character_id: CharacterEntityId,
|
||||||
|
dest_character_id: CharacterEntityId,
|
||||||
|
current_item_id: ClientItemId,
|
||||||
|
new_item_id: ClientItemId,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[async_trait::async_trait]
|
#[async_trait::async_trait]
|
||||||
impl<EG: EntityGateway> ItemAction<EG> for AddIndividualItemToInventory {
|
impl<EG: EntityGateway> ItemAction<EG> for TradeIndividualItem {
|
||||||
async fn commit(&self, _item_manager: &mut ItemManager, entity_gateway: &mut EG) -> Result<(), TransactionCommitError> {
|
async fn commit(&self, item_manager: &mut ItemManager, entity_gateway: &mut EG) -> Result<(), TransactionCommitError> {
|
||||||
|
let src_inventory = item_manager.character_inventory.get_mut(&self.src_character_id).ok_or(ItemManagerError::NoCharacter(self.src_character_id))?;
|
||||||
|
let inventory_item = src_inventory.take_item_by_id(self.current_item_id).ok_or(ItemManagerError::NoSuchItemId(self.current_item_id))?;
|
||||||
|
entity_gateway.set_character_inventory(&self.src_character_id, &src_inventory.as_inventory_entity(&self.src_character_id)).await?;
|
||||||
|
|
||||||
|
let dest_inventory = item_manager.character_inventory.get_mut(&self.dest_character_id).ok_or(ItemManagerError::NoCharacter(self.dest_character_id))?;
|
||||||
|
dest_inventory.add_item(inventory_item);
|
||||||
|
entity_gateway.set_character_inventory(&self.dest_character_id, &dest_inventory.as_inventory_entity(&self.dest_character_id)).await?;
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
struct AddStackedItemToInventory {
|
#[derive(Debug)]
|
||||||
character_id: CharacterEntityId,
|
struct TradeStackedItem {
|
||||||
item_ids: Vec<ItemEntityId>,
|
src_character_id: CharacterEntityId,
|
||||||
}
|
dest_character_id: CharacterEntityId,
|
||||||
|
current_item_id: ClientItemId,
|
||||||
#[async_trait::async_trait]
|
new_item_id: ClientItemId,
|
||||||
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,
|
amount: usize,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[async_trait::async_trait]
|
#[async_trait::async_trait]
|
||||||
impl<EG: EntityGateway> ItemAction<EG> for AddMesetaToInventory {
|
impl<EG: EntityGateway> ItemAction<EG> for TradeStackedItem {
|
||||||
async fn commit(&self, _item_manager: &mut ItemManager, entity_gateway: &mut EG) -> Result<(), TransactionCommitError> {
|
async fn commit(&self, item_manager: &mut ItemManager, entity_gateway: &mut EG) -> Result<(), TransactionCommitError> {
|
||||||
|
let src_inventory = item_manager.character_inventory.get_mut(&self.src_character_id).ok_or(ItemManagerError::NoCharacter(self.src_character_id))?;
|
||||||
|
let inventory_item = src_inventory.take_stacked_item_by_id(self.current_item_id, self.amount).ok_or(ItemManagerError::NoSuchItemId(self.current_item_id))?;
|
||||||
|
entity_gateway.set_character_inventory(&self.src_character_id, &src_inventory.as_inventory_entity(&self.src_character_id)).await?;
|
||||||
|
|
||||||
|
let dest_inventory = item_manager.character_inventory.get_mut(&self.dest_character_id).ok_or(ItemManagerError::NoCharacter(self.dest_character_id))?;
|
||||||
|
dest_inventory.add_stacked_item(inventory_item);
|
||||||
|
entity_gateway.set_character_inventory(&self.dest_character_id, &dest_inventory.as_inventory_entity(&self.dest_character_id)).await?;
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
struct RemoveMesetaFromInventory {
|
#[derive(Debug)]
|
||||||
character_id: CharacterEntityId,
|
struct TradeMeseta {
|
||||||
|
src_character_id: CharacterEntityId,
|
||||||
|
dest_character_id: CharacterEntityId,
|
||||||
amount: usize,
|
amount: usize,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[async_trait::async_trait]
|
#[async_trait::async_trait]
|
||||||
impl<EG: EntityGateway> ItemAction<EG> for RemoveMesetaFromInventory {
|
impl<EG: EntityGateway> ItemAction<EG> for TradeMeseta {
|
||||||
async fn commit(&self, _item_manager: &mut ItemManager, entity_gateway: &mut EG) -> Result<(), TransactionCommitError> {
|
async fn commit(&self, _item_manager: &mut ItemManager, entity_gateway: &mut EG) -> Result<(), TransactionCommitError> {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
@ -20,7 +20,7 @@ pub enum TransactionCommitError {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[async_trait::async_trait]
|
#[async_trait::async_trait]
|
||||||
pub trait ItemAction<EG: EntityGateway>: std::marker::Send + std::marker::Sync {
|
pub trait ItemAction<EG: EntityGateway>: std::marker::Send + std::marker::Sync + std::fmt::Debug {
|
||||||
async fn commit(&self, manager: &mut ItemManager, entity_gateway: &mut EG) -> Result<(), TransactionCommitError>;
|
async fn commit(&self, manager: &mut ItemManager, entity_gateway: &mut EG) -> Result<(), TransactionCommitError>;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -11,3 +11,4 @@ pub mod drops;
|
|||||||
pub mod packet;
|
pub mod packet;
|
||||||
pub mod quests;
|
pub mod quests;
|
||||||
pub mod shops;
|
pub mod shops;
|
||||||
|
pub mod trade;
|
||||||
|
@ -6,11 +6,12 @@ use libpso::packet::ship::*;
|
|||||||
use libpso::packet::messages::*;
|
use libpso::packet::messages::*;
|
||||||
use crate::common::leveltable::CharacterLevelTable;
|
use crate::common::leveltable::CharacterLevelTable;
|
||||||
use crate::common::serverstate::ClientId;
|
use crate::common::serverstate::ClientId;
|
||||||
use crate::ship::ship::{SendShipPacket, ShipError, Clients, Rooms, ItemShops, TradeItem, TradeState, TradeStatus};
|
use crate::ship::ship::{SendShipPacket, ShipError, Clients, Rooms, ItemShops};
|
||||||
use crate::ship::location::{ClientLocation, ClientLocationError};
|
use crate::ship::location::{ClientLocation, ClientLocationError, LocalClientId};
|
||||||
use crate::ship::drops::ItemDrop;
|
use crate::ship::drops::ItemDrop;
|
||||||
use crate::ship::items::{ItemManager, ItemManagerError, ClientItemId, TriggerCreateItem, FloorItem, FloorType, ItemToTradeDetail};
|
use crate::ship::items::{ItemManager, ItemManagerError, ClientItemId, TriggerCreateItem, FloorItem, FloorType, ItemToTradeDetail};
|
||||||
use crate::ship::items::inventory::InventoryItem;
|
use crate::ship::items::inventory::InventoryItem;
|
||||||
|
use crate::ship::trade::{TradeItem, TradeState, TradeStatus};
|
||||||
use crate::entity::gateway::EntityGateway;
|
use crate::entity::gateway::EntityGateway;
|
||||||
use crate::entity::item;
|
use crate::entity::item;
|
||||||
use libpso::utf8_to_utf16_array;
|
use libpso::utf8_to_utf16_array;
|
||||||
@ -18,80 +19,320 @@ use crate::ship::packet::builder;
|
|||||||
use crate::ship::shops::{ShopItem, ToolShopItem, ArmorShopItem};
|
use crate::ship::shops::{ShopItem, ToolShopItem, ArmorShopItem};
|
||||||
|
|
||||||
#[derive(thiserror::Error, Debug)]
|
#[derive(thiserror::Error, Debug)]
|
||||||
#[error("")]
|
|
||||||
pub enum TradeError {
|
pub enum TradeError {
|
||||||
|
#[error("no partner")]
|
||||||
CouldNotFindTradePartner,
|
CouldNotFindTradePartner,
|
||||||
|
#[error("item does not match id")]
|
||||||
ClientItemIdDidNotMatchItem(ClientItemId, [u8; 16]),
|
ClientItemIdDidNotMatchItem(ClientItemId, [u8; 16]),
|
||||||
|
#[error("invalid stack {1}")]
|
||||||
InvalidStackAmount(ClientItemId, usize),
|
InvalidStackAmount(ClientItemId, usize),
|
||||||
|
#[error("not in trade menu")]
|
||||||
NotInTradeMenu,
|
NotInTradeMenu,
|
||||||
|
#[error("trade menu at an invalid point")]
|
||||||
|
MismatchedStatus,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// TODO: remove target
|
||||||
pub async fn inner_items_to_trade<EG>(id: ClientId,
|
pub async fn trade_request<EG>(id: ClientId,
|
||||||
items_to_trade: &ItemsToTrade,
|
trade_request: &TradeRequest,
|
||||||
entity_gateway: &mut EG,
|
target: u32,
|
||||||
client_location: &ClientLocation,
|
entity_gateway: &mut EG,
|
||||||
clients: &mut Clients,
|
client_location: &ClientLocation,
|
||||||
item_manager: &mut ItemManager)
|
clients: &mut Clients,
|
||||||
-> Result<Box<dyn Iterator<Item = (ClientId, SendShipPacket)> + Send>, anyhow::Error>
|
item_manager: &mut ItemManager,
|
||||||
|
trades: &mut TradeState)
|
||||||
|
-> Result<Box<dyn Iterator<Item = (ClientId, SendShipPacket)> + Send>, anyhow::Error>
|
||||||
where
|
where
|
||||||
EG: EntityGateway
|
EG: EntityGateway
|
||||||
{
|
{
|
||||||
let client = clients.get_mut(&id).ok_or(ShipError::ClientNotFound(id))?;
|
let trade_request = trade_request.clone(); // TODO: make this function take ownership of packet
|
||||||
let inventory = item_manager.get_character_inventory_mut(&client.character)?;
|
match trade_request.trade {
|
||||||
let trade_partner = client_location.get_client_neighbors(id)?
|
TradeRequestCommand::Initialize(ref act, meseta) => {
|
||||||
.into_iter()
|
match act {
|
||||||
.filter(|ac| {
|
TradeRequestInitializeCommand::Initialize => {
|
||||||
ac.local_client.id() == items_to_trade.trade_target
|
let trade_partner = client_location.get_client_neighbors(id)?
|
||||||
})
|
.into_iter()
|
||||||
.next()
|
.filter(|ac| {
|
||||||
.ok_or(TradeError::CouldNotFindTradePartner)?;
|
ac.local_client.id() == trade_request.client
|
||||||
|
})
|
||||||
|
.next()
|
||||||
|
.ok_or(TradeError::CouldNotFindTradePartner)?;
|
||||||
|
trades.new_trade(&id, &trade_partner.client);
|
||||||
|
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()))))
|
||||||
|
})))
|
||||||
|
},
|
||||||
|
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 item_blobs = items_to_trade.items.iter().take(items_to_trade.count as usize);
|
let trade_request = trade_request.clone();
|
||||||
let trade_items = item_blobs
|
Some(Box::new(client_location.get_all_clients_by_client(id).ok()?.into_iter()
|
||||||
.map(|item| {
|
.filter(move |client| client.local_client.id() == target as u8)
|
||||||
// TOOD: meseta?
|
.map(move |client| {
|
||||||
let real_item = inventory.get_item_handle_by_id(ClientItemId(item.item_id))
|
(client.client, SendShipPacket::DirectMessage(DirectMessage::new(target, GameMessage::TradeRequest(trade_request.clone()))))
|
||||||
.ok_or(ItemManagerError::NoSuchItemId(ClientItemId(item.item_id)))?;
|
})))
|
||||||
let real_item = real_item
|
}
|
||||||
.item()
|
else {
|
||||||
.ok_or(ItemManagerError::NoSuchItemId(ClientItemId(item.item_id)))?;
|
None
|
||||||
let trade_item_bytes: [u8; 16] = item.item_data.iter()
|
}
|
||||||
.chain(item.item_data2.iter())
|
})?
|
||||||
.cloned().collect::<Vec<u8>>()
|
.unwrap_or_else(|| -> Box<dyn Iterator<Item = (ClientId, SendShipPacket)> + Send> {
|
||||||
.try_into()
|
trades.remove_trade(&id);
|
||||||
.unwrap();
|
Box::new(client_location.get_all_clients_by_client(id).unwrap().into_iter()
|
||||||
if real_item.as_client_bytes() == trade_item_bytes {
|
.filter(move |client| client.local_client.id() == target as u8)
|
||||||
match real_item {
|
.map(move |client| {
|
||||||
InventoryItem::Individual(individual_inventory_item) => {
|
(client.client, SendShipPacket::CancelTrade(CancelTrade {}))
|
||||||
Ok(TradeItem::Individual(individual_inventory_item.item_id))
|
})
|
||||||
},
|
.chain(std::iter::once((id, SendShipPacket::CancelTrade(CancelTrade {})))))
|
||||||
InventoryItem::Stacked(stacked_inventory_item) => {
|
}))
|
||||||
let amount = trade_item_bytes[5] as usize;
|
|
||||||
if amount > stacked_inventory_item.entity_ids.len() {
|
|
||||||
Ok(TradeItem::Stacked(stacked_inventory_item.item_id, amount))
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
Err(TradeError::InvalidStackAmount(stacked_inventory_item.item_id, amount).into())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else {
|
},
|
||||||
Err(TradeError::ClientItemIdDidNotMatchItem(ClientItemId(item.item_id), trade_item_bytes).into())
|
TradeRequestCommand::AddItem(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_manager.get_character_inventory(&client.character).ok()?;
|
||||||
|
let item = inventory.get_item_by_id(ClientItemId(item_id))?;
|
||||||
|
|
||||||
|
match item {
|
||||||
|
InventoryItem::Individual(_) => {
|
||||||
|
this.items.push(TradeItem::Individual(ClientItemId(item_id)));
|
||||||
|
},
|
||||||
|
InventoryItem::Stacked(stacked_item) => {
|
||||||
|
if stacked_item.count() < amount as usize {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
this.items.push(TradeItem::Stacked(ClientItemId(item_id), amount as usize));
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
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
|
||||||
|
}
|
||||||
|
})?
|
||||||
|
.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 {})))))
|
||||||
|
}))
|
||||||
|
},
|
||||||
|
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_manager.get_character_inventory(&client.character).ok()?;
|
||||||
|
let item = inventory.get_item_by_id(ClientItemId(item_id))?;
|
||||||
|
|
||||||
|
match item {
|
||||||
|
InventoryItem::Individual(_) => {
|
||||||
|
this.items.retain(|item| {
|
||||||
|
item.item_id() != ClientItemId(item_id)
|
||||||
|
})
|
||||||
|
//this.items.push(TradeItem::Individual(ClientItemId(item_id)));
|
||||||
|
},
|
||||||
|
InventoryItem::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()?.1 -= amount as usize;
|
||||||
|
},
|
||||||
|
std::cmp::Ordering::Equal => {
|
||||||
|
this.items.remove(trade_item_index);
|
||||||
|
},
|
||||||
|
std::cmp::Ordering::Less => {
|
||||||
|
return None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
}
|
||||||
|
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
|
||||||
|
}
|
||||||
|
})?
|
||||||
|
.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 {})))))
|
||||||
|
}))
|
||||||
|
},
|
||||||
|
TradeRequestCommand::Confirm => {
|
||||||
|
Ok(trades
|
||||||
|
.with(&id, |this, other| -> Option<Box<dyn Iterator<Item = (ClientId, SendShipPacket)> + Send>> {
|
||||||
|
if this.status == TradeStatus::Trading && (other.status == TradeStatus::Trading || other.status == 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
|
||||||
|
}
|
||||||
|
})?
|
||||||
|
.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 {})))))
|
||||||
|
}))
|
||||||
|
},
|
||||||
|
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
|
||||||
|
}
|
||||||
|
})?
|
||||||
|
.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 {})))))
|
||||||
|
}))
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
fn status_is<const N: usize>(status: &TradeStatus, statuses: &[TradeStatus; N]) -> bool {
|
||||||
|
statuses.iter().any(|s| s == status)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn status_is_not<const N: usize>(status: &TradeStatus, statuses: &[TradeStatus; N]) -> bool {
|
||||||
|
!status_is(status, statuses)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn inner_items_to_trade<EG>(id: ClientId,
|
||||||
|
items_to_trade: &ItemsToTrade,
|
||||||
|
entity_gateway: &mut EG,
|
||||||
|
client_location: &ClientLocation,
|
||||||
|
clients: &mut Clients,
|
||||||
|
item_manager: &mut ItemManager,
|
||||||
|
trades: &mut TradeState)
|
||||||
|
-> Result<Box<dyn Iterator<Item = (ClientId, SendShipPacket)> + Send>, anyhow::Error>
|
||||||
|
where
|
||||||
|
EG: EntityGateway
|
||||||
|
{
|
||||||
|
Ok(trades
|
||||||
|
.with(&id, |this, other| -> Result<Box<dyn Iterator<Item = (ClientId, SendShipPacket)> + Send>, anyhow::Error> {
|
||||||
|
println!("statuses {:?} {:?}", this.status, other.status);
|
||||||
|
if status_is_not(&this.status, &[TradeStatus::FinalConfirm]) || status_is_not(&other.status, &[TradeStatus::FinalConfirm, TradeStatus::ItemsChecked]) {
|
||||||
|
//if this.status != TradeStatus::FinalConfirm || (other.status != TradeStatus::FinalConfirm || other.status != TradeStatus::ItemsChecked) {
|
||||||
|
return Err(TradeError::MismatchedStatus.into())
|
||||||
}
|
}
|
||||||
})
|
|
||||||
.collect::<Result<Vec<_>, anyhow::Error>>()?;
|
|
||||||
|
|
||||||
// TODO: check room in inventory for items
|
let client = clients.get(&this.client()).ok_or(ShipError::ClientNotFound(this.client()))?;
|
||||||
client.trade = Some(TradeState {
|
let inventory = item_manager.get_character_inventory(&client.character)?;
|
||||||
other_client: trade_partner.client,
|
|
||||||
items: trade_items,
|
|
||||||
status: TradeStatus::Unconfirmed
|
|
||||||
});
|
|
||||||
|
|
||||||
Ok(Box::new(vec![(trade_partner.client, SendShipPacket::AcknowledgeTrade(AcknowledgeTrade {}))].into_iter()))
|
let item_blobs = items_to_trade.items.iter().take(items_to_trade.count as usize);
|
||||||
|
let trade_items = item_blobs
|
||||||
|
.map(|item| {
|
||||||
|
// TOOD: meseta?
|
||||||
|
let real_item = inventory.get_item_by_id(ClientItemId(item.item_id))
|
||||||
|
.ok_or(ItemManagerError::NoSuchItemId(ClientItemId(item.item_id)))?;
|
||||||
|
let trade_item_bytes: [u8; 16] = item.item_data.iter()
|
||||||
|
.chain(item.item_data2.iter())
|
||||||
|
.cloned().collect::<Vec<u8>>()
|
||||||
|
.try_into()
|
||||||
|
.unwrap();
|
||||||
|
match real_item {
|
||||||
|
InventoryItem::Individual(individual_inventory_item) => {
|
||||||
|
println!("real indiv item: {:?} {:?} ==? {:?}", real_item, real_item.as_client_bytes(), trade_item_bytes);
|
||||||
|
if real_item.as_client_bytes() == trade_item_bytes {
|
||||||
|
Ok(TradeItem::Individual(individual_inventory_item.item_id))
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
Err(TradeError::ClientItemIdDidNotMatchItem(ClientItemId(item.item_id), trade_item_bytes).into())
|
||||||
|
}
|
||||||
|
},
|
||||||
|
InventoryItem::Stacked(stacked_inventory_item) => {
|
||||||
|
println!("real stack item: {:?} {:?} ==? {:?}", real_item, real_item.as_client_bytes(), trade_item_bytes);
|
||||||
|
if real_item.as_client_bytes()[0..4] == trade_item_bytes[0..4] {
|
||||||
|
let amount = trade_item_bytes[5] as usize;
|
||||||
|
if amount <= stacked_inventory_item.entity_ids.len() {
|
||||||
|
Ok(TradeItem::Stacked(stacked_inventory_item.item_id, amount))
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
Err(TradeError::InvalidStackAmount(stacked_inventory_item.item_id, amount).into())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
Err(TradeError::ClientItemIdDidNotMatchItem(ClientItemId(item.item_id), trade_item_bytes).into())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.collect::<Result<Vec<_>, anyhow::Error>>()?;
|
||||||
|
|
||||||
|
this.status = TradeStatus::ItemsChecked;
|
||||||
|
Ok(Box::new(std::iter::once((other.client(), SendShipPacket::AcknowledgeTrade(AcknowledgeTrade {})))))
|
||||||
|
})?
|
||||||
|
.unwrap_or_else(|err| {
|
||||||
|
println!("asdf {:?}", 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 {})))))
|
||||||
|
}))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn items_to_trade<EG>(id: ClientId,
|
pub async fn items_to_trade<EG>(id: ClientId,
|
||||||
@ -99,28 +340,25 @@ pub async fn items_to_trade<EG>(id: ClientId,
|
|||||||
entity_gateway: &mut EG,
|
entity_gateway: &mut EG,
|
||||||
client_location: &ClientLocation,
|
client_location: &ClientLocation,
|
||||||
clients: &mut Clients,
|
clients: &mut Clients,
|
||||||
item_manager: &mut ItemManager)
|
item_manager: &mut ItemManager,
|
||||||
|
trades: &mut TradeState)
|
||||||
-> Result<Box<dyn Iterator<Item = (ClientId, SendShipPacket)> + Send>, anyhow::Error>
|
-> Result<Box<dyn Iterator<Item = (ClientId, SendShipPacket)> + Send>, anyhow::Error>
|
||||||
where
|
where
|
||||||
EG: EntityGateway
|
EG: EntityGateway
|
||||||
{
|
{
|
||||||
let t = inner_items_to_trade(id, items_to_trade_pkt, entity_gateway, client_location, clients, item_manager).await;
|
let t = inner_items_to_trade(id, items_to_trade_pkt, entity_gateway, client_location, clients, item_manager, trades).await;
|
||||||
match t {
|
match t {
|
||||||
Ok(p) => Ok(p),
|
Ok(p) => Ok(p),
|
||||||
Err(err) => {
|
Err(err) => {
|
||||||
let client = clients.get(&id).ok_or(ShipError::ClientNotFound(id))?;
|
log::warn!("atrade error: {:?}", err);
|
||||||
let trade_partner = client.trade.as_ref()
|
println!("qwer");
|
||||||
.and_then(|trade_state| {
|
let (this, other) = trades.remove_trade(&id);
|
||||||
client_location.get_local_client(trade_state.other_client).ok()
|
Ok(Box::new(client_location.get_all_clients_by_client(id)?.into_iter()
|
||||||
})
|
.filter(move |client| other.as_ref().map(|other| client.client == other.client()).unwrap_or_else(|| false))
|
||||||
.map(|trade_partner| {
|
.map(move |client| {
|
||||||
(trade_partner.client, SendShipPacket::CancelTrade(CancelTrade {}))
|
(client.client, SendShipPacket::CancelTrade(CancelTrade {}))
|
||||||
});
|
})
|
||||||
|
.chain(std::iter::once((id, SendShipPacket::CancelTrade(CancelTrade {}))))))
|
||||||
log::warn!("error in trading: {:?}", err);
|
|
||||||
Ok(Box::new(vec![(id, SendShipPacket::CancelTrade(CancelTrade {}))]
|
|
||||||
.into_iter()
|
|
||||||
.chain(trade_partner.into_iter())))
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -129,83 +367,112 @@ pub async fn trade_confirmed<EG>(id: ClientId,
|
|||||||
entity_gateway: &mut EG,
|
entity_gateway: &mut EG,
|
||||||
client_location: &ClientLocation,
|
client_location: &ClientLocation,
|
||||||
clients: &mut Clients,
|
clients: &mut Clients,
|
||||||
item_manager: &mut ItemManager)
|
item_manager: &mut ItemManager,
|
||||||
|
trades: &mut TradeState)
|
||||||
-> Result<Box<dyn Iterator<Item = (ClientId, SendShipPacket)> + Send>, anyhow::Error>
|
-> Result<Box<dyn Iterator<Item = (ClientId, SendShipPacket)> + Send>, anyhow::Error>
|
||||||
where
|
where
|
||||||
EG: EntityGateway
|
EG: EntityGateway
|
||||||
{
|
{
|
||||||
{
|
enum TradeReady<'a> {
|
||||||
let this_client = clients.get_mut(&id).ok_or(ShipError::ClientNotFound(id))?;
|
OnePlayer,
|
||||||
this_client.trade.as_mut().ok_or(TradeError::NotInTradeMenu)?.status = TradeStatus::Confirmed;
|
BothPlayers(crate::ship::location::RoomId,
|
||||||
|
(crate::ship::location::AreaClient, &'a crate::ship::ship::ClientState, crate::ship::trade::ClientTradeState),
|
||||||
|
(crate::ship::location::AreaClient, &'a crate::ship::ship::ClientState, crate::ship::trade::ClientTradeState)),
|
||||||
}
|
}
|
||||||
|
|
||||||
let both_confirmed = {
|
let trade_instructions = trades
|
||||||
let this_client = clients.get(&id).ok_or(ShipError::ClientNotFound(id))?;
|
.with(&id, |this, other| -> Result<_, anyhow::Error> {
|
||||||
let other_client_id = this_client.trade.as_ref().ok_or(TradeError::NotInTradeMenu)?.other_client;
|
if status_is_not(&this.status, &[TradeStatus::ItemsChecked]) || status_is_not(&other.status, &[TradeStatus::ItemsChecked, TradeStatus::TradeComplete]) {
|
||||||
let other_client = clients.get(&other_client_id).ok_or(ShipError::ClientNotFound(other_client_id))?;
|
return Err(TradeError::MismatchedStatus.into())
|
||||||
|
}
|
||||||
|
|
||||||
let this_client_trade = this_client.trade.as_ref().ok_or(TradeError::NotInTradeMenu)?;
|
// TODO: check for space in inventory!
|
||||||
let other_client_trade = other_client.trade.as_ref().ok_or(TradeError::NotInTradeMenu)?;
|
this.status = TradeStatus::TradeComplete;
|
||||||
this_client_trade.status == TradeStatus::Confirmed && other_client_trade.status == TradeStatus::Confirmed
|
|
||||||
};
|
|
||||||
|
|
||||||
if both_confirmed {
|
if this.status == TradeStatus::TradeComplete && other.status == TradeStatus::TradeComplete {
|
||||||
let this_client_trade = {
|
let this_client = clients.get(&this.client()).ok_or(ShipError::ClientNotFound(this.client()))?;
|
||||||
let this_client = clients.get_mut(&id).ok_or(ShipError::ClientNotFound(id))?;
|
let other_client = clients.get(&other.client()).ok_or(ShipError::ClientNotFound(other.client()))?;
|
||||||
let this_client_trade = this_client.trade.as_ref().ok_or(TradeError::NotInTradeMenu)?.clone();
|
let this_local_client = client_location.get_local_client(this.client())?;
|
||||||
this_client.trade = None;
|
let other_local_client = client_location.get_local_client(other.client())?;
|
||||||
this_client_trade
|
let room_id = client_location.get_room(id).map_err(|err| -> ClientLocationError { err.into() })?;
|
||||||
};
|
|
||||||
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))?;
|
|
||||||
|
|
||||||
let this_local_client = client_location.get_local_client(id)?;
|
Ok(TradeReady::BothPlayers(room_id,
|
||||||
let other_local_client = client_location.get_local_client(this_client_trade.other_client)?;
|
(this_local_client, this_client, this.clone()),
|
||||||
|
(other_local_client, other_client, other.clone())))
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
Ok(TradeReady::OnePlayer)
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
let traded_items = item_manager.trade_items(
|
// TODO: this match needs to handle errors better
|
||||||
entity_gateway,
|
match trade_instructions {
|
||||||
(&this_local_client, &this_client.character, &this_client_trade.items),
|
Ok(Ok(trade)) => {
|
||||||
(&other_local_client, &other_client.character, &other_client_trade.items)
|
match trade {
|
||||||
).await?;
|
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 traded_items = item_manager.trade_items(entity_gateway,
|
||||||
|
room_id,
|
||||||
|
(&this_local_client, &this_client.character, &this.items),
|
||||||
|
(&other_local_client, &other_client.character, &other.items)).await?;
|
||||||
|
|
||||||
let clients_in_room = client_location.get_all_clients_by_client(id)?;
|
let clients_in_room = client_location.get_all_clients_by_client(id)?;
|
||||||
let traded_item_packets = traded_items
|
let traded_item_packets = traded_items
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.map(|item| {
|
.map(|item| {
|
||||||
match item.item_detail {
|
match item.item_detail {
|
||||||
ItemToTradeDetail::Individual(item_detail) => {
|
ItemToTradeDetail::Individual(item_detail) => {
|
||||||
[
|
[
|
||||||
GameMessage::CreateItem(builder::message::create_individual_item(item.add_to, item.item_id, &item_detail).unwrap()),
|
GameMessage::CreateItem(builder::message::create_individual_item(item.add_to, item.new_item_id, &item_detail).unwrap()),
|
||||||
GameMessage::PlayerNoLongerHasItem(builder::message::player_no_longer_has_item(item.remove_from, item.item_id, 1)) // TODO: amount = ?
|
GameMessage::PlayerNoLongerHasItem(builder::message::player_no_longer_has_item(item.remove_from, item.current_item_id, 1)) // TODO: amount = ?
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
ItemToTradeDetail::Stacked(tool, amount) => {
|
ItemToTradeDetail::Stacked(tool, amount) => {
|
||||||
[
|
[
|
||||||
GameMessage::CreateItem(builder::message::create_stacked_item(item.add_to, item.item_id, &tool, amount).unwrap()),
|
GameMessage::CreateItem(builder::message::create_stacked_item(item.add_to, item.new_item_id, &tool, amount).unwrap()),
|
||||||
GameMessage::PlayerNoLongerHasItem(builder::message::player_no_longer_has_item(item.remove_from, item.item_id, amount as u32))
|
GameMessage::PlayerNoLongerHasItem(builder::message::player_no_longer_has_item(item.remove_from, item.current_item_id, amount as u32))
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.flatten()
|
||||||
|
.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()))))
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
||||||
|
.flatten();
|
||||||
|
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)))
|
||||||
}
|
}
|
||||||
})
|
}
|
||||||
.flatten()
|
},
|
||||||
.map(move |packet| {
|
err @ _ => {
|
||||||
clients_in_room
|
let (this, other) = trades.remove_trade(&id);
|
||||||
.clone()
|
Ok(Box::new(client_location.get_all_clients_by_client(id).unwrap().into_iter()
|
||||||
.into_iter()
|
.filter(move |client| other.as_ref().map(|other| client.client == other.client()).unwrap_or_else(|| false))
|
||||||
.map(move |client| {
|
.map(move |client| {
|
||||||
(client.client, SendShipPacket::Message(Message::new(packet.clone())))
|
(client.client, SendShipPacket::CancelTrade(CancelTrade {}))
|
||||||
})
|
})
|
||||||
})
|
.chain(std::iter::once((id, SendShipPacket::CancelTrade(CancelTrade {}))))))
|
||||||
.flatten();
|
}
|
||||||
Ok(Box::new(traded_item_packets))
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
Ok(Box::new(None.into_iter()))
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -33,6 +33,7 @@ use crate::ship::quests;
|
|||||||
use crate::ship::map::{MapsError, MapAreaError, MapArea};
|
use crate::ship::map::{MapsError, MapAreaError, MapArea};
|
||||||
use crate::ship::packet::handler;
|
use crate::ship::packet::handler;
|
||||||
use crate::ship::shops::{WeaponShop, ToolShop, ArmorShop, WeaponShopItem, ToolShopItem, ArmorShopItem};
|
use crate::ship::shops::{WeaponShop, ToolShop, ArmorShop, WeaponShopItem, ToolShopItem, ArmorShopItem};
|
||||||
|
use crate::ship::trade::TradeState;
|
||||||
|
|
||||||
pub const SHIP_PORT: u16 = 23423;
|
pub const SHIP_PORT: u16 = 23423;
|
||||||
pub const QUEST_CATEGORY_MENU_ID: u32 = 0xA2;
|
pub const QUEST_CATEGORY_MENU_ID: u32 = 0xA2;
|
||||||
@ -41,7 +42,7 @@ pub type Rooms = [Option<room::RoomState>; MAX_ROOMS];
|
|||||||
pub type Clients = HashMap<ClientId, ClientState>;
|
pub type Clients = HashMap<ClientId, ClientState>;
|
||||||
|
|
||||||
#[derive(Error, Debug)]
|
#[derive(Error, Debug)]
|
||||||
#[error("")]
|
#[error("shiperror")]
|
||||||
pub enum ShipError {
|
pub enum ShipError {
|
||||||
ClientNotFound(ClientId),
|
ClientNotFound(ClientId),
|
||||||
NoCharacterInSlot(ClientId, u32),
|
NoCharacterInSlot(ClientId, u32),
|
||||||
@ -73,6 +74,7 @@ pub enum ShipError {
|
|||||||
InvalidShip(usize),
|
InvalidShip(usize),
|
||||||
InvalidBlock(usize),
|
InvalidBlock(usize),
|
||||||
InvalidItem(items::ClientItemId),
|
InvalidItem(items::ClientItemId),
|
||||||
|
TradeError(#[from] crate::ship::packet::handler::trade::TradeError),
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
@ -190,6 +192,7 @@ pub enum SendShipPacket {
|
|||||||
RedirectClient(RedirectClient),
|
RedirectClient(RedirectClient),
|
||||||
AcknowledgeTrade(AcknowledgeTrade),
|
AcknowledgeTrade(AcknowledgeTrade),
|
||||||
CancelTrade(CancelTrade),
|
CancelTrade(CancelTrade),
|
||||||
|
TradeSuccessful(TradeSuccessful),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl SendServerPacket for SendShipPacket {
|
impl SendServerPacket for SendShipPacket {
|
||||||
@ -229,6 +232,7 @@ impl SendServerPacket for SendShipPacket {
|
|||||||
SendShipPacket::RedirectClient(pkt) => pkt.as_bytes(),
|
SendShipPacket::RedirectClient(pkt) => pkt.as_bytes(),
|
||||||
SendShipPacket::AcknowledgeTrade(pkt) => pkt.as_bytes(),
|
SendShipPacket::AcknowledgeTrade(pkt) => pkt.as_bytes(),
|
||||||
SendShipPacket::CancelTrade(pkt) => pkt.as_bytes(),
|
SendShipPacket::CancelTrade(pkt) => pkt.as_bytes(),
|
||||||
|
SendShipPacket::TradeSuccessful(pkt) => pkt.as_bytes(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -248,25 +252,10 @@ pub struct LoadingQuest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
#[derive(Clone)]
|
|
||||||
pub enum TradeItem {
|
|
||||||
Individual(items::ClientItemId),
|
|
||||||
Stacked(items::ClientItemId, usize),
|
|
||||||
Meseta(usize),
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone, Eq, PartialEq)]
|
|
||||||
pub enum TradeStatus {
|
|
||||||
Confirmed,
|
|
||||||
Unconfirmed,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone)]
|
|
||||||
pub struct TradeState {
|
|
||||||
pub other_client: ClientId,
|
|
||||||
pub items: Vec<TradeItem>,
|
|
||||||
pub status: TradeStatus,
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct ClientState {
|
pub struct ClientState {
|
||||||
pub user: UserAccountEntity,
|
pub user: UserAccountEntity,
|
||||||
@ -286,7 +275,6 @@ pub struct ClientState {
|
|||||||
pub tool_shop: Vec<ToolShopItem>,
|
pub tool_shop: Vec<ToolShopItem>,
|
||||||
pub armor_shop: Vec<ArmorShopItem>,
|
pub armor_shop: Vec<ArmorShopItem>,
|
||||||
pub tek: Option<(items::ClientItemId, item::weapon::TekSpecialModifier, item::weapon::TekPercentModifier, i32)>,
|
pub tek: Option<(items::ClientItemId, item::weapon::TekSpecialModifier, item::weapon::TekPercentModifier, i32)>,
|
||||||
pub trade: Option<TradeState>,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ClientState {
|
impl ClientState {
|
||||||
@ -307,7 +295,6 @@ impl ClientState {
|
|||||||
tool_shop: Vec::new(),
|
tool_shop: Vec::new(),
|
||||||
armor_shop: Vec::new(),
|
armor_shop: Vec::new(),
|
||||||
tek: None,
|
tek: None,
|
||||||
trade: None,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -411,6 +398,7 @@ impl<EG: EntityGateway> ShipServerStateBuilder<EG> {
|
|||||||
auth_token: self.auth_token.unwrap_or_else(|| AuthToken("".into())),
|
auth_token: self.auth_token.unwrap_or_else(|| AuthToken("".into())),
|
||||||
ship_list: Vec::new(),
|
ship_list: Vec::new(),
|
||||||
shipgate_sender: None,
|
shipgate_sender: None,
|
||||||
|
trades: Default::default(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -457,6 +445,7 @@ pub struct ShipServerState<EG: EntityGateway> {
|
|||||||
auth_token: AuthToken,
|
auth_token: AuthToken,
|
||||||
ship_list: Vec<Ship>,
|
ship_list: Vec<Ship>,
|
||||||
shipgate_sender: Option<Box<dyn Fn(ShipMessage) + Send + Sync>>,
|
shipgate_sender: Option<Box<dyn Fn(ShipMessage) + Send + Sync>>,
|
||||||
|
trades: TradeState,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<EG: EntityGateway> ShipServerState<EG> {
|
impl<EG: EntityGateway> ShipServerState<EG> {
|
||||||
@ -561,6 +550,9 @@ impl<EG: EntityGateway> ShipServerState<EG> {
|
|||||||
GameMessage::TekAccept(tek_accept) => {
|
GameMessage::TekAccept(tek_accept) => {
|
||||||
handler::direct_message::accept_tek_item(id, tek_accept, &mut self.entity_gateway, &block.client_location, &mut self.clients, &mut self.item_manager).await?
|
handler::direct_message::accept_tek_item(id, tek_accept, &mut self.entity_gateway, &block.client_location, &mut self.clients, &mut self.item_manager).await?
|
||||||
},
|
},
|
||||||
|
GameMessage::TradeRequest(trade_request) => {
|
||||||
|
handler::trade::trade_request(id, trade_request, target, &mut self.entity_gateway, &block.client_location, &mut self.clients, &mut self.item_manager, &mut self.trades).await?
|
||||||
|
},
|
||||||
_ => {
|
_ => {
|
||||||
let cmsg = msg.clone();
|
let cmsg = msg.clone();
|
||||||
Box::new(block.client_location.get_all_clients_by_client(id).unwrap().into_iter()
|
Box::new(block.client_location.get_all_clients_by_client(id).unwrap().into_iter()
|
||||||
@ -729,13 +721,12 @@ impl<EG: EntityGateway> ServerState for ShipServerState<EG> {
|
|||||||
handler::ship::block_list(id, &self.name, self.blocks.0.len())
|
handler::ship::block_list(id, &self.name, self.blocks.0.len())
|
||||||
},
|
},
|
||||||
RecvShipPacket::ItemsToTrade(items_to_trade) => {
|
RecvShipPacket::ItemsToTrade(items_to_trade) => {
|
||||||
log::warn!("trade! {:?} {:?}", id, items_to_trade);
|
|
||||||
let block = self.blocks.with_client(id, &self.clients)?;
|
let block = self.blocks.with_client(id, &self.clients)?;
|
||||||
handler::trade::items_to_trade(id, items_to_trade, &mut self.entity_gateway, &block.client_location, &mut self.clients, &mut self.item_manager).await?
|
handler::trade::items_to_trade(id, items_to_trade, &mut self.entity_gateway, &block.client_location, &mut self.clients, &mut self.item_manager, &mut self.trades).await?
|
||||||
},
|
},
|
||||||
RecvShipPacket::TradeConfirmed(_) => {
|
RecvShipPacket::TradeConfirmed(_) => {
|
||||||
let block = self.blocks.with_client(id, &self.clients)?;
|
let block = self.blocks.with_client(id, &self.clients)?;
|
||||||
handler::trade::trade_confirmed(id, &mut self.entity_gateway, &block.client_location, &mut self.clients, &mut self.item_manager).await?
|
handler::trade::trade_confirmed(id, &mut self.entity_gateway, &block.client_location, &mut self.clients, &mut self.item_manager, &mut self.trades).await?
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
172
src/ship/trade.rs
Normal file
172
src/ship/trade.rs
Normal file
@ -0,0 +1,172 @@
|
|||||||
|
use std::collections::HashMap;
|
||||||
|
use std::cell::RefCell;
|
||||||
|
use async_std::sync::{Arc, Mutex};
|
||||||
|
|
||||||
|
use crate::common::serverstate::ClientId;
|
||||||
|
use crate::ship::items;
|
||||||
|
|
||||||
|
pub const MESETA_ITEM_ID: items::ClientItemId = items::ClientItemId(0xFFFFFF01);
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub enum TradeItem {
|
||||||
|
Individual(items::ClientItemId),
|
||||||
|
Stacked(items::ClientItemId, usize),
|
||||||
|
Meseta(usize),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TradeItem {
|
||||||
|
pub fn stacked(&self) -> Option<(items::ClientItemId, usize)> {
|
||||||
|
match self {
|
||||||
|
TradeItem::Stacked(item_id, amount) => Some((*item_id, *amount as usize)),
|
||||||
|
_ => None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn item_id(&self) -> items::ClientItemId {
|
||||||
|
match self {
|
||||||
|
TradeItem::Individual(item_id) => *item_id,
|
||||||
|
TradeItem::Stacked(item_id, _) => *item_id,
|
||||||
|
TradeItem::Meseta(_) => MESETA_ITEM_ID,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#[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 status: TradeStatus,
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
impl ClientTradeState {
|
||||||
|
pub fn client(&self) -> ClientId {
|
||||||
|
self.client
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn other_client(&self) -> ClientId {
|
||||||
|
self.other_client
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
pub struct ClientTradeHandle<'a> {
|
||||||
|
handle: std::cell::Ref<'a, ClientTradeState>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> ClientTradeHandle<'a> {
|
||||||
|
fn new(handle: std::cell::Ref<'a, ClientTradeState>) -> ClientTradeHandle<'a> {
|
||||||
|
ClientTradeHandle {
|
||||||
|
handle: handle,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> std::ops::Deref for ClientTradeHandle<'a> {
|
||||||
|
type Target = ClientTradeState;
|
||||||
|
fn deref(&self) -> &ClientTradeState {
|
||||||
|
&self.handle
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> std::ops::DerefMut for ClientTradeHandle<'a> {
|
||||||
|
fn deref_mut(&mut self) -> &mut ClientTradeState {
|
||||||
|
&mut self.handle
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> Clone for ClientTradeHandle<'a> {
|
||||||
|
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
#[derive(thiserror::Error, Debug)]
|
||||||
|
#[error("")]
|
||||||
|
pub enum TradeStateError {
|
||||||
|
ClientNotInTrade(ClientId),
|
||||||
|
MismatchedTrade(ClientId, ClientId),
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Default)]
|
||||||
|
pub struct TradeState {
|
||||||
|
trades: HashMap<ClientId, RefCell<ClientTradeState>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TradeState {
|
||||||
|
pub fn new_trade(&mut self, sender: &ClientId, receiver: &ClientId) {
|
||||||
|
let state = ClientTradeState {
|
||||||
|
client: *sender,
|
||||||
|
other_client: *receiver,
|
||||||
|
items: Default::default(),
|
||||||
|
status: TradeStatus::SentRequest,
|
||||||
|
};
|
||||||
|
self.trades.insert(*sender, RefCell::new(state));
|
||||||
|
|
||||||
|
let state = ClientTradeState {
|
||||||
|
client: *receiver,
|
||||||
|
other_client: *sender,
|
||||||
|
items: Default::default(),
|
||||||
|
status: TradeStatus::ReceivedRequest,
|
||||||
|
};
|
||||||
|
self.trades.insert(*receiver, RefCell::new(state));
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn with<T, F> (&self, client: &ClientId, func: F) -> Result<T, TradeStateError>
|
||||||
|
where
|
||||||
|
F: Fn(&mut ClientTradeState, &mut ClientTradeState) -> T
|
||||||
|
{
|
||||||
|
let mut c1 = self.trades.get(client).ok_or_else(|| TradeStateError::ClientNotInTrade(*client))?.borrow_mut();
|
||||||
|
let mut c2 = self.trades.get(&c1.other_client).ok_or_else(|| TradeStateError::ClientNotInTrade(c1.other_client))?.borrow_mut();
|
||||||
|
|
||||||
|
// sanity check
|
||||||
|
if c1.client != c2.other_client {
|
||||||
|
return Err(TradeStateError::MismatchedTrade(c1.client, c2.client));
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(func(&mut *c1, &mut *c2))
|
||||||
|
}
|
||||||
|
/*
|
||||||
|
pub fn with<T, F> (&self, client: &ClientId, func: F) -> Result<T, TradeStateError>
|
||||||
|
where
|
||||||
|
F: Fn(&mut ClientTradeHandle, &mut ClientTradeHandle) -> T
|
||||||
|
{
|
||||||
|
let c1 = ClientTradeHandle::new(self.trades.get(client).ok_or_else(|| TradeStateError::ClientNotInTrade(*client))?.borrow_mut());
|
||||||
|
let c2 = ClientTradeHandle::new(self.trades.get(&c1.other_client).ok_or_else(|| TradeStateError::ClientNotInTrade(c1.other_client))?.borrow_mut());
|
||||||
|
|
||||||
|
// sanity check
|
||||||
|
if c1.client != c2.other_client {
|
||||||
|
return Err(TradeStateError::MismatchedTrade(c1.client, c2.client));
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(func(&mut c1, &mut c2))
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
|
// 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)
|
||||||
|
}
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user