Browse Source

TRADING JUST IN TIME TO BARELY MISS XMAS

pull/80/head
jake 3 years ago
parent
commit
81916d1f57
  1. 49
      src/ship/items/manager.rs
  2. 54
      src/ship/packet/handler/trade.rs
  3. 13
      src/ship/trade.rs
  4. 4271
      tests/test_trade.rs

49
src/ship/items/manager.rs

@ -275,15 +275,6 @@ impl ItemManager {
)) ))
} }
/*pub fn get_character_bank_mut(&mut self, character: &CharacterEntity) -> Result<&CharacterBank, ItemManagerError> {
Ok(self.character_bank
.get_mut(&character.id)
.ok_or(ItemManagerError::NoCharacter(character.id))?
.entry(BankName("".to_string()))
.or_insert(CharacterBank::new(Vec::new())))
//.ok_or(ItemManagerError::InvalidBankName(BankName("".to_string())))?)
}*/
pub fn remove_character_from_room(&mut self, character: &CharacterEntity) { pub fn remove_character_from_room(&mut self, character: &CharacterEntity) {
self.character_inventory.remove(&character.id); self.character_inventory.remove(&character.id);
self.character_floor.remove(&character.id); self.character_floor.remove(&character.id);
@ -986,14 +977,41 @@ impl ItemManager {
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)?;
[(p2_inventory, p1_inventory, p2.2), (p1_inventory, p2_inventory, p1.2)].iter()
.map(|(src_inventory, dest_inventory, to_trade)| {
to_trade
[(p2_inventory, p1_inventory, p2.2, p1.2), (p1_inventory, p2_inventory, p1.2, p2.2)].iter()
.map(|(src_inventory, dest_inventory, trade_recv, trade_send)| {
let item_slots_lost_to_trade = trade_send
.iter()
.fold(0, |acc, item| {
match item {
TradeItem::Individual(..) => {
acc + 1
},
TradeItem::Stacked(item_id, amount) => {
let stacked_inventory_item = try {
src_inventory
.get_item_by_id(*item_id)?
.stacked()
};
if let Some(Some(item)) = stacked_inventory_item {
if item.count() == *amount {
acc + 1
}
else {
acc
}
}
else {
acc
}
}
}
});
trade_recv
.iter() .iter()
.try_fold(dest_inventory.count(), |acc, item| { .try_fold(dest_inventory.count(), |acc, item| {
match item { match item {
TradeItem::Individual(..) => { TradeItem::Individual(..) => {
if acc >= 30 {
if acc >= (30 + item_slots_lost_to_trade) {
Err(TradeError::NoInventorySpace) Err(TradeError::NoInventorySpace)
} }
else { else {
@ -1017,7 +1035,12 @@ impl ItemManager {
Err(TradeError::NoStackSpace) Err(TradeError::NoStackSpace)
}, },
SpaceForStack::No(NoThereIsNotSpace::FullInventory) => { SpaceForStack::No(NoThereIsNotSpace::FullInventory) => {
if acc >= (30 + item_slots_lost_to_trade) {
Err(TradeError::NoInventorySpace) Err(TradeError::NoInventorySpace)
}
else {
Ok(acc + 1)
}
}, },
} }
} }

54
src/ship/packet/handler/trade.rs

@ -42,6 +42,14 @@ pub enum TradeError {
NoStackSpace, NoStackSpace,
#[error("invalid meseta amount")] #[error("invalid meseta amount")]
InvalidMeseta, InvalidMeseta,
#[error("tried starting a trade while in one already")]
ClientAlreadyInTrade,
#[error("tried starting a trade while with player already in a trade")]
OtherAlreadyInTrade,
#[error("tried to trade item not specified in trade request")]
SketchyTrade,
#[error("items in trade window and items attempted to trade do not match")]
MismatchedTradeItems,
} }
@ -63,6 +71,9 @@ where
TradeRequestCommand::Initialize(ref act, meseta) => { TradeRequestCommand::Initialize(ref act, meseta) => {
match act { match act {
TradeRequestInitializeCommand::Initialize => { TradeRequestInitializeCommand::Initialize => {
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)?
.into_iter() .into_iter()
.filter(|ac| { .filter(|ac| {
@ -70,6 +81,9 @@ where
}) })
.next() .next()
.ok_or(TradeError::CouldNotFindTradePartner)?; .ok_or(TradeError::CouldNotFindTradePartner)?;
if trades.in_trade(&trade_partner.client) {
return Err(TradeError::OtherAlreadyInTrade.into())
}
trades.new_trade(&id, &trade_partner.client); 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)?.into_iter()
.filter(move |client| client.local_client.id() == target as u8) .filter(move |client| client.local_client.id() == target as u8)
@ -180,7 +194,7 @@ where
match this.items[trade_item_index].stacked()?.1.cmp(&(amount as usize)) { match this.items[trade_item_index].stacked()?.1.cmp(&(amount as usize)) {
std::cmp::Ordering::Greater => { std::cmp::Ordering::Greater => {
this.items[trade_item_index].stacked()?.1 -= amount as usize;
*this.items[trade_item_index].stacked_mut()?.1 -= amount as usize;
}, },
std::cmp::Ordering::Equal => { std::cmp::Ordering::Equal => {
this.items.remove(trade_item_index); this.items.remove(trade_item_index);
@ -267,6 +281,15 @@ where
.chain(std::iter::once((id, SendShipPacket::CancelTrade(CancelTrade {}))))) .chain(std::iter::once((id, SendShipPacket::CancelTrade(CancelTrade {})))))
})) }))
}, },
TradeRequestCommand::Cancel => {
trades.remove_trade(&id);
Ok(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 {}))))))
}
} }
} }
@ -297,17 +320,31 @@ where
} }
let client = clients.get(&this.client()).ok_or(ShipError::ClientNotFound(this.client()))?; let client = clients.get(&this.client()).ok_or(ShipError::ClientNotFound(this.client()))?;
let other_client = clients.get(&other.client()).ok_or(ShipError::ClientNotFound(other.client()))?;
let inventory = item_manager.get_character_inventory(&client.character)?; let inventory = item_manager.get_character_inventory(&client.character)?;
let item_blobs = items_to_trade.items.iter().take(items_to_trade.count as usize);
item_blobs
if items_to_trade.count as usize != (this.items.len() + (if this.meseta != 0 { 1 } else { 0 })) {
return Err(TradeError::MismatchedTradeItems.into())
}
items_to_trade.items
.iter()
.take(items_to_trade.count as usize)
.map(|item| { .map(|item| {
if ClientItemId(item.item_id) == OTHER_MESETA_ITEM_ID { if ClientItemId(item.item_id) == OTHER_MESETA_ITEM_ID {
if item.item_data[0] != 4 { if item.item_data[0] != 4 {
return Err(TradeError::InvalidItemId(ClientItemId(item.item_id)).into()) return Err(TradeError::InvalidItemId(ClientItemId(item.item_id)).into())
} }
let amount = u32::from_le_bytes(item.item_data2); let amount = u32::from_le_bytes(item.item_data2);
if amount > client.character.meseta {
let character_meseta = item_manager.get_character_meseta(&client.character.id).map_err(|_| TradeError::InvalidMeseta)?;
let other_character_meseta = item_manager.get_character_meseta(&other_client.character.id).map_err(|_| TradeError::InvalidMeseta)?;
if amount > character_meseta.0 {
return Err(TradeError::InvalidMeseta.into())
}
if (amount + other_character_meseta.0) > 999999 {
return Err(TradeError::InvalidMeseta.into())
}
if amount != this.meseta as u32{
return Err(TradeError::InvalidMeseta.into()) return Err(TradeError::InvalidMeseta.into())
} }
Ok(()) Ok(())
@ -315,6 +352,10 @@ where
else { else {
let real_item = inventory.get_item_by_id(ClientItemId(item.item_id)) let real_item = inventory.get_item_by_id(ClientItemId(item.item_id))
.ok_or(ItemManagerError::NoSuchItemId(ClientItemId(item.item_id)))?; .ok_or(ItemManagerError::NoSuchItemId(ClientItemId(item.item_id)))?;
let real_trade_item = this.items
.iter()
.find(|i| i.item_id() == ClientItemId(item.item_id))
.ok_or_else(|| TradeError::SketchyTrade)?;
let trade_item_bytes: [u8; 16] = item.item_data.iter() let trade_item_bytes: [u8; 16] = item.item_data.iter()
.chain(item.item_data2.iter()) .chain(item.item_data2.iter())
.cloned().collect::<Vec<u8>>() .cloned().collect::<Vec<u8>>()
@ -333,12 +374,17 @@ where
if real_item.as_client_bytes()[0..4] == trade_item_bytes[0..4] { if real_item.as_client_bytes()[0..4] == trade_item_bytes[0..4] {
let amount = trade_item_bytes[5] as usize; let amount = trade_item_bytes[5] as usize;
if amount <= stacked_inventory_item.entity_ids.len() { if amount <= stacked_inventory_item.entity_ids.len() {
if real_trade_item.stacked().ok_or_else(|| TradeError::SketchyTrade)?.1 == amount {
Ok(()) Ok(())
} }
else { else {
Err(TradeError::InvalidStackAmount(stacked_inventory_item.item_id, amount).into()) Err(TradeError::InvalidStackAmount(stacked_inventory_item.item_id, amount).into())
} }
} }
else {
Err(TradeError::InvalidStackAmount(stacked_inventory_item.item_id, amount).into())
}
}
else { else {
Err(TradeError::ClientItemIdDidNotMatchItem(ClientItemId(item.item_id), trade_item_bytes).into()) Err(TradeError::ClientItemIdDidNotMatchItem(ClientItemId(item.item_id), trade_item_bytes).into())
} }

13
src/ship/trade.rs

@ -14,7 +14,14 @@ pub enum TradeItem {
impl TradeItem { impl TradeItem {
pub fn stacked(&self) -> Option<(items::ClientItemId, usize)> { pub fn stacked(&self) -> Option<(items::ClientItemId, usize)> {
match self { match self {
TradeItem::Stacked(item_id, amount) => Some((*item_id, *amount as usize)),
TradeItem::Stacked(item_id, amount) => Some((*item_id, *amount)),
_ => None
}
}
pub fn stacked_mut(&mut self) -> Option<(items::ClientItemId, &mut usize)> {
match self {
TradeItem::Stacked(item_id, ref mut amount) => Some((*item_id, amount)),
_ => None _ => None
} }
} }
@ -125,6 +132,10 @@ impl TradeState {
self.trades.insert(*receiver, RefCell::new(state)); self.trades.insert(*receiver, RefCell::new(state));
} }
pub fn in_trade(&self, client: &ClientId) -> bool {
self.trades.contains_key(client)
}
pub fn with<T, F> (&self, client: &ClientId, func: F) -> Result<T, TradeStateError> pub fn with<T, F> (&self, client: &ClientId, func: F) -> Result<T, TradeStateError>
where where
F: Fn(&mut ClientTradeState, &mut ClientTradeState) -> T F: Fn(&mut ClientTradeState, &mut ClientTradeState) -> T

4271
tests/test_trade.rs
File diff suppressed because it is too large
View File

Loading…
Cancel
Save