Browse Source

basic item use infra, with jackolanterns

pull/124/head
jake 2 years ago
parent
commit
dbf73acb8d
  1. 7
      src/entity/item/mod.rs
  2. 144
      src/ship/items/actions.rs
  3. 51
      src/ship/items/apply_item.rs
  4. 2
      src/ship/items/inventory.rs
  5. 2
      src/ship/items/state.rs
  6. 14
      src/ship/items/tasks.rs
  7. 22
      src/ship/packet/handler/message.rs
  8. 53
      tests/test_item_use.rs
  9. 32
      tests/test_trade.rs

7
src/entity/item/mod.rs

@ -241,6 +241,13 @@ impl InventoryItemEntity {
_ => None,
}
}
pub fn stacked(&self) -> Option<&Vec<ItemEntity>> {
match self {
InventoryItemEntity::Stacked(i) => Some(i),
_ => None,
}
}
}
#[derive(Clone, Debug, Default)]

144
src/ship/items/actions.rs

@ -1,10 +1,14 @@
// TODO: replace various u32s and usizes denoting item amounts for ItemAmount(u32) for consistency
use crate::ship::items::ClientItemId;
use crate::entity::item::{Meseta, ItemNote};
use async_std::sync::Arc;
use std::future::Future;
use std::pin::Pin;
use std::iter::IntoIterator;
use libpso::packet::{ship::Message, messages::GameMessage};
use crate::ship::map::MapArea;
use crate::ship::ship::SendShipPacket;
use crate::entity::character::{CharacterEntity, CharacterEntityId};
use crate::entity::gateway::{EntityGateway, EntityGatewayTransaction};
use crate::ship::items::state::{ItemStateProxy, ItemStateError, AddItemResult, StackedItemDetail, IndividualItemDetail};
@ -17,6 +21,8 @@ use crate::entity::item::tool::Tool;
use crate::entity::item::ItemModifier;
use crate::ship::shops::ShopItem;
use crate::ship::drops::{ItemDrop, ItemDropType};
use crate::ship::packet::builder;
use crate::ship::location::AreaClient;
type BoxFuture<T> = Pin<Box<dyn Future<Output=T> + Send>>;
@ -462,13 +468,14 @@ where
pub(super) fn use_consumed_item<EG, TR>(
character: CharacterEntity,
character: &CharacterEntity,
) -> impl Fn((ItemStateProxy, TR), InventoryItem)
-> BoxFuture<Result<((ItemStateProxy, TR), Vec<ApplyItemAction>), ItemStateError>>
where
EG: EntityGateway + Clone + 'static,
TR: EntityGatewayTransaction<ParentGateway = EG> + 'static,
{
let character = character.clone();
move |(mut item_state, transaction), inventory_item| {
let mut character = character.clone();
Box::pin(async move {
@ -690,22 +697,22 @@ where
#[async_recursion::async_recursion]
async fn foreach_inner<'a, EG, TR, O, T, F>(
async fn foreach_inner<'a, EG, TR, O, T, F, I>(
state: (ItemStateProxy, TR),
mut input: Vec<T>,
func: F,
mut input: I,
func: Arc<F>,
) -> Result<((ItemStateProxy, TR), Vec<O>), ItemStateError>
where
'a: 'async_recursion,
EG: EntityGateway,
TR: EntityGatewayTransaction<ParentGateway = EG> + 'static,
O: Send,
T: Clone + Send,
T: Send,
F: Fn((ItemStateProxy, TR), T)
-> BoxFuture<Result<((ItemStateProxy, TR), O), ItemStateError>> + Send + Sync,
F: Clone,
I: Iterator<Item = T> + Send + Sync + 'static,
{
let item = match input.pop() {
let item = match input.next() {
Some(item) => item,
None => return Ok((state, Vec::new()))
};
@ -718,9 +725,9 @@ where
Ok((state, output))
}
pub(super) fn foreach<EG, TR, O, T, F>(
pub(super) fn foreach<EG, TR, O, T, F, I>(
func: F
) -> impl Fn((ItemStateProxy, TR), Vec<T>)
) -> impl Fn((ItemStateProxy, TR), I)
-> BoxFuture<Result<((ItemStateProxy, TR), Vec<O>), ItemStateError>>
where
EG: EntityGateway,
@ -729,11 +736,14 @@ where
T: Send + Clone + 'static + std::fmt::Debug,
F: Fn((ItemStateProxy, TR), T)
-> BoxFuture<Result<((ItemStateProxy, TR), O), ItemStateError>> + Send + Sync + 'static,
F: Clone,
T: Clone + Send + Sync,
T: Send + Sync,
I: IntoIterator<Item = T> + Send + Sync + 'static,
I::IntoIter: Send + Sync,
{
let func = Arc::new(func);
move |(item_state, transaction), items| {
let func = func.clone();
let items = items.into_iter();
Box::pin(async move {
let (state, result) = foreach_inner((item_state, transaction), items, func).await?;
Ok((state, result))
@ -758,6 +768,35 @@ where
}
}
pub(super) fn fork<EG, TR, F1, F2, T, O1, O2>(
func1: F1,
func2: F2,
) -> impl Fn((ItemStateProxy, TR), T)
-> BoxFuture<Result<((ItemStateProxy, TR), (O1, O2)), ItemStateError>>
where
EG: EntityGateway,
TR: EntityGatewayTransaction<ParentGateway = EG> + 'static,
F1: Fn((ItemStateProxy, TR), T) -> BoxFuture<Result<((ItemStateProxy, TR), O1), ItemStateError>> + Send + Sync + 'static,
F2: Fn((ItemStateProxy, TR), T) -> BoxFuture<Result<((ItemStateProxy, TR), O2), ItemStateError>> + Send + Sync + 'static,
T: Send + Sync + Clone + 'static,
O1: Send,
O2: Send,
{
let func1 = Arc::new(func1);
let func2 = Arc::new(func2);
move |(item_state, transaction), input| {
let input = input.clone();
let func1 = func1.clone();
let func2 = func2.clone();
Box::pin(async move {
let ((item_state, transaction), result1) = func1((item_state, transaction), input.clone()).await?;
let ((item_state, transaction), result2) = func2((item_state, transaction), input).await?;
Ok(((item_state, transaction), (result1, result2)))
})
}
}
pub(super) fn add_item_to_inventory<EG, TR>(
character: CharacterEntity,
) -> impl Fn((ItemStateProxy, TR), InventoryItem)
@ -994,3 +1033,86 @@ where
})
}
}
pub(super) fn apply_item_action_packets<EG, TR>(
character_id: CharacterEntityId,
area_client: AreaClient,
) -> impl Fn((ItemStateProxy, TR), ApplyItemAction)
-> BoxFuture<Result<((ItemStateProxy, TR), Vec<SendShipPacket>), ItemStateError>>
where
EG: EntityGateway,
TR: EntityGatewayTransaction<ParentGateway = EG> + 'static,
{
move |(mut item_state, mut transaction), apply_item_action| {
Box::pin(async move {
let pkts = if let ApplyItemAction::CreateItem(item_detail) = apply_item_action {
let new_item = transaction.gateway().create_item(NewItemEntity {
item: item_detail.clone(),
}).await?;
let item_id = item_state.new_item_id().await?;
let (inventory_item_detail, create_item) = if item_detail.is_stackable() {
let tool = item_detail.as_tool().ok_or_else(|| ItemStateError::NotATool(ClientItemId(0xFFFFFFFF)))?;
let create_item = builder::message::create_stacked_item(area_client, item_id, &tool, 1).map_err(|_err| ItemStateError::Dummy)?;
let item_detail = StackedItemDetail {
entity_ids: vec![new_item.id],
tool
};
(InventoryItemDetail::Stacked(item_detail), create_item)
}
else {
let item_detail = IndividualItemDetail {
entity_id: new_item.id,
item: item_detail,
};
let create_item = builder::message::create_individual_item(area_client, item_id, &item_detail).map_err(|_err| ItemStateError::Dummy)?;
(InventoryItemDetail::Individual(item_detail), create_item)
};
let inventory_item = InventoryItem {
item_id,
item: inventory_item_detail,
};
let mut inventory = item_state.inventory(&character_id).await?;
inventory.add_item(inventory_item)?;
transaction.gateway().set_character_inventory(&character_id, &inventory.as_inventory_entity(&character_id)).await?;
item_state.set_inventory(inventory).await;
vec![SendShipPacket::Message(Message::new(GameMessage::CreateItem(create_item)))]
}
else {
Vec::new()
};
Ok(((item_state, transaction), pkts))
})
}
}
pub(super) fn apply_item_action_character<EG, TR>(
character: &CharacterEntity
) -> impl Fn((ItemStateProxy, TR), Vec<ApplyItemAction>)
-> BoxFuture<Result<((ItemStateProxy, TR), CharacterEntity), ItemStateError>>
where
EG: EntityGateway,
TR: EntityGatewayTransaction<ParentGateway = EG> + 'static,
{
let character = character.clone();
move |(item_state, transaction), apply_item_actions| {
let mut character = character.clone();
Box::pin(async move {
for action in apply_item_actions {
match action {
ApplyItemAction::UpdateCharacter(new_character) => character = *new_character,
_ => {}
}
}
Ok(((item_state, transaction), character))
})
}
}

51
src/ship/items/apply_item.rs

@ -2,10 +2,12 @@ use std::convert::TryInto;
use futures::future::{join_all, BoxFuture, LocalBoxFuture};
use futures::stream::{FuturesOrdered, StreamExt};
use thiserror::Error;
use rand::{Rng, SeedableRng};
use rand::distributions::{WeightedIndex, Distribution};
use crate::entity::gateway::{EntityGateway, GatewayError};
use crate::entity::character::CharacterEntity;
use crate::entity::item::mag::{MagCell, MagCellError};
use crate::entity::item::tool::ToolType;
use crate::entity::item::mag::{MagType, MagCell, MagCellError};
use crate::entity::item::tool::{Tool, ToolType};
use crate::entity::item::{ItemDetail, ItemEntityId};
use crate::ship::items::state::{ItemStateProxy, ItemStateError};
use crate::ship::items::inventory::{InventoryItem, InventoryItemDetail};
@ -29,9 +31,12 @@ pub enum ApplyItemError {
MagCellError(#[from] MagCellError),
}
#[derive(Debug, Clone)]
pub enum ApplyItemAction {
UpdateCharacter(CharacterEntity),
CreateItem(()),
UpdateCharacter(Box<CharacterEntity>),
CreateItem(ItemDetail),
//TransformItem,
//RemoveItem,
}
impl From<ItemStateError> for ApplyItemError {
@ -43,43 +48,43 @@ impl From<ItemStateError> for ApplyItemError {
async fn power_material<EG: EntityGateway + ?Sized>(entity_gateway: &mut EG, character: &mut CharacterEntity) -> Result<Vec<ApplyItemAction>, ApplyItemError> {
character.materials.power += 1;
entity_gateway.save_character(character).await?;
Ok(vec![ApplyItemAction::UpdateCharacter(character.clone())])
Ok(vec![ApplyItemAction::UpdateCharacter(Box::new(character.clone()))])
}
async fn mind_material<EG: EntityGateway + ?Sized>(entity_gateway: &mut EG, character: &mut CharacterEntity) -> Result<Vec<ApplyItemAction>, ApplyItemError> {
character.materials.mind += 1;
entity_gateway.save_character(character).await.unwrap();
Ok(vec![ApplyItemAction::UpdateCharacter(character.clone())])
Ok(vec![ApplyItemAction::UpdateCharacter(Box::new(character.clone()))])
}
async fn evade_material<EG: EntityGateway + ?Sized>(entity_gateway: &mut EG, character: &mut CharacterEntity) -> Result<Vec<ApplyItemAction>, ApplyItemError> {
character.materials.evade += 1;
entity_gateway.save_character(character).await.unwrap();
Ok(vec![ApplyItemAction::UpdateCharacter(character.clone())])
Ok(vec![ApplyItemAction::UpdateCharacter(Box::new(character.clone()))])
}
async fn def_material<EG: EntityGateway + ?Sized>(entity_gateway: &mut EG, character: &mut CharacterEntity) -> Result<Vec<ApplyItemAction>, ApplyItemError> {
character.materials.def += 1;
entity_gateway.save_character(character).await.unwrap();
Ok(vec![ApplyItemAction::UpdateCharacter(character.clone())])
Ok(vec![ApplyItemAction::UpdateCharacter(Box::new(character.clone()))])
}
async fn luck_material<EG: EntityGateway + ?Sized>(entity_gateway: &mut EG, character: &mut CharacterEntity) -> Result<Vec<ApplyItemAction>, ApplyItemError> {
character.materials.luck += 1;
entity_gateway.save_character(character).await.unwrap();
Ok(vec![ApplyItemAction::UpdateCharacter(character.clone())])
Ok(vec![ApplyItemAction::UpdateCharacter(Box::new(character.clone()))])
}
async fn hp_material<EG: EntityGateway + ?Sized>(entity_gateway: &mut EG, character: &mut CharacterEntity) -> Result<Vec<ApplyItemAction>, ApplyItemError> {
character.materials.hp += 1;
entity_gateway.save_character(character).await.unwrap();
Ok(vec![ApplyItemAction::UpdateCharacter(character.clone())])
Ok(vec![ApplyItemAction::UpdateCharacter(Box::new(character.clone()))])
}
async fn tp_material<EG: EntityGateway + ?Sized>(entity_gateway: &mut EG, character: &mut CharacterEntity) -> Result<Vec<ApplyItemAction>, ApplyItemError> {
character.materials.tp += 1;
entity_gateway.save_character(character).await.unwrap();
Ok(vec![ApplyItemAction::UpdateCharacter(character.clone())])
Ok(vec![ApplyItemAction::UpdateCharacter(Box::new(character.clone()))])
}
/*
@ -224,13 +229,23 @@ pub async fn liberta_kit<EG: EntityGateway>(entity_gateway: &mut EG, used_cell:
}
*/
async fn jack_o_lantern<'a, EG>(item_state: &mut ItemStateProxy,
entity_gateway: &mut EG
) -> Result<Vec<ApplyItemAction>, ApplyItemError>
where
EG: EntityGateway + ?Sized,
fn jack_o_lantern() -> Result<Vec<ApplyItemAction>, ApplyItemError>
{
Ok(Vec::new())
let mag_rate = WeightedIndex::new(&[13, 13, 13, 13, 12, 12, 12, 12]).unwrap();
let mag_type = match mag_rate.sample(&mut rand_chacha::ChaChaRng::from_entropy()) {
0 => ToolType::CellOfMag502,
1 => ToolType::CellOfMag213,
2 => ToolType::HeartOfChuChu,
3 => ToolType::HeartOfKapuKapu,
4 => ToolType::PartsOfRobochao,
5 => ToolType::HeartOfOpaOpa,
6 => ToolType::HeartOfPian,
7 => ToolType::HeartOfChao,
_ => unreachable!(),
};
Ok(vec![ApplyItemAction::CreateItem(ItemDetail::Tool(Tool {tool: mag_type}))])
}
async fn apply_tool<'a, EG>(item_state: &mut ItemStateProxy,
@ -283,7 +298,7 @@ where
| ToolType::LibertaKit => {
mag_cell(item_state, entity_gateway, character, entity_id, tool.try_into()?).await
}
ToolType::JackOLantern => jack_o_lantern(item_state, entity_gateway).await,
ToolType::JackOLantern => jack_o_lantern(),
// TODO: rest of these
_ => Err(ApplyItemError::InvalidItem)
}

2
src/ship/items/inventory.rs

@ -179,7 +179,7 @@ pub enum InventoryError {
MesetaFull,
}
#[derive(Clone)]
#[derive(Clone, Debug)]
pub struct InventoryState {
pub character_id: CharacterEntityId,
pub item_id_counter: u32,

2
src/ship/items/state.rs

@ -124,7 +124,7 @@ pub enum AddItemResult {
}
#[derive(Clone)]
#[derive(Clone, Debug)]
pub struct ItemState {
character_inventory: Arc<RwLock<HashMap<CharacterEntityId, RwLock<InventoryState>>>>,
character_bank: Arc<RwLock<HashMap<CharacterEntityId, RwLock<BankState>>>>,

14
src/ship/items/tasks.rs

@ -272,22 +272,28 @@ pub async fn use_item<'a, EG> (
item_state: &'a mut ItemState,
entity_gateway: &mut EG,
character: &mut CharacterEntity,
area_client: AreaClient,
item_id: &ClientItemId,
amount: u32,
) -> Result<(), ItemStateError>
) -> Result<Vec<SendShipPacket>, ItemStateError>
where
EG: EntityGateway + 'static,
{
entity_gateway.with_transaction(|transaction| async move {
let item_state_proxy = ItemStateProxy::new(item_state.clone());
let ((item_state_proxy, transaction), apply_item_actions) = ItemStateAction::default()
let ((item_state_proxy, transaction), (pkts, new_character)) = ItemStateAction::default()
.act(actions::take_item_from_inventory(character.id, *item_id, amount))
.act(actions::use_consumed_item(character.clone()))
.act(actions::use_consumed_item(&character))
.act(actions::fork(
actions::foreach(actions::apply_item_action_packets(character.id, area_client)),
actions::apply_item_action_character(character)
))
.commit((item_state_proxy, transaction))
.await?;
item_state_proxy.commit().await;
*character = new_character;
Ok((transaction, ()))
Ok((transaction, pkts.into_iter().flatten().collect()))
}).await
}

22
src/ship/packet/handler/message.rs

@ -330,20 +330,32 @@ where
pub async fn player_uses_item<EG>(id: ClientId,
player_use_tool: PlayerUseItem,
entity_gateway: &mut EG,
_client_location: &ClientLocation,
client_location: &ClientLocation,
clients: &Clients,
item_state: &mut ItemState)
-> Result<Vec<(ClientId, SendShipPacket)>, ShipError>
where
EG: EntityGateway + Clone + 'static,
{
clients.with_mut(id, |client| {
let neighbors = client_location.get_all_clients_by_client(id).await?.into_iter();
let area_client = client_location.get_local_client(id).await?;
Ok(clients.with_mut(id, |client| {
let mut entity_gateway = entity_gateway.clone();
let mut item_state = item_state.clone();
Box::pin(async move {
use_item(&mut item_state, &mut entity_gateway, &mut client.character, &ClientItemId(player_use_tool.item_id), 1).await
})}).await??;
Ok(Vec::new())
use_item(&mut item_state, &mut entity_gateway, &mut client.character, area_client, &ClientItemId(player_use_tool.item_id), 1).await
})}).await??
.into_iter()
.flat_map(move |pkt| {
let player_use_tool = player_use_tool.clone();
neighbors.clone().map(move |client| {
vec![(client.client, SendShipPacket::Message(Message::new(GameMessage::PlayerUseItem(player_use_tool.clone())))), (client.client, pkt.clone())]
})
})
.flatten()
.collect::<Vec<_>>()
)
}
pub async fn player_used_medical_center<EG>(id: ClientId,

53
tests/test_item_use.rs

@ -251,6 +251,59 @@ async fn test_use_materials() {
assert!(char.materials.luck == 2);
}
#[async_std::test]
async fn test_jackolantern() {
let mut entity_gateway = InMemoryGateway::default();
let (_user1, char1) = new_user_character(&mut entity_gateway, "a1", "a", 1).await;
let p1_inv = vec![
item::InventoryItemEntity::Stacked(
vec![
entity_gateway.create_item(
item::NewItemEntity {
item: item::ItemDetail::Tool(
item::tool::Tool {
tool: item::tool::ToolType::JackOLantern,
}
),
}).await.unwrap(),
entity_gateway.create_item(item::NewItemEntity {
item: item::ItemDetail::Tool(
item::tool::Tool {
tool: item::tool::ToolType::JackOLantern,
}
),
}).await.unwrap(),
])];
entity_gateway.set_character_inventory(&char1.id, &item::InventoryEntity::new(p1_inv)).await.unwrap();
let mut ship = Box::new(ShipServerState::builder()
.gateway(entity_gateway.clone())
.build());
log_in_char(&mut ship, ClientId(1), "a1", "a").await;
join_lobby(&mut ship, ClientId(1)).await;
create_room(&mut ship, ClientId(1), "room", "").await;
ship.handle(ClientId(1), RecvShipPacket::Message(Message::new(GameMessage::PlayerUseItem(PlayerUseItem {
client: 0,
target: 0,
item_id: 0x10000,
})))).await.unwrap();
ship.handle(ClientId(1), RecvShipPacket::Message(Message::new(GameMessage::PlayerUseItem(PlayerUseItem {
client: 0,
target: 0,
item_id: 0x10000,
})))).await.unwrap();
let inventory_items = entity_gateway.get_character_inventory(&char1.id).await.unwrap();
for item in inventory_items.items {
for sitem in item.stacked().unwrap() {
assert!(sitem.item.clone().as_tool().unwrap().tool.is_mag_cell());
}
}
}
// TODO: tests for ALL ITEMS WOW
/*

32
tests/test_trade.rs

@ -1792,7 +1792,7 @@ async fn test_trade_multiple_individual() {
msg: GameMessage::CreateItem(CreateItem {
client: 1,
item_data: [0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], // saber
item_id: 0x810003,
item_id: 0x810004,
..
}),
..
@ -1801,7 +1801,7 @@ async fn test_trade_multiple_individual() {
msg: GameMessage::CreateItem(CreateItem {
client: 1,
item_data: [0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], // saber
item_id: 0x810003,
item_id: 0x810004,
..
}),
..
@ -1810,7 +1810,7 @@ async fn test_trade_multiple_individual() {
msg: GameMessage::CreateItem(CreateItem {
client: 1,
item_data: [0, 1, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0], // saber
item_id: 0x810004,
item_id: 0x810003,
..
}),
..
@ -1819,7 +1819,7 @@ async fn test_trade_multiple_individual() {
msg: GameMessage::CreateItem(CreateItem {
client: 1,
item_data: [0, 1, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0], // saber
item_id: 0x810004,
item_id: 0x810003,
..
}),
..
@ -1828,7 +1828,7 @@ async fn test_trade_multiple_individual() {
msg: GameMessage::CreateItem(CreateItem {
client: 0,
item_data: [0, 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], // handgun
item_id: 0x810001,
item_id: 0x810002,
..
}),
..
@ -1837,7 +1837,7 @@ async fn test_trade_multiple_individual() {
msg: GameMessage::CreateItem(CreateItem {
client: 0,
item_data: [0, 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], // handgun
item_id: 0x810001,
item_id: 0x810002,
..
}),
..
@ -1846,7 +1846,7 @@ async fn test_trade_multiple_individual() {
msg: GameMessage::CreateItem(CreateItem {
client: 0,
item_data: [0, 6, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0], // handgun
item_id: 0x810002,
item_id: 0x810001,
..
}),
..
@ -1855,7 +1855,7 @@ async fn test_trade_multiple_individual() {
msg: GameMessage::CreateItem(CreateItem {
client: 0,
item_data: [0, 6, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0], // handgun
item_id: 0x810002,
item_id: 0x810001,
..
}),
..
@ -2063,7 +2063,7 @@ async fn test_trade_multiple_stacked() {
assert!(matches!(ack[4], (ClientId(1), SendShipPacket::Message(Message {
msg: GameMessage::CreateItem(CreateItem {
client: 1,
item_id: 0x810003,
item_id: 0x810004,
..
}),
..
@ -2071,7 +2071,7 @@ async fn test_trade_multiple_stacked() {
assert!(matches!(ack[5], (ClientId(2), SendShipPacket::Message(Message {
msg: GameMessage::CreateItem(CreateItem {
client: 1,
item_id: 0x810003,
item_id: 0x810004,
..
}),
..
@ -2079,7 +2079,7 @@ async fn test_trade_multiple_stacked() {
assert!(matches!(ack[6], (ClientId(1), SendShipPacket::Message(Message {
msg: GameMessage::CreateItem(CreateItem {
client: 1,
item_id: 0x810004,
item_id: 0x810003,
..
}),
..
@ -2087,7 +2087,7 @@ async fn test_trade_multiple_stacked() {
assert!(matches!(ack[7], (ClientId(2), SendShipPacket::Message(Message {
msg: GameMessage::CreateItem(CreateItem {
client: 1,
item_id: 0x810004,
item_id: 0x810003,
..
}),
..
@ -2095,7 +2095,7 @@ async fn test_trade_multiple_stacked() {
assert!(matches!(ack[8], (ClientId(1), SendShipPacket::Message(Message {
msg: GameMessage::CreateItem(CreateItem {
client: 0,
item_id: 0x810001,
item_id: 0x810002,
..
}),
..
@ -2103,7 +2103,7 @@ async fn test_trade_multiple_stacked() {
assert!(matches!(ack[9], (ClientId(2), SendShipPacket::Message(Message {
msg: GameMessage::CreateItem(CreateItem {
client: 0,
item_id: 0x810001,
item_id: 0x810002,
..
}),
..
@ -2111,7 +2111,7 @@ async fn test_trade_multiple_stacked() {
assert!(matches!(ack[10], (ClientId(1), SendShipPacket::Message(Message {
msg: GameMessage::CreateItem(CreateItem {
client: 0,
item_id: 0x810002,
item_id: 0x810001,
..
}),
..
@ -2119,7 +2119,7 @@ async fn test_trade_multiple_stacked() {
assert!(matches!(ack[11], (ClientId(2), SendShipPacket::Message(Message {
msg: GameMessage::CreateItem(CreateItem {
client: 0,
item_id: 0x810002,
item_id: 0x810001,
..
}),
..

Loading…
Cancel
Save