use std::convert::TryInto;
use elseware::common::serverstate::{ClientId, ServerState};
use elseware::entity::gateway::{EntityGateway, InMemoryGateway};
use elseware::entity::item;
use elseware::ship::ship::{ShipServerState, RecvShipPacket, SendShipPacket, ShipError};
use elseware::entity::item::{Meseta, ItemEntity};
use elseware::ship::packet::handler::trade::TradeError;

use libpso::packet::ship::*;
use libpso::packet::messages::*;

#[path = "common.rs"]
mod common;
use common::*;

async fn initialize_trade<EG: EntityGateway + Clone>(ship: &mut ShipServerState<EG>, client1: ClientId, client2: ClientId) {
    ship.handle(client1, RecvShipPacket::DirectMessage(DirectMessage::new(client2.0 as u32 -1, GameMessage::TradeRequest(TradeRequest {
        client: client1.0 as u8 -1,
        target: 0,
        trade: TradeRequestCommand::Initialize(TradeRequestInitializeCommand::Initialize, 0)
    })))).await.unwrap();

    ship.handle(client2, RecvShipPacket::DirectMessage(DirectMessage::new(client1.0 as u32 -1, GameMessage::TradeRequest(TradeRequest {
        client: client2.0 as u8 -1,
        target: 0,
        trade: TradeRequestCommand::Initialize(TradeRequestInitializeCommand::Respond, 0)
    })))).await.unwrap();
}

async fn confirm_trade<EG: EntityGateway + Clone>(ship: &mut ShipServerState<EG>, client1: ClientId, client2: ClientId) {
    ship.handle(client1, RecvShipPacket::DirectMessage(DirectMessage::new(client2.0 as u32 -1, GameMessage::TradeRequest(TradeRequest {
        client: client1.0 as u8 -1,
        target: 0,
        trade: TradeRequestCommand::Confirm
    })))).await.unwrap();

    ship.handle(client2, RecvShipPacket::DirectMessage(DirectMessage::new(client1.0 as u32 -1, GameMessage::TradeRequest(TradeRequest {
        client: client2.0 as u8 -1,
        target: 0,
        trade: TradeRequestCommand::Confirm
    })))).await.unwrap();
}

async fn finalconfirm_trade<EG: EntityGateway + Clone>(ship: &mut ShipServerState<EG>, client1: ClientId, client2: ClientId) {
    ship.handle(client1, RecvShipPacket::DirectMessage(DirectMessage::new(client2.0 as u32 -1, GameMessage::TradeRequest(TradeRequest {
        client: client1.0 as u8 -1,
        target: 0,
        trade: TradeRequestCommand::FinalConfirm
    })))).await.unwrap();

    ship.handle(client2, RecvShipPacket::DirectMessage(DirectMessage::new(client1.0 as u32 -1, GameMessage::TradeRequest(TradeRequest {
        client: client2.0 as u8 -1,
        target: 0,
        trade: TradeRequestCommand::FinalConfirm
    })))).await.unwrap();
}

#[derive(Default)]
struct TradeItemBuilder {
    count: usize,
    items: [TradeItem; 32],
}

impl TradeItemBuilder {
    fn individual(mut self, item: &elseware::entity::item::InventoryItemEntity, item_id: u32) -> Self {
        let idata = item.with_individual(|i| i.item.as_client_bytes()).unwrap();
        self.items[self.count] = TradeItem {
            item_data: idata[0..12].try_into().unwrap(),
            item_id: item_id,
            item_data2: idata[12..16].try_into().unwrap(),
        };

        self.count += 1;
        self
    }

    fn stacked(mut self, item: &elseware::entity::item::InventoryItemEntity, item_id: u32, amount: u8) -> Self {
        let idata = item
            .with_stacked(|i| i[0].item.tool().unwrap().as_stacked_bytes(i.len()))
            .map(|mut data| {
                data[5] = amount;
                data
            })
            .unwrap();
        self.items[self.count] = TradeItem {
            item_data: idata[0..12].try_into().unwrap(),
            item_id: item_id,
            item_data2: idata[12..16].try_into().unwrap(),
        };

        self.count += 1;
        self
    }

    fn meseta(mut self, amount: usize) -> Self {
        self.items[self.count] = TradeItem {
            item_data: [4, 0, 0, 0,  0, 0, 0, 0,  0, 0, 0, 0],
            item_id: 0xFFFFFFFF,
            item_data2: u32::to_le_bytes(amount as u32),
        };

        self.count += 1;
        self
    }

    fn build(self) -> [TradeItem; 32] {
        self.items
    }
}


#[async_std::test]
async fn test_trade_one_individual_item() {
    let mut entity_gateway = InMemoryGateway::default();

    let (_user1, char1) = new_user_character(&mut entity_gateway, "a1", "a", 1).await;
    let (_user2, char2) = new_user_character(&mut entity_gateway, "a2", "a", 1).await;

    let mut p1_inv = Vec::new();
    p1_inv.push(entity_gateway.create_item(
            item::NewItemEntity {
                item: item::ItemDetail::Weapon(
                    item::weapon::Weapon {
                        weapon: item::weapon::WeaponType::Handgun,
                        grind: 0,
                        special: None,
                        attrs: [None, None, None],
                        tekked: true,
                    }
                ),
            }).await.unwrap());

    entity_gateway.set_character_inventory(&char1.id, &item::InventoryEntity::new(p1_inv)).await.unwrap();
    entity_gateway.set_character_inventory(&char2.id, &item::InventoryEntity::new(Vec::<item::InventoryItemEntity>::new())).await.unwrap();

    let mut ship = Box::new(ShipServerState::builder()
        .gateway(entity_gateway.clone())
        .build());
    log_in_char(&mut ship, ClientId(1), "a1", "a").await;
    log_in_char(&mut ship, ClientId(2), "a2", "a").await;

    join_lobby(&mut ship, ClientId(1)).await;
    join_lobby(&mut ship, ClientId(2)).await;

    create_room(&mut ship, ClientId(1), "room", "").await;
    join_room(&mut ship, ClientId(2), 0).await;

    let p1_items = entity_gateway.get_character_inventory(&char1.id).await.unwrap();
    assert_eq!(p1_items.items.len(), 1);
    let p2_items = entity_gateway.get_character_inventory(&char2.id).await.unwrap();
    assert_eq!(p2_items.items.len(), 0);

    initialize_trade(&mut ship, ClientId(1), ClientId(2)).await;

    ship.handle(ClientId(1), RecvShipPacket::DirectMessage(DirectMessage::new(1, GameMessage::TradeRequest(TradeRequest {
        client: 1,
        target: 0,
        trade: TradeRequestCommand::AddItem(0x10000, 1)
    })))).await.unwrap();

    confirm_trade(&mut ship, ClientId(1), ClientId(2)).await;
    finalconfirm_trade(&mut ship, ClientId(1), ClientId(2)).await;

    let titems = TradeItemBuilder::default()
        .individual(&p1_items.items[0], 0x10000)
        .build();
    let ack = ship.handle(ClientId(1), RecvShipPacket::ItemsToTrade(ItemsToTrade {
        trade_target: 1,
        unknown2: 0,
        count: 1,
        items: titems,
    })).await.unwrap();
    assert_eq!(ack.len(), 0);

    let ack = ship.handle(ClientId(2), RecvShipPacket::ItemsToTrade(ItemsToTrade {
        trade_target: 0,
        unknown2: 0,
        count: 0,
        items: Default::default(),
    })).await.unwrap();
    assert_eq!(ack.len(), 2);
    assert!(matches!(ack[0], (ClientId(2), SendShipPacket::AcknowledgeTrade(AcknowledgeTrade {}))));
    assert!(matches!(ack[1], (ClientId(1), SendShipPacket::AcknowledgeTrade(AcknowledgeTrade {}))));

    let ack = ship.handle(ClientId(1), RecvShipPacket::TradeConfirmed(TradeConfirmed {
    })).await.unwrap();
    assert_eq!(ack.len(), 0);

    let ack = ship.handle(ClientId(2), RecvShipPacket::TradeConfirmed(TradeConfirmed {
    })).await.unwrap();
    assert_eq!(ack.len(), 5);
    assert!(matches!(ack[0], (ClientId(2), SendShipPacket::Message(Message {
        msg: GameMessage::PlayerNoLongerHasItem(PlayerNoLongerHasItem {..}),
        ..
    }))));
    assert!(matches!(ack[1], (ClientId(1), SendShipPacket::Message(Message {
        msg: GameMessage::CreateItem(CreateItem {..}),
        ..
    }))));
    assert!(matches!(ack[2], (ClientId(2), SendShipPacket::Message(Message {
        msg: GameMessage::CreateItem(CreateItem {..}),
        ..
    }))));
    assert!(matches!(ack[3], (ClientId(2), SendShipPacket::TradeSuccessful {..})));
    assert!(matches!(ack[4], (ClientId(1), SendShipPacket::TradeSuccessful {..})));

    let p1_items = entity_gateway.get_character_inventory(&char1.id).await.unwrap();
    assert_eq!(p1_items.items.len(), 0);
    let p2_items = entity_gateway.get_character_inventory(&char2.id).await.unwrap();
    assert_eq!(p2_items.items.len(), 1);
}

#[async_std::test]
async fn test_trade_player2_to_player1() {
    let mut entity_gateway = InMemoryGateway::default();

    let (_user1, char1) = new_user_character(&mut entity_gateway, "a1", "a", 1).await;
    let (_user2, char2) = new_user_character(&mut entity_gateway, "a2", "a", 1).await;

    let mut p2_inv = Vec::new();
    p2_inv.push(entity_gateway.create_item(
            item::NewItemEntity {
                item: item::ItemDetail::Weapon(
                    item::weapon::Weapon {
                        weapon: item::weapon::WeaponType::Handgun,
                        grind: 0,
                        special: None,
                        attrs: [None, None, None],
                        tekked: true,
                    }
                ),
            }).await.unwrap());

    entity_gateway.set_character_inventory(&char1.id, &item::InventoryEntity::new(Vec::<item::InventoryItemEntity>::new())).await.unwrap();
    entity_gateway.set_character_inventory(&char2.id, &item::InventoryEntity::new(p2_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;
    log_in_char(&mut ship, ClientId(2), "a2", "a").await;

    join_lobby(&mut ship, ClientId(1)).await;
    join_lobby(&mut ship, ClientId(2)).await;

    create_room(&mut ship, ClientId(1), "room", "").await;
    join_room(&mut ship, ClientId(2), 0).await;

    let p1_items = entity_gateway.get_character_inventory(&char1.id).await.unwrap();
    assert_eq!(p1_items.items.len(), 0);
    let p2_items = entity_gateway.get_character_inventory(&char2.id).await.unwrap();
    assert_eq!(p2_items.items.len(), 1);

    initialize_trade(&mut ship, ClientId(1), ClientId(2)).await;

    ship.handle(ClientId(2), RecvShipPacket::DirectMessage(DirectMessage::new(1, GameMessage::TradeRequest(TradeRequest {
        client: 1,
        target: 0,
        trade: TradeRequestCommand::AddItem(0x210000, 1)
    })))).await.unwrap();

    confirm_trade(&mut ship, ClientId(1), ClientId(2)).await;
    finalconfirm_trade(&mut ship, ClientId(1), ClientId(2)).await;

    let ack = ship.handle(ClientId(1), RecvShipPacket::ItemsToTrade(ItemsToTrade {
        trade_target: 1,
        unknown2: 0,
        count: 0,
        items: Default::default(),
    })).await.unwrap();
    assert_eq!(ack.len(), 0);

    let titems = TradeItemBuilder::default()
        .individual(&p2_items.items[0], 0x210000)
        .build();
    let ack = ship.handle(ClientId(2), RecvShipPacket::ItemsToTrade(ItemsToTrade {
        trade_target: 0,
        unknown2: 0,
        count: 1,
        items: titems,
    })).await.unwrap();
    assert_eq!(ack.len(), 2);
    assert!(matches!(ack[0], (ClientId(2), SendShipPacket::AcknowledgeTrade(AcknowledgeTrade {}))));
    assert!(matches!(ack[1], (ClientId(1), SendShipPacket::AcknowledgeTrade(AcknowledgeTrade {}))));

    let ack = ship.handle(ClientId(1), RecvShipPacket::TradeConfirmed(TradeConfirmed {
    })).await.unwrap();
    assert_eq!(ack.len(), 0);

    let ack = ship.handle(ClientId(2), RecvShipPacket::TradeConfirmed(TradeConfirmed {
    })).await.unwrap();
    assert_eq!(ack.len(), 5);
    assert!(matches!(ack[0], (ClientId(1), SendShipPacket::Message(Message {
        msg: GameMessage::PlayerNoLongerHasItem(PlayerNoLongerHasItem {..}),
        ..
    }))));
    assert!(matches!(ack[1], (ClientId(1), SendShipPacket::Message(Message {
        msg: GameMessage::CreateItem(CreateItem {..}),
        ..
    }))));
    assert!(matches!(ack[2], (ClientId(2), SendShipPacket::Message(Message {
        msg: GameMessage::CreateItem(CreateItem {..}),
        ..
    }))));
    assert!(matches!(ack[3], (ClientId(2), SendShipPacket::TradeSuccessful {..})));
    assert!(matches!(ack[4], (ClientId(1), SendShipPacket::TradeSuccessful {..})));

    let p1_items = entity_gateway.get_character_inventory(&char1.id).await.unwrap();
    assert_eq!(p1_items.items.len(), 1);
    let p2_items = entity_gateway.get_character_inventory(&char2.id).await.unwrap();
    assert_eq!(p2_items.items.len(), 0);
}

#[async_std::test]
async fn test_reverse_trade_ack_order() {
    let mut entity_gateway = InMemoryGateway::default();

    let (_user1, char1) = new_user_character(&mut entity_gateway, "a1", "a", 1).await;
    let (_user2, char2) = new_user_character(&mut entity_gateway, "a2", "a", 1).await;

    let mut p1_inv = Vec::new();
    p1_inv.push(entity_gateway.create_item(
            item::NewItemEntity {
                item: item::ItemDetail::Weapon(
                    item::weapon::Weapon {
                        weapon: item::weapon::WeaponType::Handgun,
                        grind: 0,
                        special: None,
                        attrs: [None, None, None],
                        tekked: true,
                    }
                ),
            }).await.unwrap());

    entity_gateway.set_character_inventory(&char1.id, &item::InventoryEntity::new(p1_inv)).await.unwrap();
    entity_gateway.set_character_inventory(&char2.id, &item::InventoryEntity::new(Vec::<item::InventoryItemEntity>::new())).await.unwrap();

    let mut ship = Box::new(ShipServerState::builder()
        .gateway(entity_gateway.clone())
        .build());
    log_in_char(&mut ship, ClientId(1), "a1", "a").await;
    log_in_char(&mut ship, ClientId(2), "a2", "a").await;

    join_lobby(&mut ship, ClientId(1)).await;
    join_lobby(&mut ship, ClientId(2)).await;

    create_room(&mut ship, ClientId(1), "room", "").await;
    join_room(&mut ship, ClientId(2), 0).await;

    let p1_items = entity_gateway.get_character_inventory(&char1.id).await.unwrap();
    assert_eq!(p1_items.items.len(), 1);
    let p2_items = entity_gateway.get_character_inventory(&char2.id).await.unwrap();
    assert_eq!(p2_items.items.len(), 0);

    initialize_trade(&mut ship, ClientId(2), ClientId(1)).await;

    ship.handle(ClientId(1), RecvShipPacket::DirectMessage(DirectMessage::new(1, GameMessage::TradeRequest(TradeRequest {
        client: 1,
        target: 0,
        trade: TradeRequestCommand::AddItem(0x10000, 1)
    })))).await.unwrap();

    confirm_trade(&mut ship, ClientId(2), ClientId(1)).await;
    finalconfirm_trade(&mut ship, ClientId(2), ClientId(1)).await;

    let ack = ship.handle(ClientId(2), RecvShipPacket::ItemsToTrade(ItemsToTrade {
        trade_target: 0,
        unknown2: 0,
        count: 0,
        items: Default::default(),
    })).await.unwrap();
    assert_eq!(ack.len(), 0);

    let titems = TradeItemBuilder::default()
        .individual(&p1_items.items[0], 0x10000)
        .build();
    let ack = ship.handle(ClientId(1), RecvShipPacket::ItemsToTrade(ItemsToTrade {
        trade_target: 1,
        unknown2: 0,
        count: 1,
        items: titems,
    })).await.unwrap();
    assert_eq!(ack.len(), 2);
    assert!(matches!(ack[0], (ClientId(1), SendShipPacket::AcknowledgeTrade(AcknowledgeTrade {}))));
    assert!(matches!(ack[1], (ClientId(2), SendShipPacket::AcknowledgeTrade(AcknowledgeTrade {}))));

    let ack = ship.handle(ClientId(1), RecvShipPacket::TradeConfirmed(TradeConfirmed {
    })).await.unwrap();
    assert_eq!(ack.len(), 0);

    let ack = ship.handle(ClientId(2), RecvShipPacket::TradeConfirmed(TradeConfirmed {
    })).await.unwrap();
    assert_eq!(ack.len(), 5);
    assert!(matches!(ack[0], (ClientId(2), SendShipPacket::Message(Message {
        msg: GameMessage::PlayerNoLongerHasItem(PlayerNoLongerHasItem {..}),
        ..
    }))));
    assert!(matches!(ack[1], (ClientId(1), SendShipPacket::Message(Message {
        msg: GameMessage::CreateItem(CreateItem {..}),
        ..
    }))));
    assert!(matches!(ack[2], (ClientId(2), SendShipPacket::Message(Message {
        msg: GameMessage::CreateItem(CreateItem {..}),
        ..
    }))));
    assert!(matches!(ack[3], (ClientId(2), SendShipPacket::TradeSuccessful {..})));
    assert!(matches!(ack[4], (ClientId(1), SendShipPacket::TradeSuccessful {..})));

    let p1_items = entity_gateway.get_character_inventory(&char1.id).await.unwrap();
    assert_eq!(p1_items.items.len(), 0);
    let p2_items = entity_gateway.get_character_inventory(&char2.id).await.unwrap();
    assert_eq!(p2_items.items.len(), 1);
}

#[async_std::test]
async fn test_trade_one_stacked_item() {
    let mut entity_gateway = InMemoryGateway::default();

    let (_user1, char1) = new_user_character(&mut entity_gateway, "a1", "a", 1).await;
    let (_user2, char2) = new_user_character(&mut entity_gateway, "a2", "a", 1).await;

    let p1_stack = futures::future::join_all((0..2).map(|_| {
        let mut entity_gateway = entity_gateway.clone();
        async move {
            entity_gateway.create_item(
                item::NewItemEntity {
                    item: item::ItemDetail::Tool(
                        item::tool::Tool {
                            tool: item::tool::ToolType::Monomate,
                        }
                    )
                }).await
        }}))
        .await
        .into_iter()
        .collect::<Result<Vec<ItemEntity>,_>>()
        .unwrap();

    entity_gateway.set_character_inventory(&char1.id, &item::InventoryEntity::new(vec![p1_stack])).await.unwrap();
    entity_gateway.set_character_inventory(&char2.id, &item::InventoryEntity::new(Vec::<item::InventoryItemEntity>::new())).await.unwrap();

    let mut ship = Box::new(ShipServerState::builder()
        .gateway(entity_gateway.clone())
        .build());
    log_in_char(&mut ship, ClientId(1), "a1", "a").await;
    log_in_char(&mut ship, ClientId(2), "a2", "a").await;

    join_lobby(&mut ship, ClientId(1)).await;
    join_lobby(&mut ship, ClientId(2)).await;

    create_room(&mut ship, ClientId(1), "room", "").await;
    join_room(&mut ship, ClientId(2), 0).await;

    let p1_items = entity_gateway.get_character_inventory(&char1.id).await.unwrap();
    assert_eq!(p1_items.items.len(), 1);
    let p2_items = entity_gateway.get_character_inventory(&char2.id).await.unwrap();
    assert_eq!(p2_items.items.len(), 0);

    initialize_trade(&mut ship, ClientId(1), ClientId(2)).await;

    ship.handle(ClientId(1), RecvShipPacket::DirectMessage(DirectMessage::new(1, GameMessage::TradeRequest(TradeRequest {
        client: 1,
        target: 0,
        trade: TradeRequestCommand::AddItem(0x10000, 2)
    })))).await.unwrap();

    confirm_trade(&mut ship, ClientId(1), ClientId(2)).await;
    finalconfirm_trade(&mut ship, ClientId(1), ClientId(2)).await;

    let titems = TradeItemBuilder::default()
        .stacked(&p1_items.items[0], 0x10000, 2)
        .build();
    let ack = ship.handle(ClientId(1), RecvShipPacket::ItemsToTrade(ItemsToTrade {
        trade_target: 1,
        unknown2: 0,
        count: 1,
        items: titems,
    })).await.unwrap();
    assert_eq!(ack.len(), 0);

    let ack = ship.handle(ClientId(2), RecvShipPacket::ItemsToTrade(ItemsToTrade {
        trade_target: 0,
        unknown2: 0,
        count: 0,
        items: Default::default(),
    })).await.unwrap();
    assert_eq!(ack.len(), 2);
    assert!(matches!(ack[0], (ClientId(2), SendShipPacket::AcknowledgeTrade(AcknowledgeTrade {}))));
    assert!(matches!(ack[1], (ClientId(1), SendShipPacket::AcknowledgeTrade(AcknowledgeTrade {}))));

    let ack = ship.handle(ClientId(1), RecvShipPacket::TradeConfirmed(TradeConfirmed {
    })).await.unwrap();
    assert_eq!(ack.len(), 0);

    let ack = ship.handle(ClientId(2), RecvShipPacket::TradeConfirmed(TradeConfirmed {
    })).await.unwrap();
    assert_eq!(ack.len(), 5);
    assert!(matches!(ack[0], (ClientId(2), SendShipPacket::Message(Message {
        msg: GameMessage::PlayerNoLongerHasItem(PlayerNoLongerHasItem {..}),
        ..
    }))));
    assert!(matches!(ack[1], (ClientId(1), SendShipPacket::Message(Message {
        msg: GameMessage::CreateItem(CreateItem {..}),
        ..
    }))));
    assert!(matches!(ack[2], (ClientId(2), SendShipPacket::Message(Message {
        msg: GameMessage::CreateItem(CreateItem {..}),
        ..
    }))));
    assert!(matches!(ack[3], (ClientId(2), SendShipPacket::TradeSuccessful {..})));
    assert!(matches!(ack[4], (ClientId(1), SendShipPacket::TradeSuccessful {..})));

    let p1_items = entity_gateway.get_character_inventory(&char1.id).await.unwrap();
    assert_eq!(p1_items.items.len(), 0);
    let p2_items = entity_gateway.get_character_inventory(&char2.id).await.unwrap();
    assert_eq!(p2_items.items.len(), 1);
}

#[async_std::test]
async fn test_trade_partial_stacked_item() {
    let mut entity_gateway = InMemoryGateway::default();

    let (_user1, char1) = new_user_character(&mut entity_gateway, "a1", "a", 1).await;
    let (_user2, char2) = new_user_character(&mut entity_gateway, "a2", "a", 1).await;

    let p1_stack = futures::future::join_all((0..2).map(|_| {
        let mut entity_gateway = entity_gateway.clone();
        async move {
            entity_gateway.create_item(
                item::NewItemEntity {
                    item: item::ItemDetail::Tool(
                        item::tool::Tool {
                            tool: item::tool::ToolType::Monomate,
                        }
                    )
                }).await
        }}))
        .await
        .into_iter()
        .collect::<Result<Vec<ItemEntity>,_>>()
        .unwrap();

    entity_gateway.set_character_inventory(&char1.id, &item::InventoryEntity::new(vec![p1_stack])).await.unwrap();
    entity_gateway.set_character_inventory(&char2.id, &item::InventoryEntity::new(Vec::<item::InventoryItemEntity>::new())).await.unwrap();

    let mut ship = Box::new(ShipServerState::builder()
        .gateway(entity_gateway.clone())
        .build());
    log_in_char(&mut ship, ClientId(1), "a1", "a").await;
    log_in_char(&mut ship, ClientId(2), "a2", "a").await;

    join_lobby(&mut ship, ClientId(1)).await;
    join_lobby(&mut ship, ClientId(2)).await;

    create_room(&mut ship, ClientId(1), "room", "").await;
    join_room(&mut ship, ClientId(2), 0).await;

    let p1_items = entity_gateway.get_character_inventory(&char1.id).await.unwrap();
    assert_eq!(p1_items.items.len(), 1);
    assert_eq!(p1_items.items[0].with_stacked(|i| i.len()).unwrap(), 2);
    let p2_items = entity_gateway.get_character_inventory(&char2.id).await.unwrap();
    assert_eq!(p2_items.items.len(), 0);

    initialize_trade(&mut ship, ClientId(1), ClientId(2)).await;

    ship.handle(ClientId(1), RecvShipPacket::DirectMessage(DirectMessage::new(1, GameMessage::TradeRequest(TradeRequest {
        client: 1,
        target: 0,
        trade: TradeRequestCommand::AddItem(0x10000, 1)
    })))).await.unwrap();

    confirm_trade(&mut ship, ClientId(1), ClientId(2)).await;
    finalconfirm_trade(&mut ship, ClientId(1), ClientId(2)).await;

    let titems = TradeItemBuilder::default()
        .stacked(&p1_items.items[0], 0x10000, 1)
        .build();
    let ack = ship.handle(ClientId(1), RecvShipPacket::ItemsToTrade(ItemsToTrade {
        trade_target: 1,
        unknown2: 0,
        count: 1,
        items: titems,
    })).await.unwrap();
    assert_eq!(ack.len(), 0);

    let ack = ship.handle(ClientId(2), RecvShipPacket::ItemsToTrade(ItemsToTrade {
        trade_target: 0,
        unknown2: 0,
        count: 0,
        items: Default::default(),
    })).await.unwrap();
    assert_eq!(ack.len(), 2);
    assert!(matches!(ack[0], (ClientId(2), SendShipPacket::AcknowledgeTrade(AcknowledgeTrade {}))));
    assert!(matches!(ack[1], (ClientId(1), SendShipPacket::AcknowledgeTrade(AcknowledgeTrade {}))));

    let ack = ship.handle(ClientId(1), RecvShipPacket::TradeConfirmed(TradeConfirmed {
    })).await.unwrap();
    assert_eq!(ack.len(), 0);

    let ack = ship.handle(ClientId(2), RecvShipPacket::TradeConfirmed(TradeConfirmed {
    })).await.unwrap();
    assert_eq!(ack.len(), 5);
    assert!(matches!(ack[0], (ClientId(2), SendShipPacket::Message(Message {
        msg: GameMessage::PlayerNoLongerHasItem(PlayerNoLongerHasItem {..}),
        ..
    }))));
    assert!(matches!(ack[1], (ClientId(1), SendShipPacket::Message(Message {
        msg: GameMessage::CreateItem(CreateItem {..}),
        ..
    }))));
    assert!(matches!(ack[2], (ClientId(2), SendShipPacket::Message(Message {
        msg: GameMessage::CreateItem(CreateItem {..}),
        ..
    }))));
    assert!(matches!(ack[3], (ClientId(2), SendShipPacket::TradeSuccessful {..})));
    assert!(matches!(ack[4], (ClientId(1), SendShipPacket::TradeSuccessful {..})));

    let p1_items = entity_gateway.get_character_inventory(&char1.id).await.unwrap();
    assert_eq!(p1_items.items.len(), 1);
    assert_eq!(p1_items.items[0].with_stacked(|i| i.len()).unwrap(), 1);
    let p2_items = entity_gateway.get_character_inventory(&char2.id).await.unwrap();
    assert_eq!(p2_items.items.len(), 1);
    assert_eq!(p2_items.items[0].with_stacked(|i| i.len()).unwrap(), 1);
}

#[async_std::test]
async fn test_trade_individual_both() {
    let mut entity_gateway = InMemoryGateway::default();

    let (_user1, char1) = new_user_character(&mut entity_gateway, "a1", "a", 1).await;
    let (_user2, char2) = new_user_character(&mut entity_gateway, "a2", "a", 1).await;

    let p1_inv = vec![
        entity_gateway.create_item(
            item::NewItemEntity {
                item: item::ItemDetail::Weapon(
                    item::weapon::Weapon {
                        weapon: item::weapon::WeaponType::Saber,
                        grind: 0,
                        special: None,
                        attrs: [None, None, None],
                        tekked: true,
                    }
                ),
            }).await.unwrap()];
    let p2_inv = vec![
        entity_gateway.create_item(
            item::NewItemEntity {
                item: item::ItemDetail::Weapon(
                    item::weapon::Weapon {
                        weapon: item::weapon::WeaponType::Handgun,
                        grind: 0,
                        special: None,
                        attrs: [None, None, None],
                        tekked: true,
                    }
                ),
            }).await.unwrap()];

    entity_gateway.set_character_inventory(&char1.id, &item::InventoryEntity::new(p1_inv)).await.unwrap();
    entity_gateway.set_character_inventory(&char2.id, &item::InventoryEntity::new(p2_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;
    log_in_char(&mut ship, ClientId(2), "a2", "a").await;

    join_lobby(&mut ship, ClientId(1)).await;
    join_lobby(&mut ship, ClientId(2)).await;

    create_room(&mut ship, ClientId(1), "room", "").await;
    join_room(&mut ship, ClientId(2), 0).await;

    let p1_items = entity_gateway.get_character_inventory(&char1.id).await.unwrap();
    assert_eq!(p1_items.items.len(), 1);
    let p2_items = entity_gateway.get_character_inventory(&char2.id).await.unwrap();
    assert_eq!(p2_items.items.len(), 1);

    initialize_trade(&mut ship, ClientId(1), ClientId(2)).await;

    ship.handle(ClientId(1), RecvShipPacket::DirectMessage(DirectMessage::new(1, GameMessage::TradeRequest(TradeRequest {
        client: 1,
        target: 0,
        trade: TradeRequestCommand::AddItem(0x10000, 1)
    })))).await.unwrap();

    ship.handle(ClientId(2), RecvShipPacket::DirectMessage(DirectMessage::new(1, GameMessage::TradeRequest(TradeRequest {
        client: 0,
        target: 0,
        trade: TradeRequestCommand::AddItem(0x210000, 1)
    })))).await.unwrap();

    confirm_trade(&mut ship, ClientId(1), ClientId(2)).await;
    finalconfirm_trade(&mut ship, ClientId(1), ClientId(2)).await;

    let titems = TradeItemBuilder::default()
        .individual(&p1_items.items[0], 0x10000)
        .build();
    let ack = ship.handle(ClientId(1), RecvShipPacket::ItemsToTrade(ItemsToTrade {
        trade_target: 1,
        unknown2: 0,
        count: 1,
        items: titems,
    })).await.unwrap();
    assert_eq!(ack.len(), 0);

    let titems = TradeItemBuilder::default()
        .individual(&p2_items.items[0], 0x210000)
        .build();
    let ack = ship.handle(ClientId(2), RecvShipPacket::ItemsToTrade(ItemsToTrade {
        trade_target: 0,
        unknown2: 0,
        count: 1,
        items: titems,
    })).await.unwrap();
    assert_eq!(ack.len(), 2);
    assert!(matches!(ack[0], (ClientId(2), SendShipPacket::AcknowledgeTrade(AcknowledgeTrade {}))));
    assert!(matches!(ack[1], (ClientId(1), SendShipPacket::AcknowledgeTrade(AcknowledgeTrade {}))));

    let ack = ship.handle(ClientId(1), RecvShipPacket::TradeConfirmed(TradeConfirmed {
    })).await.unwrap();
    assert_eq!(ack.len(), 0);

    let ack = ship.handle(ClientId(2), RecvShipPacket::TradeConfirmed(TradeConfirmed {
    })).await.unwrap();
    assert_eq!(ack.len(), 8);
    assert!(matches!(ack[0], (ClientId(1), SendShipPacket::Message(Message {
        msg: GameMessage::PlayerNoLongerHasItem(PlayerNoLongerHasItem {
            client: 1,
            item_id: 0x210000,
            ..
        }),
        ..
    }))));
    assert!(matches!(ack[1], (ClientId(2), SendShipPacket::Message(Message {
        msg: GameMessage::PlayerNoLongerHasItem(PlayerNoLongerHasItem {
            client: 0,
            item_id: 0x10000,
            ..
        }),
        ..
    }))));
    assert!(matches!(ack[2], (ClientId(1), SendShipPacket::Message(Message {
        msg: GameMessage::CreateItem(CreateItem {
            client: 1,
            item_data: [0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], // saber
            item_id: 0x810002,
            ..
        }),
        ..
    }))));
    assert!(matches!(ack[3], (ClientId(2), SendShipPacket::Message(Message {
        msg: GameMessage::CreateItem(CreateItem {
            client: 1,
            item_data: [0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], // saber
            item_id: 0x810002,
            ..
        }),
        ..
    }))));
    assert!(matches!(ack[4], (ClientId(1), SendShipPacket::Message(Message {
        msg: GameMessage::CreateItem(CreateItem {
            client: 0,
            item_data: [0, 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], // handgun
            item_id: 0x810001,
            ..
        }),
        ..
    }))));
    assert!(matches!(ack[5], (ClientId(2), SendShipPacket::Message(Message {
        msg: GameMessage::CreateItem(CreateItem {
            client: 0,
            item_data: [0, 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], // handgun
            item_id: 0x810001,
            ..
        }),
        ..
    }))));
    assert!(matches!(ack[6], (ClientId(2), SendShipPacket::TradeSuccessful {..})));
    assert!(matches!(ack[7], (ClientId(1), SendShipPacket::TradeSuccessful {..})));

    let p1_items = entity_gateway.get_character_inventory(&char1.id).await.unwrap();
    assert_eq!(p1_items.items.len(), 1);
    assert!(matches!(p1_items.items[0].with_individual(|i| i.clone()).unwrap(), item::ItemEntity{item: item::ItemDetail::Weapon(item::weapon::Weapon {weapon: item::weapon::WeaponType::Handgun, ..}), ..}));
    let p2_items = entity_gateway.get_character_inventory(&char2.id).await.unwrap();
    assert_eq!(p2_items.items.len(), 1);
    assert!(matches!(p2_items.items[0].with_individual(|i| i.clone()).unwrap(), item::ItemEntity{item: item::ItemDetail::Weapon(item::weapon::Weapon {weapon: item::weapon::WeaponType::Saber, ..}), ..}));
}

#[async_std::test]
async fn test_trade_stacked_both() {
    let mut entity_gateway = InMemoryGateway::default();

    let (_user1, char1) = new_user_character(&mut entity_gateway, "a1", "a", 1).await;
    let (_user2, char2) = new_user_character(&mut entity_gateway, "a2", "a", 1).await;

    let p1_stack = futures::future::join_all((0..2).map(|_| {
        let mut entity_gateway = entity_gateway.clone();
        async move {
            entity_gateway.create_item(
                item::NewItemEntity {
                    item: item::ItemDetail::Tool(
                        item::tool::Tool {
                            tool: item::tool::ToolType::Monomate,
                        }
                    )
                }).await
        }}))
        .await
        .into_iter()
        .collect::<Result<Vec<ItemEntity>,_>>()
        .unwrap();

    let p2_stack = futures::future::join_all((0..3).map(|_| {
        let mut entity_gateway = entity_gateway.clone();
        async move {
            entity_gateway.create_item(
                item::NewItemEntity {
                    item: item::ItemDetail::Tool(
                        item::tool::Tool {
                            tool: item::tool::ToolType::Monofluid,
                        }
                    )
                }).await
        }}))
        .await
        .into_iter()
        .collect::<Result<Vec<ItemEntity>,_>>()
        .unwrap();

    entity_gateway.set_character_inventory(&char1.id, &item::InventoryEntity::new(vec![p1_stack])).await.unwrap();
    entity_gateway.set_character_inventory(&char2.id, &item::InventoryEntity::new(vec![p2_stack])).await.unwrap();

    let mut ship = Box::new(ShipServerState::builder()
        .gateway(entity_gateway.clone())
        .build());
    log_in_char(&mut ship, ClientId(1), "a1", "a").await;
    log_in_char(&mut ship, ClientId(2), "a2", "a").await;

    join_lobby(&mut ship, ClientId(1)).await;
    join_lobby(&mut ship, ClientId(2)).await;

    create_room(&mut ship, ClientId(1), "room", "").await;
    join_room(&mut ship, ClientId(2), 0).await;

    let p1_items = entity_gateway.get_character_inventory(&char1.id).await.unwrap();
    assert_eq!(p1_items.items.len(), 1);
    let p2_items = entity_gateway.get_character_inventory(&char2.id).await.unwrap();
    assert_eq!(p2_items.items.len(), 1);

    initialize_trade(&mut ship, ClientId(1), ClientId(2)).await;

    ship.handle(ClientId(1), RecvShipPacket::DirectMessage(DirectMessage::new(1, GameMessage::TradeRequest(TradeRequest {
        client: 1,
        target: 0,
        trade: TradeRequestCommand::AddItem(0x10000, 2)
    })))).await.unwrap();

    ship.handle(ClientId(2), RecvShipPacket::DirectMessage(DirectMessage::new(0, GameMessage::TradeRequest(TradeRequest {
        client: 0,
        target: 0,
        trade: TradeRequestCommand::AddItem(0x210000, 3)
    })))).await.unwrap();

    confirm_trade(&mut ship, ClientId(1), ClientId(2)).await;
    finalconfirm_trade(&mut ship, ClientId(1), ClientId(2)).await;

    let titems = TradeItemBuilder::default()
        .stacked(&p1_items.items[0], 0x10000, 2)
        .build();
    let ack = ship.handle(ClientId(1), RecvShipPacket::ItemsToTrade(ItemsToTrade {
        trade_target: 1,
        unknown2: 0,
        count: 1,
        items: titems,
    })).await.unwrap();
    assert_eq!(ack.len(), 0);

    let titems = TradeItemBuilder::default()
        .stacked(&p2_items.items[0], 0x210000, 3)
        .build();
    let ack = ship.handle(ClientId(2), RecvShipPacket::ItemsToTrade(ItemsToTrade {
        trade_target: 0,
        unknown2: 0,
        count: 1,
        items: titems,
    })).await.unwrap();
    assert_eq!(ack.len(), 2);
    assert!(matches!(ack[0], (ClientId(2), SendShipPacket::AcknowledgeTrade(AcknowledgeTrade {}))));
    assert!(matches!(ack[1], (ClientId(1), SendShipPacket::AcknowledgeTrade(AcknowledgeTrade {}))));

    let ack = ship.handle(ClientId(1), RecvShipPacket::TradeConfirmed(TradeConfirmed {
    })).await.unwrap();
    assert_eq!(ack.len(), 0);

    let ack = ship.handle(ClientId(2), RecvShipPacket::TradeConfirmed(TradeConfirmed {
    })).await.unwrap();
    assert_eq!(ack.len(), 8);
    assert!(matches!(ack[0], (ClientId(1), SendShipPacket::Message(Message {
        msg: GameMessage::PlayerNoLongerHasItem(PlayerNoLongerHasItem {
            client: 1,
            item_id: 0x210000,
            ..
        }),
        ..
    }))));
    assert!(matches!(ack[1], (ClientId(2), SendShipPacket::Message(Message {
        msg: GameMessage::PlayerNoLongerHasItem(PlayerNoLongerHasItem {
            client: 0,
            item_id: 0x10000,
            ..
        }),
        ..
    }))));
    assert!(matches!(ack[2], (ClientId(1), SendShipPacket::Message(Message {
        msg: GameMessage::CreateItem(CreateItem {
            client: 1,
            item_id: 0x810002,
            ..
        }),
        ..
    }))));
    assert!(matches!(ack[3], (ClientId(2), SendShipPacket::Message(Message {
        msg: GameMessage::CreateItem(CreateItem {
            client: 1,
            item_id: 0x810002,
            ..
        }),
        ..
    }))));
    assert!(matches!(ack[4], (ClientId(1), SendShipPacket::Message(Message {
        msg: GameMessage::CreateItem(CreateItem {
            client: 0,
            item_id: 0x810001,
            ..
        }),
        ..
    }))));
    assert!(matches!(ack[5], (ClientId(2), SendShipPacket::Message(Message {
        msg: GameMessage::CreateItem(CreateItem {
            client: 0,
            item_id: 0x810001,
            ..
        }),
        ..
    }))));
    assert!(matches!(ack[6], (ClientId(2), SendShipPacket::TradeSuccessful {..})));
    assert!(matches!(ack[7], (ClientId(1), SendShipPacket::TradeSuccessful {..})));

    let p1_items = entity_gateway.get_character_inventory(&char1.id).await.unwrap();
    assert_eq!(p1_items.items.len(), 1);
    assert_eq!(p1_items.items[0].with_stacked(|i| i.clone()).unwrap().len(), 3);
    assert!(matches!(p1_items.items[0].with_stacked(|i| i.clone()).unwrap()[0], item::ItemEntity{item: item::ItemDetail::Tool(item::tool::Tool {tool: item::tool::ToolType::Monofluid, ..}), ..}));
    let p2_items = entity_gateway.get_character_inventory(&char2.id).await.unwrap();
    assert_eq!(p2_items.items.len(), 1);
    assert_eq!(p2_items.items[0].with_stacked(|i| i.clone()).unwrap().len(), 2);
    assert!(matches!(p2_items.items[0].with_stacked(|i| i.clone()).unwrap()[0], item::ItemEntity{item: item::ItemDetail::Tool(item::tool::Tool {tool: item::tool::ToolType::Monomate, ..}), ..}));
}

#[async_std::test]
async fn test_trade_partial_stack_both() {
    let mut entity_gateway = InMemoryGateway::default();

    let (_user1, char1) = new_user_character(&mut entity_gateway, "a1", "a", 1).await;
    let (_user2, char2) = new_user_character(&mut entity_gateway, "a2", "a", 1).await;

    let p1_stack = futures::future::join_all((0..2).map(|_| {
        let mut entity_gateway = entity_gateway.clone();
        async move {
            entity_gateway.create_item(
                item::NewItemEntity {
                    item: item::ItemDetail::Tool(
                        item::tool::Tool {
                            tool: item::tool::ToolType::Monomate,
                        }
                    )
                }).await
        }}))
        .await
        .into_iter()
        .collect::<Result<Vec<ItemEntity>,_>>()
        .unwrap();

    let p2_stack = futures::future::join_all((0..3).map(|_| {
        let mut entity_gateway = entity_gateway.clone();
        async move {
            entity_gateway.create_item(
                item::NewItemEntity {
                    item: item::ItemDetail::Tool(
                        item::tool::Tool {
                            tool: item::tool::ToolType::Monofluid,
                        }
                    )
                }).await
        }}))
        .await
        .into_iter()
        .collect::<Result<Vec<ItemEntity>,_>>()
        .unwrap();

    entity_gateway.set_character_inventory(&char1.id, &item::InventoryEntity::new(vec![p1_stack])).await.unwrap();
    entity_gateway.set_character_inventory(&char2.id, &item::InventoryEntity::new(vec![p2_stack])).await.unwrap();

    let mut ship = Box::new(ShipServerState::builder()
        .gateway(entity_gateway.clone())
        .build());
    log_in_char(&mut ship, ClientId(1), "a1", "a").await;
    log_in_char(&mut ship, ClientId(2), "a2", "a").await;

    join_lobby(&mut ship, ClientId(1)).await;
    join_lobby(&mut ship, ClientId(2)).await;

    create_room(&mut ship, ClientId(1), "room", "").await;
    join_room(&mut ship, ClientId(2), 0).await;

    let p1_items = entity_gateway.get_character_inventory(&char1.id).await.unwrap();
    assert_eq!(p1_items.items.len(), 1);
    let p2_items = entity_gateway.get_character_inventory(&char2.id).await.unwrap();
    assert_eq!(p2_items.items.len(), 1);

    initialize_trade(&mut ship, ClientId(1), ClientId(2)).await;

    ship.handle(ClientId(1), RecvShipPacket::DirectMessage(DirectMessage::new(1, GameMessage::TradeRequest(TradeRequest {
        client: 1,
        target: 0,
        trade: TradeRequestCommand::AddItem(0x10000, 1)
    })))).await.unwrap();

    ship.handle(ClientId(2), RecvShipPacket::DirectMessage(DirectMessage::new(0, GameMessage::TradeRequest(TradeRequest {
        client: 0,
        target: 0,
        trade: TradeRequestCommand::AddItem(0x210000, 2)
    })))).await.unwrap();

    confirm_trade(&mut ship, ClientId(1), ClientId(2)).await;
    finalconfirm_trade(&mut ship, ClientId(1), ClientId(2)).await;

    let titems = TradeItemBuilder::default()
        .stacked(&p1_items.items[0], 0x10000, 1)
        .build();
    let ack = ship.handle(ClientId(1), RecvShipPacket::ItemsToTrade(ItemsToTrade {
        trade_target: 1,
        unknown2: 0,
        count: 1,
        items: titems,
    })).await.unwrap();
    assert_eq!(ack.len(), 0);

    let titems = TradeItemBuilder::default()
        .stacked(&p2_items.items[0], 0x210000, 2)
        .build();
    let ack = ship.handle(ClientId(2), RecvShipPacket::ItemsToTrade(ItemsToTrade {
        trade_target: 0,
        unknown2: 0,
        count: 1,
        items: titems,
    })).await.unwrap();
    assert_eq!(ack.len(), 2);
    assert!(matches!(ack[0], (ClientId(2), SendShipPacket::AcknowledgeTrade(AcknowledgeTrade {}))));
    assert!(matches!(ack[1], (ClientId(1), SendShipPacket::AcknowledgeTrade(AcknowledgeTrade {}))));

    let ack = ship.handle(ClientId(1), RecvShipPacket::TradeConfirmed(TradeConfirmed {
    })).await.unwrap();
    assert_eq!(ack.len(), 0);

    let ack = ship.handle(ClientId(2), RecvShipPacket::TradeConfirmed(TradeConfirmed {
    })).await.unwrap();
    assert_eq!(ack.len(), 8);
    assert!(matches!(ack[0], (ClientId(1), SendShipPacket::Message(Message {
        msg: GameMessage::PlayerNoLongerHasItem(PlayerNoLongerHasItem {
            client: 1,
            item_id: 0x210000,
            amount: 2,
            ..
        }),
        ..
    }))));
    assert!(matches!(ack[1], (ClientId(2), SendShipPacket::Message(Message {
        msg: GameMessage::PlayerNoLongerHasItem(PlayerNoLongerHasItem {
            client: 0,
            item_id: 0x10000,
            amount: 1,
            ..
        }),
        ..
    }))));
    assert!(matches!(ack[2], (ClientId(1), SendShipPacket::Message(Message {
        msg: GameMessage::CreateItem(CreateItem {
            client: 1,
            item_id: 0x810002,
            ..
        }),
        ..
    }))));
    assert!(matches!(ack[3], (ClientId(2), SendShipPacket::Message(Message {
        msg: GameMessage::CreateItem(CreateItem {
            client: 1,
            item_id: 0x810002,
            ..
        }),
        ..
    }))));
    assert!(matches!(ack[4], (ClientId(1), SendShipPacket::Message(Message {
        msg: GameMessage::CreateItem(CreateItem {
            client: 0,
            item_id: 0x810001,
            ..
        }),
        ..
    }))));
    assert!(matches!(ack[5], (ClientId(2), SendShipPacket::Message(Message {
        msg: GameMessage::CreateItem(CreateItem {
            client: 0,
            item_id: 0x810001,
            ..
        }),
        ..
    }))));
    assert!(matches!(ack[6], (ClientId(2), SendShipPacket::TradeSuccessful {..})));
    assert!(matches!(ack[7], (ClientId(1), SendShipPacket::TradeSuccessful {..})));

    let p1_items = entity_gateway.get_character_inventory(&char1.id).await.unwrap();
    assert_eq!(p1_items.items.len(), 2);
    assert_eq!(p1_items.items[0].with_stacked(|i| i.clone()).unwrap().len(), 1);
    assert_eq!(p1_items.items[1].with_stacked(|i| i.clone()).unwrap().len(), 2);
    assert!(matches!(p1_items.items[0].with_stacked(|i| i.clone()).unwrap()[0], item::ItemEntity{item: item::ItemDetail::Tool(item::tool::Tool {tool: item::tool::ToolType::Monomate, ..}), ..}));
    assert!(matches!(p1_items.items[1].with_stacked(|i| i.clone()).unwrap()[0], item::ItemEntity{item: item::ItemDetail::Tool(item::tool::Tool {tool: item::tool::ToolType::Monofluid, ..}), ..}));
    let p2_items = entity_gateway.get_character_inventory(&char2.id).await.unwrap();
    assert_eq!(p2_items.items.len(), 2);
    assert_eq!(p2_items.items[0].with_stacked(|i| i.clone()).unwrap().len(), 1);
    assert_eq!(p2_items.items[1].with_stacked(|i| i.clone()).unwrap().len(), 1);
    assert!(matches!(p2_items.items[0].with_stacked(|i| i.clone()).unwrap()[0], item::ItemEntity{item: item::ItemDetail::Tool(item::tool::Tool {tool: item::tool::ToolType::Monofluid, ..}), ..}));
    assert!(matches!(p2_items.items[1].with_stacked(|i| i.clone()).unwrap()[0], item::ItemEntity{item: item::ItemDetail::Tool(item::tool::Tool {tool: item::tool::ToolType::Monomate, ..}), ..}));
}

#[async_std::test]
async fn test_trade_same_stacked_item_to_eachother() {
    let mut entity_gateway = InMemoryGateway::default();

    let (_user1, char1) = new_user_character(&mut entity_gateway, "a1", "a", 1).await;
    let (_user2, char2) = new_user_character(&mut entity_gateway, "a2", "a", 1).await;

    let p1_stack = futures::future::join_all((0..3).map(|_| {
        let mut entity_gateway = entity_gateway.clone();
        async move {
            entity_gateway.create_item(
                item::NewItemEntity {
                    item: item::ItemDetail::Tool(
                        item::tool::Tool {
                            tool: item::tool::ToolType::Monomate,
                        }
                    )
                }).await
        }}))
        .await
        .into_iter()
        .collect::<Result<Vec<ItemEntity>,_>>()
        .unwrap();

    let p2_stack = futures::future::join_all((0..4).map(|_| {
        let mut entity_gateway = entity_gateway.clone();
        async move {
            entity_gateway.create_item(
                item::NewItemEntity {
                    item: item::ItemDetail::Tool(
                        item::tool::Tool {
                            tool: item::tool::ToolType::Monomate,
                        }
                    )
                }).await
        }}))
        .await
        .into_iter()
        .collect::<Result<Vec<ItemEntity>,_>>()
        .unwrap();

    entity_gateway.set_character_inventory(&char1.id, &item::InventoryEntity::new(vec![p1_stack])).await.unwrap();
    entity_gateway.set_character_inventory(&char2.id, &item::InventoryEntity::new(vec![p2_stack])).await.unwrap();

    let mut ship = Box::new(ShipServerState::builder()
        .gateway(entity_gateway.clone())
        .build());
    log_in_char(&mut ship, ClientId(1), "a1", "a").await;
    log_in_char(&mut ship, ClientId(2), "a2", "a").await;

    join_lobby(&mut ship, ClientId(1)).await;
    join_lobby(&mut ship, ClientId(2)).await;

    create_room(&mut ship, ClientId(1), "room", "").await;
    join_room(&mut ship, ClientId(2), 0).await;

    let p1_items = entity_gateway.get_character_inventory(&char1.id).await.unwrap();
    assert_eq!(p1_items.items.len(), 1);
    let p2_items = entity_gateway.get_character_inventory(&char2.id).await.unwrap();
    assert_eq!(p2_items.items.len(), 1);

    initialize_trade(&mut ship, ClientId(1), ClientId(2)).await;

    ship.handle(ClientId(1), RecvShipPacket::DirectMessage(DirectMessage::new(1, GameMessage::TradeRequest(TradeRequest {
        client: 1,
        target: 0,
        trade: TradeRequestCommand::AddItem(0x10000, 1)
    })))).await.unwrap();

    ship.handle(ClientId(2), RecvShipPacket::DirectMessage(DirectMessage::new(0, GameMessage::TradeRequest(TradeRequest {
        client: 0,
        target: 0,
        trade: TradeRequestCommand::AddItem(0x210000, 3)
    })))).await.unwrap();

    confirm_trade(&mut ship, ClientId(1), ClientId(2)).await;
    finalconfirm_trade(&mut ship, ClientId(1), ClientId(2)).await;

    let titems = TradeItemBuilder::default()
        .stacked(&p1_items.items[0], 0x10000, 1)
        .build();
    let ack = ship.handle(ClientId(1), RecvShipPacket::ItemsToTrade(ItemsToTrade {
        trade_target: 1,
        unknown2: 0,
        count: 1,
        items: titems,
    })).await.unwrap();
    assert_eq!(ack.len(), 0);

    let titems = TradeItemBuilder::default()
        .stacked(&p2_items.items[0], 0x210000, 3)
        .build();
    let ack = ship.handle(ClientId(2), RecvShipPacket::ItemsToTrade(ItemsToTrade {
        trade_target: 0,
        unknown2: 0,
        count: 1,
        items: titems,
    })).await.unwrap();
    assert_eq!(ack.len(), 2);
    assert!(matches!(ack[0], (ClientId(2), SendShipPacket::AcknowledgeTrade(AcknowledgeTrade {}))));
    assert!(matches!(ack[1], (ClientId(1), SendShipPacket::AcknowledgeTrade(AcknowledgeTrade {}))));

    let ack = ship.handle(ClientId(1), RecvShipPacket::TradeConfirmed(TradeConfirmed {
    })).await.unwrap();
    assert_eq!(ack.len(), 0);

    let ack = ship.handle(ClientId(2), RecvShipPacket::TradeConfirmed(TradeConfirmed {
    })).await.unwrap();
    assert_eq!(ack.len(), 8);
    assert!(matches!(ack[0], (ClientId(1), SendShipPacket::Message(Message {
        msg: GameMessage::PlayerNoLongerHasItem(PlayerNoLongerHasItem {
            client: 1,
            item_id: 0x210000,
            amount: 3,
            ..
        }),
        ..
    }))));
    assert!(matches!(ack[1], (ClientId(2), SendShipPacket::Message(Message {
        msg: GameMessage::PlayerNoLongerHasItem(PlayerNoLongerHasItem {
            client: 0,
            item_id: 0x10000,
            amount: 1,
            ..
        }),
        ..
    }))));
    assert!(matches!(ack[2], (ClientId(1), SendShipPacket::Message(Message {
        msg: GameMessage::CreateItem(CreateItem {
            client: 1,
            item_id: 0x810002,
            ..
        }),
        ..
    }))));
    assert!(matches!(ack[3], (ClientId(2), SendShipPacket::Message(Message {
        msg: GameMessage::CreateItem(CreateItem {
            client: 1,
            item_id: 0x810002,
            ..
        }),
        ..
    }))));
    assert!(matches!(ack[4], (ClientId(1), SendShipPacket::Message(Message {
        msg: GameMessage::CreateItem(CreateItem {
            client: 0,
            item_id: 0x810001,
            ..
        }),
        ..
    }))));
    assert!(matches!(ack[5], (ClientId(2), SendShipPacket::Message(Message {
        msg: GameMessage::CreateItem(CreateItem {
            client: 0,
            item_id: 0x810001,
            ..
        }),
        ..
    }))));
    assert!(matches!(ack[6], (ClientId(2), SendShipPacket::TradeSuccessful {..})));
    assert!(matches!(ack[7], (ClientId(1), SendShipPacket::TradeSuccessful {..})));

    let p1_items = entity_gateway.get_character_inventory(&char1.id).await.unwrap();
    assert_eq!(p1_items.items.len(), 1);
    assert_eq!(p1_items.items[0].with_stacked(|i| i.clone()).unwrap().len(), 5);
    assert!(matches!(p1_items.items[0].with_stacked(|i| i.clone()).unwrap()[0], item::ItemEntity{item: item::ItemDetail::Tool(item::tool::Tool {tool: item::tool::ToolType::Monomate, ..}), ..}));
    let p2_items = entity_gateway.get_character_inventory(&char2.id).await.unwrap();
    assert_eq!(p2_items.items.len(), 1);
    assert_eq!(p2_items.items[0].with_stacked(|i| i.clone()).unwrap().len(), 2);
    assert!(matches!(p2_items.items[0].with_stacked(|i| i.clone()).unwrap()[0], item::ItemEntity{item: item::ItemDetail::Tool(item::tool::Tool {tool: item::tool::ToolType::Monomate, ..}), ..}));
}

#[async_std::test]
async fn test_trade_stacked_when_already_have_partial_stack() {
    let mut entity_gateway = InMemoryGateway::default();

    let (_user1, char1) = new_user_character(&mut entity_gateway, "a1", "a", 1).await;
    let (_user2, char2) = new_user_character(&mut entity_gateway, "a2", "a", 1).await;

    let p1_stack = futures::future::join_all((0..3).map(|_| {
        let mut entity_gateway = entity_gateway.clone();
        async move {
            entity_gateway.create_item(
                item::NewItemEntity {
                    item: item::ItemDetail::Tool(
                        item::tool::Tool {
                            tool: item::tool::ToolType::Monomate,
                        }
                    )
                }).await
        }}))
        .await
        .into_iter()
        .collect::<Result<Vec<ItemEntity>,_>>()
        .unwrap();

    let p2_stack = futures::future::join_all((0..3).map(|_| {
        let mut entity_gateway = entity_gateway.clone();
        async move {
            entity_gateway.create_item(
                item::NewItemEntity {
                    item: item::ItemDetail::Tool(
                        item::tool::Tool {
                            tool: item::tool::ToolType::Monomate,
                        }
                    )
                }).await
        }}))
        .await
        .into_iter()
        .collect::<Result<Vec<ItemEntity>,_>>()
        .unwrap();

    entity_gateway.set_character_inventory(&char1.id, &item::InventoryEntity::new(vec![p1_stack])).await.unwrap();
    entity_gateway.set_character_inventory(&char2.id, &item::InventoryEntity::new(vec![p2_stack])).await.unwrap();

    let mut ship = Box::new(ShipServerState::builder()
        .gateway(entity_gateway.clone())
        .build());
    log_in_char(&mut ship, ClientId(1), "a1", "a").await;
    log_in_char(&mut ship, ClientId(2), "a2", "a").await;

    join_lobby(&mut ship, ClientId(1)).await;
    join_lobby(&mut ship, ClientId(2)).await;

    create_room(&mut ship, ClientId(1), "room", "").await;
    join_room(&mut ship, ClientId(2), 0).await;

    let p1_items = entity_gateway.get_character_inventory(&char1.id).await.unwrap();
    assert_eq!(p1_items.items.len(), 1);
    let p2_items = entity_gateway.get_character_inventory(&char2.id).await.unwrap();
    assert_eq!(p2_items.items.len(), 1);

    initialize_trade(&mut ship, ClientId(1), ClientId(2)).await;

    ship.handle(ClientId(1), RecvShipPacket::DirectMessage(DirectMessage::new(1, GameMessage::TradeRequest(TradeRequest {
        client: 1,
        target: 0,
        trade: TradeRequestCommand::AddItem(0x10000, 2)
    })))).await.unwrap();

    confirm_trade(&mut ship, ClientId(1), ClientId(2)).await;
    finalconfirm_trade(&mut ship, ClientId(1), ClientId(2)).await;

    let titems = TradeItemBuilder::default()
        .stacked(&p1_items.items[0], 0x10000, 2)
        .build();
    let ack = ship.handle(ClientId(1), RecvShipPacket::ItemsToTrade(ItemsToTrade {
        trade_target: 1,
        unknown2: 0,
        count: 1,
        items: titems,
    })).await.unwrap();
    assert_eq!(ack.len(), 0);

    let ack = ship.handle(ClientId(2), RecvShipPacket::ItemsToTrade(ItemsToTrade {
        trade_target: 0,
        unknown2: 0,
        count: 0,
        items: Default::default(),
    })).await.unwrap();
    assert_eq!(ack.len(), 2);
    assert!(matches!(ack[0], (ClientId(2), SendShipPacket::AcknowledgeTrade(AcknowledgeTrade {}))));
    assert!(matches!(ack[1], (ClientId(1), SendShipPacket::AcknowledgeTrade(AcknowledgeTrade {}))));

    let ack = ship.handle(ClientId(1), RecvShipPacket::TradeConfirmed(TradeConfirmed {
    })).await.unwrap();
    assert_eq!(ack.len(), 0);

    let ack = ship.handle(ClientId(2), RecvShipPacket::TradeConfirmed(TradeConfirmed {
    })).await.unwrap();
    assert_eq!(ack.len(), 5);
    assert!(matches!(ack[0], (ClientId(2), SendShipPacket::Message(Message {
        msg: GameMessage::PlayerNoLongerHasItem(PlayerNoLongerHasItem {
            client: 0,
            item_id: 0x10000,
            amount: 2,
            ..
        }),
        ..
    }))));
    assert!(matches!(ack[1], (ClientId(1), SendShipPacket::Message(Message {
        msg: GameMessage::CreateItem(CreateItem {
            client: 1,
            item_id: 0x810001,
            item_data: [3, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0],
            ..
        }),
        ..
    }))));
    assert!(matches!(ack[2], (ClientId(2), SendShipPacket::Message(Message {
        msg: GameMessage::CreateItem(CreateItem {
            client: 1,
            item_id: 0x810001,
            item_data: [3, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0],
            ..
        }),
        ..
    }))));
    assert!(matches!(ack[3], (ClientId(2), SendShipPacket::TradeSuccessful {..})));
    assert!(matches!(ack[4], (ClientId(1), SendShipPacket::TradeSuccessful {..})));

    let p1_items = entity_gateway.get_character_inventory(&char1.id).await.unwrap();
    assert_eq!(p1_items.items.len(), 1);
    assert_eq!(p1_items.items[0].with_stacked(|i| i.clone()).unwrap().len(), 1);
    assert!(matches!(p1_items.items[0].with_stacked(|i| i.clone()).unwrap()[0], item::ItemEntity{item: item::ItemDetail::Tool(item::tool::Tool {tool: item::tool::ToolType::Monomate, ..}), ..}));
    let p2_items = entity_gateway.get_character_inventory(&char2.id).await.unwrap();
    assert_eq!(p2_items.items.len(), 1);
    assert_eq!(p2_items.items[0].with_stacked(|i| i.clone()).unwrap().len(), 5);
    assert!(matches!(p2_items.items[0].with_stacked(|i| i.clone()).unwrap()[0], item::ItemEntity{item: item::ItemDetail::Tool(item::tool::Tool {tool: item::tool::ToolType::Monomate, ..}), ..}));
}

#[async_std::test]
async fn test_trade_individual_for_stacked() {
    let mut entity_gateway = InMemoryGateway::default();

    let (_user1, char1) = new_user_character(&mut entity_gateway, "a1", "a", 1).await;
    let (_user2, char2) = new_user_character(&mut entity_gateway, "a2", "a", 1).await;

    let p1_inv = vec![
        entity_gateway.create_item(
            item::NewItemEntity {
                item: item::ItemDetail::Weapon(
                    item::weapon::Weapon {
                        weapon: item::weapon::WeaponType::Saber,
                        grind: 0,
                        special: None,
                        attrs: [None, None, None],
                        tekked: true,
                    }
                ),
            }).await.unwrap()];

    let p2_stack = futures::future::join_all((0..2).map(|_| {
        let mut entity_gateway = entity_gateway.clone();
        async move {
            entity_gateway.create_item(
                item::NewItemEntity {
                    item: item::ItemDetail::Tool(
                        item::tool::Tool {
                            tool: item::tool::ToolType::Monomate,
                        }
                    )
                }).await
        }}))
        .await
        .into_iter()
        .collect::<Result<Vec<ItemEntity>,_>>()
        .unwrap();

    entity_gateway.set_character_inventory(&char1.id, &item::InventoryEntity::new(p1_inv)).await.unwrap();
    entity_gateway.set_character_inventory(&char2.id, &item::InventoryEntity::new(vec![p2_stack])).await.unwrap();

    let mut ship = Box::new(ShipServerState::builder()
        .gateway(entity_gateway.clone())
        .build());
    log_in_char(&mut ship, ClientId(1), "a1", "a").await;
    log_in_char(&mut ship, ClientId(2), "a2", "a").await;

    join_lobby(&mut ship, ClientId(1)).await;
    join_lobby(&mut ship, ClientId(2)).await;

    create_room(&mut ship, ClientId(1), "room", "").await;
    join_room(&mut ship, ClientId(2), 0).await;

    let p1_items = entity_gateway.get_character_inventory(&char1.id).await.unwrap();
    assert_eq!(p1_items.items.len(), 1);
    let p2_items = entity_gateway.get_character_inventory(&char2.id).await.unwrap();
    assert_eq!(p2_items.items.len(), 1);

    initialize_trade(&mut ship, ClientId(1), ClientId(2)).await;

    ship.handle(ClientId(1), RecvShipPacket::DirectMessage(DirectMessage::new(1, GameMessage::TradeRequest(TradeRequest {
        client: 1,
        target: 0,
        trade: TradeRequestCommand::AddItem(0x10000, 1)
    })))).await.unwrap();

    ship.handle(ClientId(2), RecvShipPacket::DirectMessage(DirectMessage::new(0, GameMessage::TradeRequest(TradeRequest {
        client: 0,
        target: 0,
        trade: TradeRequestCommand::AddItem(0x210000, 2)
    })))).await.unwrap();

    confirm_trade(&mut ship, ClientId(1), ClientId(2)).await;
    finalconfirm_trade(&mut ship, ClientId(1), ClientId(2)).await;

    let titems = TradeItemBuilder::default()
        .individual(&p1_items.items[0], 0x10000)
        .build();
    let ack = ship.handle(ClientId(1), RecvShipPacket::ItemsToTrade(ItemsToTrade {
        trade_target: 1,
        unknown2: 0,
        count: 1,
        items: titems,
    })).await.unwrap();
    assert_eq!(ack.len(), 0);

    let titems = TradeItemBuilder::default()
        .stacked(&p2_items.items[0], 0x210000, 2)
        .build();
    let ack = ship.handle(ClientId(2), RecvShipPacket::ItemsToTrade(ItemsToTrade {
        trade_target: 0,
        unknown2: 0,
        count: 1,
        items: titems,
    })).await.unwrap();
    assert_eq!(ack.len(), 2);
    assert!(matches!(ack[0], (ClientId(2), SendShipPacket::AcknowledgeTrade(AcknowledgeTrade {}))));
    assert!(matches!(ack[1], (ClientId(1), SendShipPacket::AcknowledgeTrade(AcknowledgeTrade {}))));

    let ack = ship.handle(ClientId(1), RecvShipPacket::TradeConfirmed(TradeConfirmed {
    })).await.unwrap();
    assert_eq!(ack.len(), 0);

    let ack = ship.handle(ClientId(2), RecvShipPacket::TradeConfirmed(TradeConfirmed {
    })).await.unwrap();
    assert_eq!(ack.len(), 8);
    assert!(matches!(ack[0], (ClientId(1), SendShipPacket::Message(Message {
        msg: GameMessage::PlayerNoLongerHasItem(PlayerNoLongerHasItem {
            client: 1,
            item_id: 0x210000,
            ..
        }),
        ..
    }))));
    assert!(matches!(ack[1], (ClientId(2), SendShipPacket::Message(Message {
        msg: GameMessage::PlayerNoLongerHasItem(PlayerNoLongerHasItem {
            client: 0,
            item_id: 0x10000,
            ..
        }),
        ..
    }))));
    assert!(matches!(ack[2], (ClientId(1), SendShipPacket::Message(Message {
        msg: GameMessage::CreateItem(CreateItem {
            client: 1,
            item_data: [0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
            item_id: 0x810002,
            ..
        }),
        ..
    }))));
    assert!(matches!(ack[3], (ClientId(2), SendShipPacket::Message(Message {
        msg: GameMessage::CreateItem(CreateItem {
            client: 1,
            item_data: [0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
            item_id: 0x810002,
            ..
        }),
        ..
    }))));
    assert!(matches!(ack[4], (ClientId(1), SendShipPacket::Message(Message {
        msg: GameMessage::CreateItem(CreateItem {
            client: 0,
            item_data: [3, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0],
            item_id: 0x810001,
            ..
        }),
        ..
    }))));
    assert!(matches!(ack[5], (ClientId(2), SendShipPacket::Message(Message {
        msg: GameMessage::CreateItem(CreateItem {
            client: 0,
            item_data: [3, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0],
            item_id: 0x810001,
            ..
        }),
        ..
    }))));
    assert!(matches!(ack[6], (ClientId(2), SendShipPacket::TradeSuccessful {..})));
    assert!(matches!(ack[7], (ClientId(1), SendShipPacket::TradeSuccessful {..})));

    let p1_items = entity_gateway.get_character_inventory(&char1.id).await.unwrap();
    assert_eq!(p1_items.items.len(), 1);
    assert_eq!(p1_items.items[0].with_stacked(|i| i.clone()).unwrap().len(), 2);
    assert!(matches!(p1_items.items[0].with_stacked(|i| i.clone()).unwrap()[0], item::ItemEntity{item: item::ItemDetail::Tool(item::tool::Tool {tool: item::tool::ToolType::Monomate, ..}), ..}));
    let p2_items = entity_gateway.get_character_inventory(&char2.id).await.unwrap();
    assert_eq!(p2_items.items.len(), 1);
    assert!(matches!(p2_items.items[0].with_individual(|i| i.clone()).unwrap(), item::ItemEntity{item: item::ItemDetail::Weapon(item::weapon::Weapon {weapon: item::weapon::WeaponType::Saber, ..}), ..}));
}

#[async_std::test]
async fn test_trade_multiple_individual() {
    let mut entity_gateway = InMemoryGateway::default();

    let (_user1, char1) = new_user_character(&mut entity_gateway, "a1", "a", 1).await;
    let (_user2, char2) = new_user_character(&mut entity_gateway, "a2", "a", 1).await;

    let p1_inv = vec![
        entity_gateway.create_item(
            item::NewItemEntity {
                item: item::ItemDetail::Weapon(
                    item::weapon::Weapon {
                        weapon: item::weapon::WeaponType::Saber,
                        grind: 0,
                        special: None,
                        attrs: [None, None, None],
                        tekked: true,
                    }
                ),
            }).await.unwrap(),
        entity_gateway.create_item(
            item::NewItemEntity {
                item: item::ItemDetail::Weapon(
                    item::weapon::Weapon {
                        weapon: item::weapon::WeaponType::Buster,
                        grind: 0,
                        special: None,
                        attrs: [None, None, None],
                        tekked: true,
                    }
                ),
            }).await.unwrap(),
    ];
    let p2_inv = vec![
        entity_gateway.create_item(
            item::NewItemEntity {
                item: item::ItemDetail::Weapon(
                    item::weapon::Weapon {
                        weapon: item::weapon::WeaponType::Handgun,
                        grind: 0,
                        special: None,
                        attrs: [None, None, None],
                        tekked: true,
                    }
                ),
            }).await.unwrap(),
        entity_gateway.create_item(
            item::NewItemEntity {
                item: item::ItemDetail::Weapon(
                    item::weapon::Weapon {
                        weapon: item::weapon::WeaponType::Autogun,
                        grind: 0,
                        special: None,
                        attrs: [None, None, None],
                        tekked: true,
                    }
                ),
            }).await.unwrap(),
    ];

    entity_gateway.set_character_inventory(&char1.id, &item::InventoryEntity::new(p1_inv)).await.unwrap();
    entity_gateway.set_character_inventory(&char2.id, &item::InventoryEntity::new(p2_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;
    log_in_char(&mut ship, ClientId(2), "a2", "a").await;

    join_lobby(&mut ship, ClientId(1)).await;
    join_lobby(&mut ship, ClientId(2)).await;

    create_room(&mut ship, ClientId(1), "room", "").await;
    join_room(&mut ship, ClientId(2), 0).await;

    let p1_items = entity_gateway.get_character_inventory(&char1.id).await.unwrap();
    assert_eq!(p1_items.items.len(), 2);
    let p2_items = entity_gateway.get_character_inventory(&char2.id).await.unwrap();
    assert_eq!(p2_items.items.len(), 2);

    initialize_trade(&mut ship, ClientId(1), ClientId(2)).await;

    ship.handle(ClientId(1), RecvShipPacket::DirectMessage(DirectMessage::new(1, GameMessage::TradeRequest(TradeRequest {
        client: 1,
        target: 0,
        trade: TradeRequestCommand::AddItem(0x10000, 1)
    })))).await.unwrap();
    ship.handle(ClientId(1), RecvShipPacket::DirectMessage(DirectMessage::new(1, GameMessage::TradeRequest(TradeRequest {
        client: 1,
        target: 0,
        trade: TradeRequestCommand::AddItem(0x10001, 1)
    })))).await.unwrap();

    ship.handle(ClientId(2), RecvShipPacket::DirectMessage(DirectMessage::new(0, GameMessage::TradeRequest(TradeRequest {
        client: 0,
        target: 0,
        trade: TradeRequestCommand::AddItem(0x210000, 1)
    })))).await.unwrap();
    ship.handle(ClientId(2), RecvShipPacket::DirectMessage(DirectMessage::new(0, GameMessage::TradeRequest(TradeRequest {
        client: 0,
        target: 0,
        trade: TradeRequestCommand::AddItem(0x210001, 1)
    })))).await.unwrap();

    confirm_trade(&mut ship, ClientId(1), ClientId(2)).await;
    finalconfirm_trade(&mut ship, ClientId(1), ClientId(2)).await;

    let titems = TradeItemBuilder::default()
        .individual(&p1_items.items[0], 0x10000)
        .individual(&p1_items.items[1], 0x10001)
        .build();
    let ack = ship.handle(ClientId(1), RecvShipPacket::ItemsToTrade(ItemsToTrade {
        trade_target: 1,
        unknown2: 0,
        count: 2,
        items: titems,
    })).await.unwrap();
    assert_eq!(ack.len(), 0);

    let titems = TradeItemBuilder::default()
        .individual(&p2_items.items[0], 0x210000)
        .individual(&p2_items.items[1], 0x210001)
        .build();
    let ack = ship.handle(ClientId(2), RecvShipPacket::ItemsToTrade(ItemsToTrade {
        trade_target: 0,
        unknown2: 0,
        count: 2,
        items: titems,
    })).await.unwrap();
    assert_eq!(ack.len(), 2);
    assert!(matches!(ack[0], (ClientId(2), SendShipPacket::AcknowledgeTrade(AcknowledgeTrade {}))));
    assert!(matches!(ack[1], (ClientId(1), SendShipPacket::AcknowledgeTrade(AcknowledgeTrade {}))));

    let ack = ship.handle(ClientId(1), RecvShipPacket::TradeConfirmed(TradeConfirmed {
    })).await.unwrap();
    assert_eq!(ack.len(), 0);

    let ack = ship.handle(ClientId(2), RecvShipPacket::TradeConfirmed(TradeConfirmed {
    })).await.unwrap();
    assert_eq!(ack.len(), 14);
    assert!(matches!(ack[0], (ClientId(1), SendShipPacket::Message(Message {
        msg: GameMessage::PlayerNoLongerHasItem(PlayerNoLongerHasItem {
            client: 1,
            item_id: 0x210000,
            ..
        }),
        ..
    }))));
    assert!(matches!(ack[1], (ClientId(1), SendShipPacket::Message(Message {
        msg: GameMessage::PlayerNoLongerHasItem(PlayerNoLongerHasItem {
            client: 1,
            item_id: 0x210001,
            ..
        }),
        ..
    }))));
    assert!(matches!(ack[2], (ClientId(2), SendShipPacket::Message(Message {
        msg: GameMessage::PlayerNoLongerHasItem(PlayerNoLongerHasItem {
            client: 0,
            item_id: 0x10000,
            ..
        }),
        ..
    }))));
    assert!(matches!(ack[3], (ClientId(2), SendShipPacket::Message(Message {
        msg: GameMessage::PlayerNoLongerHasItem(PlayerNoLongerHasItem {
            client: 0,
            item_id: 0x10001,
            ..
        }),
        ..
    }))));
    assert!(matches!(ack[4], (ClientId(1), SendShipPacket::Message(Message {
        msg: GameMessage::CreateItem(CreateItem {
            client: 1,
            item_data: [0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], // saber
            item_id: 0x810004,
            ..
        }),
        ..
    }))));
    assert!(matches!(ack[5], (ClientId(2), SendShipPacket::Message(Message {
        msg: GameMessage::CreateItem(CreateItem {
            client: 1,
            item_data: [0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], // saber
            item_id: 0x810004,
            ..
        }),
        ..
    }))));
    assert!(matches!(ack[6], (ClientId(1), SendShipPacket::Message(Message {
        msg: GameMessage::CreateItem(CreateItem {
            client: 1,
            item_data: [0, 1, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0], // saber
            item_id: 0x810003,
            ..
        }),
        ..
    }))));
    assert!(matches!(ack[7], (ClientId(2), SendShipPacket::Message(Message {
        msg: GameMessage::CreateItem(CreateItem {
            client: 1,
            item_data: [0, 1, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0], // saber
            item_id: 0x810003,
            ..
        }),
        ..
    }))));
    assert!(matches!(ack[8], (ClientId(1), SendShipPacket::Message(Message {
        msg: GameMessage::CreateItem(CreateItem {
            client: 0,
            item_data: [0, 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], // handgun
            item_id: 0x810002,
            ..
        }),
        ..
    }))));
    assert!(matches!(ack[9], (ClientId(2), SendShipPacket::Message(Message {
        msg: GameMessage::CreateItem(CreateItem {
            client: 0,
            item_data: [0, 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], // handgun
            item_id: 0x810002,
            ..
        }),
        ..
    }))));
    assert!(matches!(ack[10], (ClientId(1), SendShipPacket::Message(Message {
        msg: GameMessage::CreateItem(CreateItem {
            client: 0,
            item_data: [0, 6, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0], // handgun
            item_id: 0x810001,
            ..
        }),
        ..
    }))));
    assert!(matches!(ack[11], (ClientId(2), SendShipPacket::Message(Message {
        msg: GameMessage::CreateItem(CreateItem {
            client: 0,
            item_data: [0, 6, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0], // handgun
            item_id: 0x810001,
            ..
        }),
        ..
    }))));
    assert!(matches!(ack[12], (ClientId(2), SendShipPacket::TradeSuccessful {..})));
    assert!(matches!(ack[13], (ClientId(1), SendShipPacket::TradeSuccessful {..})));

    let p1_items = entity_gateway.get_character_inventory(&char1.id).await.unwrap();
    assert_eq!(p1_items.items.len(), 2);
    assert!(matches!(p1_items.items[0].with_individual(|i| i.clone()).unwrap(), item::ItemEntity{item: item::ItemDetail::Weapon(item::weapon::Weapon {weapon: item::weapon::WeaponType::Handgun, ..}), ..}));
    assert!(matches!(p1_items.items[1].with_individual(|i| i.clone()).unwrap(), item::ItemEntity{item: item::ItemDetail::Weapon(item::weapon::Weapon {weapon: item::weapon::WeaponType::Autogun, ..}), ..}));
    let p2_items = entity_gateway.get_character_inventory(&char2.id).await.unwrap();
    assert_eq!(p2_items.items.len(), 2);
    assert!(matches!(p2_items.items[0].with_individual(|i| i.clone()).unwrap(), item::ItemEntity{item: item::ItemDetail::Weapon(item::weapon::Weapon {weapon: item::weapon::WeaponType::Saber, ..}), ..}));
    assert!(matches!(p2_items.items[1].with_individual(|i| i.clone()).unwrap(), item::ItemEntity{item: item::ItemDetail::Weapon(item::weapon::Weapon {weapon: item::weapon::WeaponType::Buster, ..}), ..}));
}


#[async_std::test]
async fn test_trade_multiple_stacked() {
    let mut entity_gateway = InMemoryGateway::default();

    let (_user1, char1) = new_user_character(&mut entity_gateway, "a1", "a", 1).await;
    let (_user2, char2) = new_user_character(&mut entity_gateway, "a2", "a", 1).await;

    let p1_stack1 = futures::future::join_all((0..2).map(|_| {
        let mut entity_gateway = entity_gateway.clone();
        async move {
            entity_gateway.create_item(
                item::NewItemEntity {
                    item: item::ItemDetail::Tool(
                        item::tool::Tool {
                            tool: item::tool::ToolType::Monomate,
                        }
                    )
                }).await
        }}))
        .await
        .into_iter()
        .collect::<Result<Vec<ItemEntity>,_>>()
        .unwrap();
    let p1_stack2 = futures::future::join_all((0..2).map(|_| {
        let mut entity_gateway = entity_gateway.clone();
        async move {
            entity_gateway.create_item(
                item::NewItemEntity {
                    item: item::ItemDetail::Tool(
                        item::tool::Tool {
                            tool: item::tool::ToolType::Dimate,
                        }
                    )
                }).await
        }}))
        .await
        .into_iter()
        .collect::<Result<Vec<ItemEntity>,_>>()
        .unwrap();

    let p2_stack1 = futures::future::join_all((0..3).map(|_| {
        let mut entity_gateway = entity_gateway.clone();
        async move {
            entity_gateway.create_item(
                item::NewItemEntity {
                    item: item::ItemDetail::Tool(
                        item::tool::Tool {
                            tool: item::tool::ToolType::Monofluid,
                        }
                    )
                }).await
        }}))
        .await
        .into_iter()
        .collect::<Result<Vec<ItemEntity>,_>>()
        .unwrap();
    let p2_stack2 = futures::future::join_all((0..3).map(|_| {
        let mut entity_gateway = entity_gateway.clone();
        async move {
            entity_gateway.create_item(
                item::NewItemEntity {
                    item: item::ItemDetail::Tool(
                        item::tool::Tool {
                            tool: item::tool::ToolType::Difluid,
                        }
                    )
                }).await
        }}))
        .await
        .into_iter()
        .collect::<Result<Vec<ItemEntity>,_>>()
        .unwrap();

    entity_gateway.set_character_inventory(&char1.id, &item::InventoryEntity::new(vec![p1_stack1, p1_stack2])).await.unwrap();
    entity_gateway.set_character_inventory(&char2.id, &item::InventoryEntity::new(vec![p2_stack1, p2_stack2])).await.unwrap();

    let mut ship = Box::new(ShipServerState::builder()
        .gateway(entity_gateway.clone())
        .build());
    log_in_char(&mut ship, ClientId(1), "a1", "a").await;
    log_in_char(&mut ship, ClientId(2), "a2", "a").await;

    join_lobby(&mut ship, ClientId(1)).await;
    join_lobby(&mut ship, ClientId(2)).await;

    create_room(&mut ship, ClientId(1), "room", "").await;
    join_room(&mut ship, ClientId(2), 0).await;

    let p1_items = entity_gateway.get_character_inventory(&char1.id).await.unwrap();
    assert_eq!(p1_items.items.len(), 2);
    let p2_items = entity_gateway.get_character_inventory(&char2.id).await.unwrap();
    assert_eq!(p2_items.items.len(), 2);

    initialize_trade(&mut ship, ClientId(1), ClientId(2)).await;

    ship.handle(ClientId(1), RecvShipPacket::DirectMessage(DirectMessage::new(1, GameMessage::TradeRequest(TradeRequest {
        client: 1,
        target: 0,
        trade: TradeRequestCommand::AddItem(0x10000, 2)
    })))).await.unwrap();
    ship.handle(ClientId(1), RecvShipPacket::DirectMessage(DirectMessage::new(1, GameMessage::TradeRequest(TradeRequest {
        client: 1,
        target: 0,
        trade: TradeRequestCommand::AddItem(0x10001, 2)
    })))).await.unwrap();

    ship.handle(ClientId(2), RecvShipPacket::DirectMessage(DirectMessage::new(0, GameMessage::TradeRequest(TradeRequest {
        client: 0,
        target: 0,
        trade: TradeRequestCommand::AddItem(0x210000, 3)
    })))).await.unwrap();
    ship.handle(ClientId(2), RecvShipPacket::DirectMessage(DirectMessage::new(0, GameMessage::TradeRequest(TradeRequest {
        client: 0,
        target: 0,
        trade: TradeRequestCommand::AddItem(0x210001, 3)
    })))).await.unwrap();

    confirm_trade(&mut ship, ClientId(1), ClientId(2)).await;
    finalconfirm_trade(&mut ship, ClientId(1), ClientId(2)).await;


    let titems = TradeItemBuilder::default()
        .stacked(&p1_items.items[0], 0x10000, 2)
        .stacked(&p1_items.items[1], 0x10001, 2)
        .build();
    let ack = ship.handle(ClientId(1), RecvShipPacket::ItemsToTrade(ItemsToTrade {
        trade_target: 1,
        unknown2: 0,
        count: 2,
        items: titems,
    })).await.unwrap();
    assert_eq!(ack.len(), 0);

    let titems = TradeItemBuilder::default()
        .stacked(&p2_items.items[0], 0x210000, 3)
        .stacked(&p2_items.items[1], 0x210001, 3)
        .build();
    let ack = ship.handle(ClientId(2), RecvShipPacket::ItemsToTrade(ItemsToTrade {
        trade_target: 0,
        unknown2: 0,
        count: 2,
        items: titems,
    })).await.unwrap();
    assert_eq!(ack.len(), 2);
    assert!(matches!(ack[0], (ClientId(2), SendShipPacket::AcknowledgeTrade(AcknowledgeTrade {}))));
    assert!(matches!(ack[1], (ClientId(1), SendShipPacket::AcknowledgeTrade(AcknowledgeTrade {}))));

    let ack = ship.handle(ClientId(1), RecvShipPacket::TradeConfirmed(TradeConfirmed {
    })).await.unwrap();
    assert_eq!(ack.len(), 0);

    let ack = ship.handle(ClientId(2), RecvShipPacket::TradeConfirmed(TradeConfirmed {
    })).await.unwrap();
    assert_eq!(ack.len(), 14);
    assert!(matches!(ack[0], (ClientId(1), SendShipPacket::Message(Message {
        msg: GameMessage::PlayerNoLongerHasItem(PlayerNoLongerHasItem {
            client: 1,
            item_id: 0x210000,
            ..
        }),
        ..
    }))));
    assert!(matches!(ack[1], (ClientId(1), SendShipPacket::Message(Message {
        msg: GameMessage::PlayerNoLongerHasItem(PlayerNoLongerHasItem {
            client: 1,
            item_id: 0x210001,
            ..
        }),
        ..
    }))));
    assert!(matches!(ack[2], (ClientId(2), SendShipPacket::Message(Message {
        msg: GameMessage::PlayerNoLongerHasItem(PlayerNoLongerHasItem {
            client: 0,
            item_id: 0x10000,
            ..
        }),
        ..
    }))));
    assert!(matches!(ack[3], (ClientId(2), SendShipPacket::Message(Message {
        msg: GameMessage::PlayerNoLongerHasItem(PlayerNoLongerHasItem {
            client: 0,
            item_id: 0x10001,
            ..
        }),
        ..
    }))));
    assert!(matches!(ack[4], (ClientId(1), SendShipPacket::Message(Message {
        msg: GameMessage::CreateItem(CreateItem {
            client: 1,
            item_id: 0x810004,
            ..
        }),
        ..
    }))));
    assert!(matches!(ack[5], (ClientId(2), SendShipPacket::Message(Message {
        msg: GameMessage::CreateItem(CreateItem {
            client: 1,
            item_id: 0x810004,
            ..
        }),
        ..
    }))));
    assert!(matches!(ack[6], (ClientId(1), SendShipPacket::Message(Message {
        msg: GameMessage::CreateItem(CreateItem {
            client: 1,
            item_id: 0x810003,
            ..
        }),
        ..
    }))));
    assert!(matches!(ack[7], (ClientId(2), SendShipPacket::Message(Message {
        msg: GameMessage::CreateItem(CreateItem {
            client: 1,
            item_id: 0x810003,
            ..
        }),
        ..
    }))));
    assert!(matches!(ack[8], (ClientId(1), SendShipPacket::Message(Message {
        msg: GameMessage::CreateItem(CreateItem {
            client: 0,
            item_id: 0x810002,
            ..
        }),
        ..
    }))));
    assert!(matches!(ack[9], (ClientId(2), SendShipPacket::Message(Message {
        msg: GameMessage::CreateItem(CreateItem {
            client: 0,
            item_id: 0x810002,
            ..
        }),
        ..
    }))));
    assert!(matches!(ack[10], (ClientId(1), SendShipPacket::Message(Message {
        msg: GameMessage::CreateItem(CreateItem {
            client: 0,
            item_id: 0x810001,
            ..
        }),
        ..
    }))));
    assert!(matches!(ack[11], (ClientId(2), SendShipPacket::Message(Message {
        msg: GameMessage::CreateItem(CreateItem {
            client: 0,
            item_id: 0x810001,
            ..
        }),
        ..
    }))));
    assert!(matches!(ack[12], (ClientId(2), SendShipPacket::TradeSuccessful {..})));
    assert!(matches!(ack[13], (ClientId(1), SendShipPacket::TradeSuccessful {..})));

    let p1_items = entity_gateway.get_character_inventory(&char1.id).await.unwrap();
    assert_eq!(p1_items.items.len(), 2);
    assert_eq!(p1_items.items[0].with_stacked(|i| i.clone()).unwrap().len(), 3);
    assert!(matches!(p1_items.items[0].with_stacked(|i| i.clone()).unwrap()[0], item::ItemEntity{item: item::ItemDetail::Tool(item::tool::Tool {tool: item::tool::ToolType::Monofluid, ..}), ..}));
    assert_eq!(p1_items.items[1].with_stacked(|i| i.clone()).unwrap().len(), 3);
    assert!(matches!(p1_items.items[1].with_stacked(|i| i.clone()).unwrap()[0], item::ItemEntity{item: item::ItemDetail::Tool(item::tool::Tool {tool: item::tool::ToolType::Difluid, ..}), ..}));
    let p2_items = entity_gateway.get_character_inventory(&char2.id).await.unwrap();
    assert_eq!(p2_items.items.len(), 2);
    assert_eq!(p2_items.items[0].with_stacked(|i| i.clone()).unwrap().len(), 2);
    assert!(matches!(p2_items.items[0].with_stacked(|i| i.clone()).unwrap()[0], item::ItemEntity{item: item::ItemDetail::Tool(item::tool::Tool {tool: item::tool::ToolType::Monomate, ..}), ..}));
    assert_eq!(p2_items.items[1].with_stacked(|i| i.clone()).unwrap().len(), 2);
    assert!(matches!(p2_items.items[1].with_stacked(|i| i.clone()).unwrap()[0], item::ItemEntity{item: item::ItemDetail::Tool(item::tool::Tool {tool: item::tool::ToolType::Dimate, ..}), ..}));
}

#[async_std::test]
async fn test_trade_not_enough_inventory_space_individual() {
    let mut entity_gateway = InMemoryGateway::default();

    let (_user1, char1) = new_user_character(&mut entity_gateway, "a1", "a", 1).await;
    let (_user2, char2) = new_user_character(&mut entity_gateway, "a2", "a", 1).await;

    let p1_inv = futures::future::join_all((0..2).map(|_| {
        let mut entity_gateway = entity_gateway.clone();
        async move {
            entity_gateway.create_item(
                item::NewItemEntity {
                    item: item::ItemDetail::Weapon(
                        item::weapon::Weapon {
                            weapon: item::weapon::WeaponType::Handgun,
                            grind: 0,
                            special: None,
                            attrs: [None, None, None],
                            tekked: true,
                        }
                    ),
                }
            ).await
        }}))
        .await
        .into_iter()
        .collect::<Result<Vec<ItemEntity>,_>>()
        .unwrap();

    let p2_inv = futures::future::join_all((0..30).map(|_| {
        let mut entity_gateway = entity_gateway.clone();
        async move {
            entity_gateway.create_item(
                item::NewItemEntity {
                    item: item::ItemDetail::Weapon(
                        item::weapon::Weapon {
                            weapon: item::weapon::WeaponType::Handgun,
                            grind: 0,
                            special: None,
                            attrs: [None, None, None],
                            tekked: true,
                        }
                    ),
                }
            ).await
        }}))
        .await
        .into_iter()
        .collect::<Result<Vec<ItemEntity>,_>>()
        .unwrap();

    entity_gateway.set_character_inventory(&char1.id, &item::InventoryEntity::new(p1_inv)).await.unwrap();
    entity_gateway.set_character_inventory(&char2.id, &item::InventoryEntity::new(p2_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;
    log_in_char(&mut ship, ClientId(2), "a2", "a").await;

    join_lobby(&mut ship, ClientId(1)).await;
    join_lobby(&mut ship, ClientId(2)).await;

    create_room(&mut ship, ClientId(1), "room", "").await;
    join_room(&mut ship, ClientId(2), 0).await;

    let p1_items = entity_gateway.get_character_inventory(&char1.id).await.unwrap();
    assert_eq!(p1_items.items.len(), 2);
    let p2_items = entity_gateway.get_character_inventory(&char2.id).await.unwrap();
    assert_eq!(p2_items.items.len(), 30);

    initialize_trade(&mut ship, ClientId(1), ClientId(2)).await;

    ship.handle(ClientId(1), RecvShipPacket::DirectMessage(DirectMessage::new(1, GameMessage::TradeRequest(TradeRequest {
        client: 1,
        target: 0,
        trade: TradeRequestCommand::AddItem(0x10000, 1)
    })))).await.unwrap();

    confirm_trade(&mut ship, ClientId(1), ClientId(2)).await;
    finalconfirm_trade(&mut ship, ClientId(1), ClientId(2)).await;

    let titems = TradeItemBuilder::default()
        .individual(&p1_items.items[0], 0x10000)
        .build();
    let ack = ship.handle(ClientId(1), RecvShipPacket::ItemsToTrade(ItemsToTrade {
        trade_target: 1,
        unknown2: 0,
        count: 1,
        items: titems,
    })).await.unwrap();
    assert_eq!(ack.len(), 0);

    let ack = ship.handle(ClientId(2), RecvShipPacket::ItemsToTrade(ItemsToTrade {
        trade_target: 0,
        unknown2: 0,
        count: 0,
        items: Default::default(),
    })).await.unwrap();
    assert_eq!(ack.len(), 2);
    assert!(matches!(ack[0], (ClientId(2), SendShipPacket::AcknowledgeTrade(AcknowledgeTrade {}))));
    assert!(matches!(ack[1], (ClientId(1), SendShipPacket::AcknowledgeTrade(AcknowledgeTrade {}))));

    let ack = ship.handle(ClientId(1), RecvShipPacket::TradeConfirmed(TradeConfirmed {
    })).await.unwrap();
    assert_eq!(ack.len(), 0);

    let ack = ship.handle(ClientId(2), RecvShipPacket::TradeConfirmed(TradeConfirmed {
    })).await.unwrap();

    assert_eq!(ack,
               vec![
                   (ClientId(1), SendShipPacket::CancelTrade(CancelTrade {})),
                   (ClientId(2), SendShipPacket::CancelTrade(CancelTrade {})),
               ]);


    let p1_items = entity_gateway.get_character_inventory(&char1.id).await.unwrap();
    assert_eq!(p1_items.items.len(), 2);
    let p2_items = entity_gateway.get_character_inventory(&char2.id).await.unwrap();
    assert_eq!(p2_items.items.len(), 30);
}

#[async_std::test]
async fn test_trade_not_enough_inventory_space_stacked() {
    let mut entity_gateway = InMemoryGateway::default();

    let (_user1, char1) = new_user_character(&mut entity_gateway, "a1", "a", 1).await;
    let (_user2, char2) = new_user_character(&mut entity_gateway, "a2", "a", 1).await;

    let p1_stack = futures::future::join_all((0..2).map(|_| {
        let mut entity_gateway = entity_gateway.clone();
        async move {
            entity_gateway.create_item(
                item::NewItemEntity {
                    item: item::ItemDetail::Tool(
                        item::tool::Tool {
                            tool: item::tool::ToolType::Monomate,
                        }
                    )
                }).await
        }}))
        .await
        .into_iter()
        .collect::<Result<Vec<ItemEntity>,_>>()
        .unwrap();

    let p2_inv = futures::future::join_all((0..30).map(|_| {
        let mut entity_gateway = entity_gateway.clone();
        async move {
            entity_gateway.create_item(
                item::NewItemEntity {
                    item: item::ItemDetail::Weapon(
                        item::weapon::Weapon {
                            weapon: item::weapon::WeaponType::Handgun,
                            grind: 0,
                            special: None,
                            attrs: [None, None, None],
                            tekked: true,
                        }
                    ),
                }
            ).await
        }}))
        .await
        .into_iter()
        .collect::<Result<Vec<ItemEntity>,_>>()
        .unwrap();

    entity_gateway.set_character_inventory(&char1.id, &item::InventoryEntity::new(vec![p1_stack])).await.unwrap();
    entity_gateway.set_character_inventory(&char2.id, &item::InventoryEntity::new(p2_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;
    log_in_char(&mut ship, ClientId(2), "a2", "a").await;

    join_lobby(&mut ship, ClientId(1)).await;
    join_lobby(&mut ship, ClientId(2)).await;

    create_room(&mut ship, ClientId(1), "room", "").await;
    join_room(&mut ship, ClientId(2), 0).await;

    let p1_items = entity_gateway.get_character_inventory(&char1.id).await.unwrap();
    assert_eq!(p1_items.items.len(), 1);
    let p2_items = entity_gateway.get_character_inventory(&char2.id).await.unwrap();
    assert_eq!(p2_items.items.len(), 30);

    initialize_trade(&mut ship, ClientId(1), ClientId(2)).await;

    ship.handle(ClientId(1), RecvShipPacket::DirectMessage(DirectMessage::new(1, GameMessage::TradeRequest(TradeRequest {
        client: 1,
        target: 0,
        trade: TradeRequestCommand::AddItem(0x10000, 2)
    })))).await.unwrap();

    confirm_trade(&mut ship, ClientId(1), ClientId(2)).await;
    finalconfirm_trade(&mut ship, ClientId(1), ClientId(2)).await;

    let titems = TradeItemBuilder::default()
        .stacked(&p1_items.items[0], 0x10000, 2)
        .build();
    let ack = ship.handle(ClientId(1), RecvShipPacket::ItemsToTrade(ItemsToTrade {
        trade_target: 1,
        unknown2: 0,
        count: 1,
        items: titems,
    })).await.unwrap();
    assert_eq!(ack.len(), 0);

    let ack = ship.handle(ClientId(2), RecvShipPacket::ItemsToTrade(ItemsToTrade {
        trade_target: 0,
        unknown2: 0,
        count: 0,
        items: Default::default(),
    })).await.unwrap();
    assert_eq!(ack.len(), 2);
    assert!(matches!(ack[0], (ClientId(2), SendShipPacket::AcknowledgeTrade(AcknowledgeTrade {}))));
    assert!(matches!(ack[1], (ClientId(1), SendShipPacket::AcknowledgeTrade(AcknowledgeTrade {}))));

    let ack = ship.handle(ClientId(1), RecvShipPacket::TradeConfirmed(TradeConfirmed {
    })).await.unwrap();
    assert_eq!(ack.len(), 0);

    let ack = ship.handle(ClientId(2), RecvShipPacket::TradeConfirmed(TradeConfirmed {
    })).await.unwrap();

    assert_eq!(ack,
               vec![
                   (ClientId(1), SendShipPacket::CancelTrade(CancelTrade {})),
                   (ClientId(2), SendShipPacket::CancelTrade(CancelTrade {})),
               ]);

    let p1_items = entity_gateway.get_character_inventory(&char1.id).await.unwrap();
    assert_eq!(p1_items.items.len(), 1);
    let p2_items = entity_gateway.get_character_inventory(&char2.id).await.unwrap();
    assert_eq!(p2_items.items.len(), 30);
}

#[async_std::test]
async fn test_trade_stack_too_big() {
    let mut entity_gateway = InMemoryGateway::default();

    let (_user1, char1) = new_user_character(&mut entity_gateway, "a1", "a", 1).await;
    let (_user2, char2) = new_user_character(&mut entity_gateway, "a2", "a", 1).await;

    let p1_stack = futures::future::join_all((0..8).map(|_| {
        let mut entity_gateway = entity_gateway.clone();
        async move {
            entity_gateway.create_item(
                item::NewItemEntity {
                    item: item::ItemDetail::Tool(
                        item::tool::Tool {
                            tool: item::tool::ToolType::Monomate,
                        }
                    )
                }).await
        }}))
        .await
        .into_iter()
        .collect::<Result<Vec<ItemEntity>,_>>()
        .unwrap();

    let p2_stack = futures::future::join_all((0..7).map(|_| {
        let mut entity_gateway = entity_gateway.clone();
        async move {
            entity_gateway.create_item(
                item::NewItemEntity {
                    item: item::ItemDetail::Tool(
                        item::tool::Tool {
                            tool: item::tool::ToolType::Monomate,
                        }
                    )
                }).await
        }}))
        .await
        .into_iter()
        .collect::<Result<Vec<ItemEntity>,_>>()
        .unwrap();

    entity_gateway.set_character_inventory(&char1.id, &item::InventoryEntity::new(vec![p1_stack])).await.unwrap();
    entity_gateway.set_character_inventory(&char2.id, &item::InventoryEntity::new(vec![p2_stack])).await.unwrap();

    let mut ship = Box::new(ShipServerState::builder()
        .gateway(entity_gateway.clone())
        .build());
    log_in_char(&mut ship, ClientId(1), "a1", "a").await;
    log_in_char(&mut ship, ClientId(2), "a2", "a").await;

    join_lobby(&mut ship, ClientId(1)).await;
    join_lobby(&mut ship, ClientId(2)).await;

    create_room(&mut ship, ClientId(1), "room", "").await;
    join_room(&mut ship, ClientId(2), 0).await;

    let p1_items = entity_gateway.get_character_inventory(&char1.id).await.unwrap();
    assert_eq!(p1_items.items.len(), 1);
    assert_eq!(p1_items.items[0].with_stacked(|i| i.len()).unwrap(), 8);
    let p2_items = entity_gateway.get_character_inventory(&char2.id).await.unwrap();
    assert_eq!(p2_items.items.len(), 1);
    assert_eq!(p2_items.items[0].with_stacked(|i| i.len()).unwrap(), 7);

    initialize_trade(&mut ship, ClientId(1), ClientId(2)).await;

    ship.handle(ClientId(1), RecvShipPacket::DirectMessage(DirectMessage::new(1, GameMessage::TradeRequest(TradeRequest {
        client: 1,
        target: 0,
        trade: TradeRequestCommand::AddItem(0x10000, 4)
    })))).await.unwrap();

    confirm_trade(&mut ship, ClientId(1), ClientId(2)).await;
    finalconfirm_trade(&mut ship, ClientId(1), ClientId(2)).await;

    let titems = TradeItemBuilder::default()
        .stacked(&p1_items.items[0], 0x10000, 4)
        .build();
    let ack = ship.handle(ClientId(1), RecvShipPacket::ItemsToTrade(ItemsToTrade {
        trade_target: 1,
        unknown2: 0,
        count: 1,
        items: titems,
    })).await.unwrap();
    assert_eq!(ack.len(), 0);

    let ack = ship.handle(ClientId(2), RecvShipPacket::ItemsToTrade(ItemsToTrade {
        trade_target: 0,
        unknown2: 0,
        count: 0,
        items: Default::default(),
    })).await.unwrap();
    assert_eq!(ack.len(), 2);
    assert!(matches!(ack[0], (ClientId(2), SendShipPacket::AcknowledgeTrade(AcknowledgeTrade {}))));
    assert!(matches!(ack[1], (ClientId(1), SendShipPacket::AcknowledgeTrade(AcknowledgeTrade {}))));

    let ack = ship.handle(ClientId(1), RecvShipPacket::TradeConfirmed(TradeConfirmed {
    })).await.unwrap();
    assert_eq!(ack.len(), 0);

    let ack = ship.handle(ClientId(2), RecvShipPacket::TradeConfirmed(TradeConfirmed {
    })).await.unwrap();

    assert_eq!(ack,
               vec![
                   (ClientId(1), SendShipPacket::CancelTrade(CancelTrade {})),
                   (ClientId(2), SendShipPacket::CancelTrade(CancelTrade {})),
               ]);

    let p1_items = entity_gateway.get_character_inventory(&char1.id).await.unwrap();
    assert_eq!(p1_items.items.len(), 1);
    assert_eq!(p1_items.items[0].with_stacked(|i| i.len()).unwrap(), 8);
    let p2_items = entity_gateway.get_character_inventory(&char2.id).await.unwrap();
    assert_eq!(p2_items.items.len(), 1);
    assert_eq!(p2_items.items[0].with_stacked(|i| i.len()).unwrap(), 7);
}

#[async_std::test]
async fn test_trade_meseta() {
    let mut entity_gateway = InMemoryGateway::default();

    let (_user1, char1) = new_user_character(&mut entity_gateway, "a1", "a", 1).await;
    let (_user2, char2) = new_user_character(&mut entity_gateway, "a2", "a", 1).await;

    entity_gateway.set_character_meseta(&char1.id, Meseta(2323)).await.unwrap();

    let mut ship = Box::new(ShipServerState::builder()
        .gateway(entity_gateway.clone())
        .build());
    log_in_char(&mut ship, ClientId(1), "a1", "a").await;
    log_in_char(&mut ship, ClientId(2), "a2", "a").await;

    join_lobby(&mut ship, ClientId(1)).await;
    join_lobby(&mut ship, ClientId(2)).await;

    create_room(&mut ship, ClientId(1), "room", "").await;
    join_room(&mut ship, ClientId(2), 0).await;

    initialize_trade(&mut ship, ClientId(1), ClientId(2)).await;

    ship.handle(ClientId(1), RecvShipPacket::DirectMessage(DirectMessage::new(1, GameMessage::TradeRequest(TradeRequest {
        client: 1,
        target: 0,
        trade: TradeRequestCommand::AddItem(0xFFFFFF01, 23)
    })))).await.unwrap();

    confirm_trade(&mut ship, ClientId(1), ClientId(2)).await;
    finalconfirm_trade(&mut ship, ClientId(1), ClientId(2)).await;

    let titems = TradeItemBuilder::default()
        .meseta(23)
        .build();
    let ack = ship.handle(ClientId(1), RecvShipPacket::ItemsToTrade(ItemsToTrade {
        trade_target: 1,
        unknown2: 0,
        count: 1,
        items: titems,
    })).await.unwrap();
    assert_eq!(ack.len(), 0);

    let ack = ship.handle(ClientId(2), RecvShipPacket::ItemsToTrade(ItemsToTrade {
        trade_target: 0,
        unknown2: 0,
        count: 0,
        items: Default::default(),
    })).await.unwrap();
    assert_eq!(ack.len(), 2);
    assert!(matches!(ack[0], (ClientId(2), SendShipPacket::AcknowledgeTrade(AcknowledgeTrade {}))));
    assert!(matches!(ack[1], (ClientId(1), SendShipPacket::AcknowledgeTrade(AcknowledgeTrade {}))));

    let ack = ship.handle(ClientId(1), RecvShipPacket::TradeConfirmed(TradeConfirmed {
    })).await.unwrap();
    assert_eq!(ack.len(), 0);

    let ack = ship.handle(ClientId(2), RecvShipPacket::TradeConfirmed(TradeConfirmed {
    })).await.unwrap();
    assert_eq!(ack.len(), 5);
    assert!(matches!(ack[0], (ClientId(2), SendShipPacket::Message(Message {
        msg: GameMessage::PlayerNoLongerHasItem(PlayerNoLongerHasItem {..}),
        ..
    }))));
    assert!(matches!(ack[1], (ClientId(1), SendShipPacket::Message(Message {
        msg: GameMessage::CreateItem(CreateItem {..}),
        ..
    }))));
    assert!(matches!(ack[2], (ClientId(2), SendShipPacket::Message(Message {
        msg: GameMessage::CreateItem(CreateItem {..}),
        ..
    }))));
    assert!(matches!(ack[3], (ClientId(2), SendShipPacket::TradeSuccessful {..})));
    assert!(matches!(ack[4], (ClientId(1), SendShipPacket::TradeSuccessful {..})));

    let c1_meseta = entity_gateway.get_character_meseta(&char1.id).await.unwrap();
    assert_eq!(c1_meseta, Meseta(2300));
    let c2_meseta = entity_gateway.get_character_meseta(&char2.id).await.unwrap();
    assert_eq!(c2_meseta, Meseta(23));
}

#[async_std::test]
async fn test_trade_too_much_meseta() {
    let mut entity_gateway = InMemoryGateway::default();

    let (_user1, char1) = new_user_character(&mut entity_gateway, "a1", "a", 1).await;
    let (_user2, char2) = new_user_character(&mut entity_gateway, "a2", "a", 1).await;

    entity_gateway.set_character_meseta(&char1.id, Meseta(4000)).await.unwrap();
    entity_gateway.set_character_meseta(&char2.id, Meseta(999000)).await.unwrap();

    let mut ship = Box::new(ShipServerState::builder()
        .gateway(entity_gateway.clone())
        .build());
    log_in_char(&mut ship, ClientId(1), "a1", "a").await;
    log_in_char(&mut ship, ClientId(2), "a2", "a").await;

    join_lobby(&mut ship, ClientId(1)).await;
    join_lobby(&mut ship, ClientId(2)).await;

    create_room(&mut ship, ClientId(1), "room", "").await;
    join_room(&mut ship, ClientId(2), 0).await;

    initialize_trade(&mut ship, ClientId(1), ClientId(2)).await;

    ship.handle(ClientId(1), RecvShipPacket::DirectMessage(DirectMessage::new(1, GameMessage::TradeRequest(TradeRequest {
        client: 1,
        target: 0,
        trade: TradeRequestCommand::AddItem(0xFFFFFF01, 2000)
    })))).await.unwrap();

    confirm_trade(&mut ship, ClientId(1), ClientId(2)).await;
    finalconfirm_trade(&mut ship, ClientId(1), ClientId(2)).await;

    let titems = TradeItemBuilder::default()
        .meseta(2000)
        .build();
    let ack = ship.handle(ClientId(1), RecvShipPacket::ItemsToTrade(ItemsToTrade {
        trade_target: 1,
        unknown2: 0,
        count: 1,
        items: titems,
    })).await.unwrap();
    assert_eq!(ack.len(), 2);
    assert!(matches!(ack[0], (ClientId(2), SendShipPacket::CancelTrade(..))));
    assert!(matches!(ack[1], (ClientId(1), SendShipPacket::CancelTrade(..))));

    let c1_meseta = entity_gateway.get_character_meseta(&char1.id).await.unwrap();
    assert_eq!(c1_meseta, Meseta(4000));
    let c2_meseta = entity_gateway.get_character_meseta(&char2.id).await.unwrap();
    assert_eq!(c2_meseta, Meseta(999000));
}

#[async_std::test]
async fn test_trade_invalid_amount_of_meseta() {
    let mut entity_gateway = InMemoryGateway::default();

    let (_user1, char1) = new_user_character(&mut entity_gateway, "a1", "a", 1).await;
    let (_user2, char2) = new_user_character(&mut entity_gateway, "a2", "a", 1).await;

    entity_gateway.set_character_meseta(&char1.id, Meseta(4000)).await.unwrap();
    entity_gateway.set_character_meseta(&char2.id, Meseta(999000)).await.unwrap();

    let mut ship = Box::new(ShipServerState::builder()
        .gateway(entity_gateway.clone())
        .build());
    log_in_char(&mut ship, ClientId(1), "a1", "a").await;
    log_in_char(&mut ship, ClientId(2), "a2", "a").await;

    join_lobby(&mut ship, ClientId(1)).await;
    join_lobby(&mut ship, ClientId(2)).await;

    create_room(&mut ship, ClientId(1), "room", "").await;
    join_room(&mut ship, ClientId(2), 0).await;

    initialize_trade(&mut ship, ClientId(1), ClientId(2)).await;

    ship.handle(ClientId(1), RecvShipPacket::DirectMessage(DirectMessage::new(1, GameMessage::TradeRequest(TradeRequest {
        client: 1,
        target: 0,
        trade: TradeRequestCommand::AddItem(0xFFFFFF01, 5000)
    })))).await.unwrap();

    confirm_trade(&mut ship, ClientId(1), ClientId(2)).await;
    finalconfirm_trade(&mut ship, ClientId(1), ClientId(2)).await;

    let titems = TradeItemBuilder::default()
        .meseta(5000)
        .build();
    let ack = ship.handle(ClientId(1), RecvShipPacket::ItemsToTrade(ItemsToTrade {
        trade_target: 1,
        unknown2: 0,
        count: 1,
        items: titems,
    })).await.unwrap();
    assert_eq!(ack.len(), 2);
    assert!(matches!(ack[0], (ClientId(2), SendShipPacket::CancelTrade(..))));
    assert!(matches!(ack[1], (ClientId(1), SendShipPacket::CancelTrade(..))));

    let c1_meseta = entity_gateway.get_character_meseta(&char1.id).await.unwrap();
    assert_eq!(c1_meseta, Meseta(4000));
    let c2_meseta = entity_gateway.get_character_meseta(&char2.id).await.unwrap();
    assert_eq!(c2_meseta, Meseta(999000));
}

#[async_std::test]
async fn test_trade_meseta_request_and_items_dont_match() {
    let mut entity_gateway = InMemoryGateway::default();

    let (_user1, char1) = new_user_character(&mut entity_gateway, "a1", "a", 1).await;
    let (_user2, char2) = new_user_character(&mut entity_gateway, "a2", "a", 1).await;

    entity_gateway.set_character_meseta(&char1.id, Meseta(4000)).await.unwrap();
    entity_gateway.set_character_meseta(&char2.id, Meseta(999000)).await.unwrap();

    let mut ship = Box::new(ShipServerState::builder()
        .gateway(entity_gateway.clone())
        .build());
    log_in_char(&mut ship, ClientId(1), "a1", "a").await;
    log_in_char(&mut ship, ClientId(2), "a2", "a").await;

    join_lobby(&mut ship, ClientId(1)).await;
    join_lobby(&mut ship, ClientId(2)).await;

    create_room(&mut ship, ClientId(1), "room", "").await;
    join_room(&mut ship, ClientId(2), 0).await;

    initialize_trade(&mut ship, ClientId(1), ClientId(2)).await;

    ship.handle(ClientId(1), RecvShipPacket::DirectMessage(DirectMessage::new(1, GameMessage::TradeRequest(TradeRequest {
        client: 1,
        target: 0,
        trade: TradeRequestCommand::AddItem(0xFFFFFF01, 50)
    })))).await.unwrap();

    confirm_trade(&mut ship, ClientId(1), ClientId(2)).await;
    finalconfirm_trade(&mut ship, ClientId(1), ClientId(2)).await;

    let titems = TradeItemBuilder::default()
        .meseta(23)
        .build();
    let ack = ship.handle(ClientId(1), RecvShipPacket::ItemsToTrade(ItemsToTrade {
        trade_target: 1,
        unknown2: 0,
        count: 1,
        items: titems,
    })).await.unwrap();
    assert_eq!(ack.len(), 2);
    assert!(matches!(ack[0], (ClientId(2), SendShipPacket::CancelTrade(..))));
    assert!(matches!(ack[1], (ClientId(1), SendShipPacket::CancelTrade(..))));

    let c1_meseta = entity_gateway.get_character_meseta(&char1.id).await.unwrap();
    assert_eq!(c1_meseta, Meseta(4000));
    let c2_meseta = entity_gateway.get_character_meseta(&char2.id).await.unwrap();
    assert_eq!(c2_meseta, Meseta(999000));
}

#[async_std::test]
async fn test_player_declined_trade() {
    let mut entity_gateway = InMemoryGateway::default();

    let (_user1, _char1) = new_user_character(&mut entity_gateway, "a1", "a", 1).await;
    let (_user2, _char2) = new_user_character(&mut entity_gateway, "a2", "a", 1).await;

    let mut ship = Box::new(ShipServerState::builder()
        .gateway(entity_gateway.clone())
        .build());
    log_in_char(&mut ship, ClientId(1), "a1", "a").await;
    log_in_char(&mut ship, ClientId(2), "a2", "a").await;

    join_lobby(&mut ship, ClientId(1)).await;
    join_lobby(&mut ship, ClientId(2)).await;

    create_room(&mut ship, ClientId(1), "room", "").await;
    join_room(&mut ship, ClientId(2), 0).await;

    initialize_trade(&mut ship, ClientId(1), ClientId(2)).await;

    let ack = ship.handle(ClientId(1), RecvShipPacket::DirectMessage(DirectMessage::new(1, GameMessage::TradeRequest(TradeRequest {
        client: 1,
        target: 0,
        trade: TradeRequestCommand::Cancel
    })))).await.unwrap();
    assert_eq!(ack.len(), 2);
    assert!(matches!(ack[0], (ClientId(2), SendShipPacket::CancelTrade(..))));
    assert!(matches!(ack[1], (ClientId(1), SendShipPacket::CancelTrade(..))));
}

#[async_std::test]
async fn test_back_out_of_trade_last_minute() {
    let mut entity_gateway = InMemoryGateway::default();

    let (_user1, char1) = new_user_character(&mut entity_gateway, "a1", "a", 1).await;
    let (_user2, char2) = new_user_character(&mut entity_gateway, "a2", "a", 1).await;

    let mut p1_inv = Vec::new();
    p1_inv.push(entity_gateway.create_item(
            item::NewItemEntity {
                item: item::ItemDetail::Weapon(
                    item::weapon::Weapon {
                        weapon: item::weapon::WeaponType::Handgun,
                        grind: 0,
                        special: None,
                        attrs: [None, None, None],
                        tekked: true,
                    }
                ),
            }).await.unwrap());

    entity_gateway.set_character_inventory(&char1.id, &item::InventoryEntity::new(p1_inv)).await.unwrap();
    entity_gateway.set_character_inventory(&char2.id, &item::InventoryEntity::new(Vec::<item::InventoryItemEntity>::new())).await.unwrap();

    let mut ship = Box::new(ShipServerState::builder()
        .gateway(entity_gateway.clone())
        .build());
    log_in_char(&mut ship, ClientId(1), "a1", "a").await;
    log_in_char(&mut ship, ClientId(2), "a2", "a").await;

    join_lobby(&mut ship, ClientId(1)).await;
    join_lobby(&mut ship, ClientId(2)).await;

    create_room(&mut ship, ClientId(1), "room", "").await;
    join_room(&mut ship, ClientId(2), 0).await;

    let p1_items = entity_gateway.get_character_inventory(&char1.id).await.unwrap();
    assert_eq!(p1_items.items.len(), 1);
    let p2_items = entity_gateway.get_character_inventory(&char2.id).await.unwrap();
    assert_eq!(p2_items.items.len(), 0);

    initialize_trade(&mut ship, ClientId(1), ClientId(2)).await;

    ship.handle(ClientId(1), RecvShipPacket::DirectMessage(DirectMessage::new(1, GameMessage::TradeRequest(TradeRequest {
        client: 1,
        target: 0,
        trade: TradeRequestCommand::AddItem(0x10000, 1)
    })))).await.unwrap();

    confirm_trade(&mut ship, ClientId(1), ClientId(2)).await;

    let ack = ship.handle(ClientId(1), RecvShipPacket::DirectMessage(DirectMessage::new(1, GameMessage::TradeRequest(TradeRequest {
        client: 1,
        target: 0,
        trade: TradeRequestCommand::Cancel
    })))).await.unwrap();
    assert_eq!(ack.len(), 2);
    assert!(matches!(ack[0], (ClientId(2), SendShipPacket::CancelTrade(..))));
    assert!(matches!(ack[1], (ClientId(1), SendShipPacket::CancelTrade(..))));

    let p1_items = entity_gateway.get_character_inventory(&char1.id).await.unwrap();
    assert_eq!(p1_items.items.len(), 1);
    let p2_items = entity_gateway.get_character_inventory(&char2.id).await.unwrap();
    assert_eq!(p2_items.items.len(), 0);
}

#[async_std::test]
async fn test_valid_trade_when_both_inventories_are_full() {
    let mut entity_gateway = InMemoryGateway::default();

    let (_user1, char1) = new_user_character(&mut entity_gateway, "a1", "a", 1).await;
    let (_user2, char2) = new_user_character(&mut entity_gateway, "a2", "a", 1).await;

    let p1_inv = futures::future::join_all((0..30).map(|_| {
        let mut entity_gateway = entity_gateway.clone();
        async move {
            entity_gateway.create_item(
                item::NewItemEntity {
                    item: item::ItemDetail::Weapon(
                        item::weapon::Weapon {
                            weapon: item::weapon::WeaponType::Saber,
                            grind: 0,
                            special: None,
                            attrs: [None, None, None],
                            tekked: true,
                        }
                    ),
                }
            ).await
        }}))
        .await
        .into_iter()
        .collect::<Result<Vec<ItemEntity>,_>>()
        .unwrap();

    let p2_inv = futures::future::join_all((0..30).map(|_| {
        let mut entity_gateway = entity_gateway.clone();
        async move {
            entity_gateway.create_item(
                item::NewItemEntity {
                    item: item::ItemDetail::Weapon(
                        item::weapon::Weapon {
                            weapon: item::weapon::WeaponType::Handgun,
                            grind: 0,
                            special: None,
                            attrs: [None, None, None],
                            tekked: true,
                        }
                    ),
                }
            ).await
        }}))
        .await
        .into_iter()
        .collect::<Result<Vec<ItemEntity>,_>>()
        .unwrap();

    entity_gateway.set_character_inventory(&char1.id, &item::InventoryEntity::new(p1_inv)).await.unwrap();
    entity_gateway.set_character_inventory(&char2.id, &item::InventoryEntity::new(p2_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;
    log_in_char(&mut ship, ClientId(2), "a2", "a").await;

    join_lobby(&mut ship, ClientId(1)).await;
    join_lobby(&mut ship, ClientId(2)).await;

    create_room(&mut ship, ClientId(1), "room", "").await;
    join_room(&mut ship, ClientId(2), 0).await;

    let p1_items = entity_gateway.get_character_inventory(&char1.id).await.unwrap();
    assert_eq!(p1_items.items.len(), 30);
    let p2_items = entity_gateway.get_character_inventory(&char2.id).await.unwrap();
    assert_eq!(p2_items.items.len(), 30);

    initialize_trade(&mut ship, ClientId(1), ClientId(2)).await;

    ship.handle(ClientId(1), RecvShipPacket::DirectMessage(DirectMessage::new(1, GameMessage::TradeRequest(TradeRequest {
        client: 1,
        target: 0,
        trade: TradeRequestCommand::AddItem(0x10000, 1)
    })))).await.unwrap();
    ship.handle(ClientId(1), RecvShipPacket::DirectMessage(DirectMessage::new(1, GameMessage::TradeRequest(TradeRequest {
        client: 1,
        target: 0,
        trade: TradeRequestCommand::AddItem(0x10001, 1)
    })))).await.unwrap();

    ship.handle(ClientId(2), RecvShipPacket::DirectMessage(DirectMessage::new(0, GameMessage::TradeRequest(TradeRequest {
        client: 0,
        target: 0,
        trade: TradeRequestCommand::AddItem(0x210000, 1)
    })))).await.unwrap();
    ship.handle(ClientId(2), RecvShipPacket::DirectMessage(DirectMessage::new(0, GameMessage::TradeRequest(TradeRequest {
        client: 0,
        target: 0,
        trade: TradeRequestCommand::AddItem(0x210001, 1)
    })))).await.unwrap();

    confirm_trade(&mut ship, ClientId(1), ClientId(2)).await;
    finalconfirm_trade(&mut ship, ClientId(1), ClientId(2)).await;

    let titems = TradeItemBuilder::default()
        .individual(&p1_items.items[0], 0x10000)
        .individual(&p1_items.items[1], 0x10001)
        .build();
    let ack = ship.handle(ClientId(1), RecvShipPacket::ItemsToTrade(ItemsToTrade {
        trade_target: 1,
        unknown2: 0,
        count: 2,
        items: titems,
    })).await.unwrap();
    assert_eq!(ack.len(), 0);

    let titems = TradeItemBuilder::default()
        .individual(&p2_items.items[0], 0x210000)
        .individual(&p2_items.items[1], 0x210001)
        .build();
    let ack = ship.handle(ClientId(2), RecvShipPacket::ItemsToTrade(ItemsToTrade {
        trade_target: 0,
        unknown2: 0,
        count: 2,
        items: titems,
    })).await.unwrap();
    assert_eq!(ack.len(), 2);
    assert!(matches!(ack[0], (ClientId(2), SendShipPacket::AcknowledgeTrade(AcknowledgeTrade {}))));
    assert!(matches!(ack[1], (ClientId(1), SendShipPacket::AcknowledgeTrade(AcknowledgeTrade {}))));

    let ack = ship.handle(ClientId(1), RecvShipPacket::TradeConfirmed(TradeConfirmed {
    })).await.unwrap();
    assert_eq!(ack.len(), 0);

    let ack = ship.handle(ClientId(2), RecvShipPacket::TradeConfirmed(TradeConfirmed {
    })).await.unwrap();
    assert_eq!(ack.len(), 14);

    let p1_items = entity_gateway.get_character_inventory(&char1.id).await.unwrap();
    assert_eq!(p1_items.items.len(), 30);
    assert_eq!(p1_items.items.iter().filter(|i| matches!(i.individual().unwrap().item, item::ItemDetail::Weapon(item::weapon::Weapon { weapon: item::weapon::WeaponType::Saber, ..}, ..))).count(), 28);
    assert_eq!(p1_items.items.iter().filter(|i| matches!(i.individual().unwrap().item, item::ItemDetail::Weapon(item::weapon::Weapon { weapon: item::weapon::WeaponType::Handgun, ..}, ..))).count(), 2);
    let p2_items = entity_gateway.get_character_inventory(&char2.id).await.unwrap();
    assert_eq!(p2_items.items.len(), 30);
    assert_eq!(p2_items.items.iter().filter(|i| matches!(i.individual().unwrap().item, item::ItemDetail::Weapon(item::weapon::Weapon { weapon: item::weapon::WeaponType::Saber, ..}, ..))).count(), 2);
    assert_eq!(p2_items.items.iter().filter(|i| matches!(i.individual().unwrap().item, item::ItemDetail::Weapon(item::weapon::Weapon { weapon: item::weapon::WeaponType::Handgun, ..}, ..))).count(), 28);
}

#[async_std::test]
async fn test_invalid_trade_when_both_inventories_are_full() {
    let mut entity_gateway = InMemoryGateway::default();

    let (_user1, char1) = new_user_character(&mut entity_gateway, "a1", "a", 1).await;
    let (_user2, char2) = new_user_character(&mut entity_gateway, "a2", "a", 1).await;

    let p1_inv = futures::future::join_all((0..30).map(|_| {
        let mut entity_gateway = entity_gateway.clone();
        async move {
            entity_gateway.create_item(
                item::NewItemEntity {
                    item: item::ItemDetail::Weapon(
                        item::weapon::Weapon {
                            weapon: item::weapon::WeaponType::Saber,
                            grind: 0,
                            special: None,
                            attrs: [None, None, None],
                            tekked: true,
                        }
                    ),
                }
            ).await
        }}))
        .await
        .into_iter()
        .collect::<Result<Vec<ItemEntity>,_>>()
        .unwrap();

    let p2_inv = futures::future::join_all((0..30).map(|_| {
        let mut entity_gateway = entity_gateway.clone();
        async move {
            entity_gateway.create_item(
                item::NewItemEntity {
                    item: item::ItemDetail::Weapon(
                        item::weapon::Weapon {
                            weapon: item::weapon::WeaponType::Handgun,
                            grind: 0,
                            special: None,
                            attrs: [None, None, None],
                            tekked: true,
                        }
                    ),
                }
            ).await
        }}))
        .await
        .into_iter()
        .collect::<Result<Vec<ItemEntity>,_>>()
        .unwrap();

    entity_gateway.set_character_inventory(&char1.id, &item::InventoryEntity::new(p1_inv)).await.unwrap();
    entity_gateway.set_character_inventory(&char2.id, &item::InventoryEntity::new(p2_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;
    log_in_char(&mut ship, ClientId(2), "a2", "a").await;

    join_lobby(&mut ship, ClientId(1)).await;
    join_lobby(&mut ship, ClientId(2)).await;

    create_room(&mut ship, ClientId(1), "room", "").await;
    join_room(&mut ship, ClientId(2), 0).await;

    let p1_items = entity_gateway.get_character_inventory(&char1.id).await.unwrap();
    assert_eq!(p1_items.items.len(), 30);
    let p2_items = entity_gateway.get_character_inventory(&char2.id).await.unwrap();
    assert_eq!(p2_items.items.len(), 30);

    initialize_trade(&mut ship, ClientId(1), ClientId(2)).await;

    ship.handle(ClientId(1), RecvShipPacket::DirectMessage(DirectMessage::new(1, GameMessage::TradeRequest(TradeRequest {
        client: 1,
        target: 0,
        trade: TradeRequestCommand::AddItem(0x10000, 1)
    })))).await.unwrap();
    ship.handle(ClientId(1), RecvShipPacket::DirectMessage(DirectMessage::new(1, GameMessage::TradeRequest(TradeRequest {
        client: 1,
        target: 0,
        trade: TradeRequestCommand::AddItem(0x10001, 1)
    })))).await.unwrap();
    ship.handle(ClientId(1), RecvShipPacket::DirectMessage(DirectMessage::new(1, GameMessage::TradeRequest(TradeRequest {
        client: 1,
        target: 0,
        trade: TradeRequestCommand::AddItem(0x10002, 1)
    })))).await.unwrap();

    ship.handle(ClientId(2), RecvShipPacket::DirectMessage(DirectMessage::new(0, GameMessage::TradeRequest(TradeRequest {
        client: 0,
        target: 0,
        trade: TradeRequestCommand::AddItem(0x210000, 1)
    })))).await.unwrap();
    ship.handle(ClientId(2), RecvShipPacket::DirectMessage(DirectMessage::new(0, GameMessage::TradeRequest(TradeRequest {
        client: 0,
        target: 0,
        trade: TradeRequestCommand::AddItem(0x210001, 1)
    })))).await.unwrap();

    confirm_trade(&mut ship, ClientId(1), ClientId(2)).await;
    finalconfirm_trade(&mut ship, ClientId(1), ClientId(2)).await;

    let titems = TradeItemBuilder::default()
        .individual(&p1_items.items[0], 0x10000)
        .individual(&p1_items.items[1], 0x10001)
        .individual(&p1_items.items[1], 0x10002)
        .build();
    let ack = ship.handle(ClientId(1), RecvShipPacket::ItemsToTrade(ItemsToTrade {
        trade_target: 1,
        unknown2: 0,
        count: 3,
        items: titems,
    })).await.unwrap();
    assert_eq!(ack.len(), 0);

    let titems = TradeItemBuilder::default()
        .individual(&p2_items.items[0], 0x210000)
        .individual(&p2_items.items[1], 0x210001)
        .build();
    let ack = ship.handle(ClientId(2), RecvShipPacket::ItemsToTrade(ItemsToTrade {
        trade_target: 0,
        unknown2: 0,
        count: 2,
        items: titems,
    })).await.unwrap();
    assert_eq!(ack.len(), 2);
    assert!(matches!(ack[0], (ClientId(2), SendShipPacket::AcknowledgeTrade(AcknowledgeTrade {}))));
    assert!(matches!(ack[1], (ClientId(1), SendShipPacket::AcknowledgeTrade(AcknowledgeTrade {}))));

    let ack = ship.handle(ClientId(1), RecvShipPacket::TradeConfirmed(TradeConfirmed {
    })).await.unwrap();
    assert_eq!(ack.len(), 0);

    let ack = ship.handle(ClientId(2), RecvShipPacket::TradeConfirmed(TradeConfirmed {
    })).await.unwrap();

    assert_eq!(ack,
               vec![
                   (ClientId(1), SendShipPacket::CancelTrade(CancelTrade {})),
                   (ClientId(2), SendShipPacket::CancelTrade(CancelTrade {})),
               ]);

    let p1_items = entity_gateway.get_character_inventory(&char1.id).await.unwrap();
    assert_eq!(p1_items.items.len(), 30);
    assert_eq!(p1_items.items.iter().filter(|i| matches!(i.individual().unwrap().item, item::ItemDetail::Weapon(item::weapon::Weapon { weapon: item::weapon::WeaponType::Saber, ..}, ..))).count(), 30);
    let p2_items = entity_gateway.get_character_inventory(&char2.id).await.unwrap();
    assert_eq!(p2_items.items.len(), 30);
    assert_eq!(p2_items.items.iter().filter(|i| matches!(i.individual().unwrap().item, item::ItemDetail::Weapon(item::weapon::Weapon { weapon: item::weapon::WeaponType::Handgun, ..}, ..))).count(), 30);
}


#[async_std::test]
async fn test_client_tries_to_start_two_trades() {
    let mut entity_gateway = InMemoryGateway::default();

    let (_user1, _char1) = new_user_character(&mut entity_gateway, "a1", "a", 1).await;
    let (_user2, _char2) = new_user_character(&mut entity_gateway, "a2", "a", 1).await;
    let (_user2, _char3) = new_user_character(&mut entity_gateway, "a3", "a", 1).await;

    let mut ship = Box::new(ShipServerState::builder()
        .gateway(entity_gateway.clone())
        .build());
    log_in_char(&mut ship, ClientId(1), "a1", "a").await;
    log_in_char(&mut ship, ClientId(2), "a2", "a").await;
    log_in_char(&mut ship, ClientId(3), "a3", "a").await;

    join_lobby(&mut ship, ClientId(1)).await;
    join_lobby(&mut ship, ClientId(2)).await;
    join_lobby(&mut ship, ClientId(3)).await;

    create_room(&mut ship, ClientId(1), "room", "").await;
    join_room(&mut ship, ClientId(2), 0).await;
    join_room(&mut ship, ClientId(3), 0).await;

    initialize_trade(&mut ship, ClientId(1), ClientId(2)).await;
    let ack = ship.handle(ClientId(1), RecvShipPacket::DirectMessage(DirectMessage::new(1, GameMessage::TradeRequest(TradeRequest {
        client: 0,
        target: 0,
        trade: TradeRequestCommand::Initialize(TradeRequestInitializeCommand::Initialize, 0)
    })))).await.err().unwrap();

    assert!(matches!(ack.downcast::<TradeError>().unwrap(), TradeError::ClientAlreadyInTrade));
}

#[async_std::test]
async fn test_client_tries_trading_with_client_already_trading() {
    let mut entity_gateway = InMemoryGateway::default();

    let (_user1, _char1) = new_user_character(&mut entity_gateway, "a1", "a", 1).await;
    let (_user2, _char2) = new_user_character(&mut entity_gateway, "a2", "a", 1).await;
    let (_user2, _char3) = new_user_character(&mut entity_gateway, "a3", "a", 1).await;

    let mut ship = Box::new(ShipServerState::builder()
        .gateway(entity_gateway.clone())
        .build());
    log_in_char(&mut ship, ClientId(1), "a1", "a").await;
    log_in_char(&mut ship, ClientId(2), "a2", "a").await;
    log_in_char(&mut ship, ClientId(3), "a3", "a").await;

    join_lobby(&mut ship, ClientId(1)).await;
    join_lobby(&mut ship, ClientId(2)).await;
    join_lobby(&mut ship, ClientId(3)).await;

    create_room(&mut ship, ClientId(1), "room", "").await;
    join_room(&mut ship, ClientId(2), 0).await;
    join_room(&mut ship, ClientId(3), 0).await;

    initialize_trade(&mut ship, ClientId(1), ClientId(2)).await;
    let ack = ship.handle(ClientId(3), RecvShipPacket::DirectMessage(DirectMessage::new(0, GameMessage::TradeRequest(TradeRequest {
        client: 2,
        target: 0,
        trade: TradeRequestCommand::Initialize(TradeRequestInitializeCommand::Initialize, 0)
    })))).await.err().unwrap();
    assert!(matches!(ack.downcast::<TradeError>().unwrap(), TradeError::OtherAlreadyInTrade));

    let ack = ship.handle(ClientId(3), RecvShipPacket::DirectMessage(DirectMessage::new(1, GameMessage::TradeRequest(TradeRequest {
        client: 2,
        target: 0,
        trade: TradeRequestCommand::Initialize(TradeRequestInitializeCommand::Initialize, 1)
    })))).await.err().unwrap();
    assert!(matches!(ack.downcast::<TradeError>().unwrap(), TradeError::OtherAlreadyInTrade));
}

#[async_std::test]
async fn test_add_then_remove_individual_item() {
    let mut entity_gateway = InMemoryGateway::default();

    let (_user1, char1) = new_user_character(&mut entity_gateway, "a1", "a", 1).await;
    let (_user2, char2) = new_user_character(&mut entity_gateway, "a2", "a", 1).await;

    let mut p1_inv = Vec::new();
    for _ in 0..2 {
        p1_inv.push(entity_gateway.create_item(
            item::NewItemEntity {
                item: item::ItemDetail::Weapon(
                    item::weapon::Weapon {
                        weapon: item::weapon::WeaponType::Handgun,
                        grind: 0,
                        special: None,
                        attrs: [None, None, None],
                        tekked: true,
                    }
                ),
            }).await.unwrap());
    }

    entity_gateway.set_character_inventory(&char1.id, &item::InventoryEntity::new(p1_inv)).await.unwrap();
    entity_gateway.set_character_inventory(&char2.id, &item::InventoryEntity::new(Vec::<item::InventoryItemEntity>::new())).await.unwrap();

    let mut ship = Box::new(ShipServerState::builder()
        .gateway(entity_gateway.clone())
        .build());
    log_in_char(&mut ship, ClientId(1), "a1", "a").await;
    log_in_char(&mut ship, ClientId(2), "a2", "a").await;

    join_lobby(&mut ship, ClientId(1)).await;
    join_lobby(&mut ship, ClientId(2)).await;

    create_room(&mut ship, ClientId(1), "room", "").await;
    join_room(&mut ship, ClientId(2), 0).await;

    let p1_items = entity_gateway.get_character_inventory(&char1.id).await.unwrap();
    assert_eq!(p1_items.items.len(), 2);
    let p2_items = entity_gateway.get_character_inventory(&char2.id).await.unwrap();
    assert_eq!(p2_items.items.len(), 0);

    initialize_trade(&mut ship, ClientId(1), ClientId(2)).await;

    ship.handle(ClientId(1), RecvShipPacket::DirectMessage(DirectMessage::new(1, GameMessage::TradeRequest(TradeRequest {
        client: 1,
        target: 0,
        trade: TradeRequestCommand::AddItem(0x10000, 1)
    })))).await.unwrap();
    ship.handle(ClientId(1), RecvShipPacket::DirectMessage(DirectMessage::new(1, GameMessage::TradeRequest(TradeRequest {
        client: 1,
        target: 0,
        trade: TradeRequestCommand::AddItem(0x10001, 1)
    })))).await.unwrap();
    ship.handle(ClientId(1), RecvShipPacket::DirectMessage(DirectMessage::new(1, GameMessage::TradeRequest(TradeRequest {
        client: 1,
        target: 0,
        trade: TradeRequestCommand::RemoveItem(0x10000, 1)
    })))).await.unwrap();

    confirm_trade(&mut ship, ClientId(1), ClientId(2)).await;
    finalconfirm_trade(&mut ship, ClientId(1), ClientId(2)).await;

    let titems = TradeItemBuilder::default()
        .individual(&p1_items.items[1], 0x10001)
        .build();
    let ack = ship.handle(ClientId(1), RecvShipPacket::ItemsToTrade(ItemsToTrade {
        trade_target: 1,
        unknown2: 0,
        count: 1,
        items: titems,
    })).await.unwrap();
    assert_eq!(ack.len(), 0);

    let ack = ship.handle(ClientId(2), RecvShipPacket::ItemsToTrade(ItemsToTrade {
        trade_target: 0,
        unknown2: 0,
        count: 0,
        items: Default::default(),
    })).await.unwrap();
    assert_eq!(ack.len(), 2);
    assert!(matches!(ack[0], (ClientId(2), SendShipPacket::AcknowledgeTrade(AcknowledgeTrade {}))));
    assert!(matches!(ack[1], (ClientId(1), SendShipPacket::AcknowledgeTrade(AcknowledgeTrade {}))));

    let ack = ship.handle(ClientId(1), RecvShipPacket::TradeConfirmed(TradeConfirmed {
    })).await.unwrap();
    assert_eq!(ack.len(), 0);

    let ack = ship.handle(ClientId(2), RecvShipPacket::TradeConfirmed(TradeConfirmed {
    })).await.unwrap();
    assert_eq!(ack.len(), 5);
    assert!(matches!(ack[0], (ClientId(2), SendShipPacket::Message(Message {
        msg: GameMessage::PlayerNoLongerHasItem(PlayerNoLongerHasItem {..}),
        ..
    }))));
    assert!(matches!(ack[1], (ClientId(1), SendShipPacket::Message(Message {
        msg: GameMessage::CreateItem(CreateItem {..}),
        ..
    }))));
    assert!(matches!(ack[2], (ClientId(2), SendShipPacket::Message(Message {
        msg: GameMessage::CreateItem(CreateItem {..}),
        ..
    }))));
    assert!(matches!(ack[3], (ClientId(2), SendShipPacket::TradeSuccessful {..})));
    assert!(matches!(ack[4], (ClientId(1), SendShipPacket::TradeSuccessful {..})));

    let p1_items = entity_gateway.get_character_inventory(&char1.id).await.unwrap();
    assert_eq!(p1_items.items.len(), 1);
    let p2_items = entity_gateway.get_character_inventory(&char2.id).await.unwrap();
    assert_eq!(p2_items.items.len(), 1);
}

#[async_std::test]
async fn test_add_then_remove_stacked_item() {
    let mut entity_gateway = InMemoryGateway::default();

    let (_user1, char1) = new_user_character(&mut entity_gateway, "a1", "a", 1).await;
    let (_user2, char2) = new_user_character(&mut entity_gateway, "a2", "a", 1).await;

    let p1_stack1 = futures::future::join_all((0..2).map(|_| {
        let mut entity_gateway = entity_gateway.clone();
        async move {
            entity_gateway.create_item(
                item::NewItemEntity {
                    item: item::ItemDetail::Tool(
                        item::tool::Tool {
                            tool: item::tool::ToolType::Monomate,
                        }
                    )
                }).await
        }}))
        .await
        .into_iter()
        .collect::<Result<Vec<ItemEntity>,_>>()
        .unwrap();

    let p1_stack2 = futures::future::join_all((0..2).map(|_| {
        let mut entity_gateway = entity_gateway.clone();
        async move {
            entity_gateway.create_item(
                item::NewItemEntity {
                    item: item::ItemDetail::Tool(
                        item::tool::Tool {
                            tool: item::tool::ToolType::Monofluid,
                        }
                    )
                }).await
        }}))
        .await
        .into_iter()
        .collect::<Result<Vec<ItemEntity>,_>>()
        .unwrap();

    entity_gateway.set_character_inventory(&char1.id, &item::InventoryEntity::new(vec![p1_stack1, p1_stack2])).await.unwrap();
    entity_gateway.set_character_inventory(&char2.id, &item::InventoryEntity::new(Vec::<item::InventoryItemEntity>::new())).await.unwrap();

    let mut ship = Box::new(ShipServerState::builder()
        .gateway(entity_gateway.clone())
        .build());
    log_in_char(&mut ship, ClientId(1), "a1", "a").await;
    log_in_char(&mut ship, ClientId(2), "a2", "a").await;

    join_lobby(&mut ship, ClientId(1)).await;
    join_lobby(&mut ship, ClientId(2)).await;

    create_room(&mut ship, ClientId(1), "room", "").await;
    join_room(&mut ship, ClientId(2), 0).await;

    let p1_items = entity_gateway.get_character_inventory(&char1.id).await.unwrap();
    assert_eq!(p1_items.items.len(), 2);
    let p2_items = entity_gateway.get_character_inventory(&char2.id).await.unwrap();
    assert_eq!(p2_items.items.len(), 0);

    initialize_trade(&mut ship, ClientId(1), ClientId(2)).await;

    ship.handle(ClientId(1), RecvShipPacket::DirectMessage(DirectMessage::new(1, GameMessage::TradeRequest(TradeRequest {
        client: 1,
        target: 0,
        trade: TradeRequestCommand::AddItem(0x10000, 2)
    })))).await.unwrap();
    ship.handle(ClientId(1), RecvShipPacket::DirectMessage(DirectMessage::new(1, GameMessage::TradeRequest(TradeRequest {
        client: 1,
        target: 0,
        trade: TradeRequestCommand::AddItem(0x10001, 2)
    })))).await.unwrap();
    ship.handle(ClientId(1), RecvShipPacket::DirectMessage(DirectMessage::new(1, GameMessage::TradeRequest(TradeRequest {
        client: 1,
        target: 0,
        trade: TradeRequestCommand::RemoveItem(0x10000, 2)
    })))).await.unwrap();

    confirm_trade(&mut ship, ClientId(1), ClientId(2)).await;
    finalconfirm_trade(&mut ship, ClientId(1), ClientId(2)).await;

    let titems = TradeItemBuilder::default()
        .stacked(&p1_items.items[1], 0x10001, 2)
        .build();
    let ack = ship.handle(ClientId(1), RecvShipPacket::ItemsToTrade(ItemsToTrade {
        trade_target: 1,
        unknown2: 0,
        count: 1,
        items: titems,
    })).await.unwrap();
    assert_eq!(ack.len(), 0);

    let ack = ship.handle(ClientId(2), RecvShipPacket::ItemsToTrade(ItemsToTrade {
        trade_target: 0,
        unknown2: 0,
        count: 0,
        items: Default::default(),
    })).await.unwrap();
    assert_eq!(ack.len(), 2);
    assert!(matches!(ack[0], (ClientId(2), SendShipPacket::AcknowledgeTrade(AcknowledgeTrade {}))));
    assert!(matches!(ack[1], (ClientId(1), SendShipPacket::AcknowledgeTrade(AcknowledgeTrade {}))));

    let ack = ship.handle(ClientId(1), RecvShipPacket::TradeConfirmed(TradeConfirmed {
    })).await.unwrap();
    assert_eq!(ack.len(), 0);

    let ack = ship.handle(ClientId(2), RecvShipPacket::TradeConfirmed(TradeConfirmed {
    })).await.unwrap();
    assert_eq!(ack.len(), 5);
    assert!(matches!(ack[0], (ClientId(2), SendShipPacket::Message(Message {
        msg: GameMessage::PlayerNoLongerHasItem(PlayerNoLongerHasItem {..}),
        ..
    }))));
    assert!(matches!(ack[1], (ClientId(1), SendShipPacket::Message(Message {
        msg: GameMessage::CreateItem(CreateItem {..}),
        ..
    }))));
    assert!(matches!(ack[2], (ClientId(2), SendShipPacket::Message(Message {
        msg: GameMessage::CreateItem(CreateItem {..}),
        ..
    }))));
    assert!(matches!(ack[3], (ClientId(2), SendShipPacket::TradeSuccessful {..})));
    assert!(matches!(ack[4], (ClientId(1), SendShipPacket::TradeSuccessful {..})));

    let p1_items = entity_gateway.get_character_inventory(&char1.id).await.unwrap();
    assert_eq!(p1_items.items.len(), 1);
    assert!(matches!(p1_items.items[0].with_stacked(|i| i.clone()).unwrap()[0], item::ItemEntity{item: item::ItemDetail::Tool(item::tool::Tool {tool: item::tool::ToolType::Monomate, ..}), ..}));
    assert_eq!(p1_items.items[0].with_stacked(|i| i.clone()).unwrap().len(), 2);
    let p2_items = entity_gateway.get_character_inventory(&char2.id).await.unwrap();
    assert_eq!(p2_items.items.len(), 1);
    assert_eq!(p2_items.items[0].with_stacked(|i| i.clone()).unwrap().len(), 2);
    assert!(matches!(p2_items.items[0].with_stacked(|i| i.clone()).unwrap()[0], item::ItemEntity{item: item::ItemDetail::Tool(item::tool::Tool {tool: item::tool::ToolType::Monofluid, ..}), ..}));
}

#[async_std::test]
async fn test_add_then_remove_partial_stack() {
    let mut entity_gateway = InMemoryGateway::default();

    let (_user1, char1) = new_user_character(&mut entity_gateway, "a1", "a", 1).await;
    let (_user2, char2) = new_user_character(&mut entity_gateway, "a2", "a", 1).await;

    let p1_stack1 = futures::future::join_all((0..2).map(|_| {
        let mut entity_gateway = entity_gateway.clone();
        async move {
            entity_gateway.create_item(
                item::NewItemEntity {
                    item: item::ItemDetail::Tool(
                        item::tool::Tool {
                            tool: item::tool::ToolType::Monomate,
                        }
                    )
                }).await
        }}))
        .await
        .into_iter()
        .collect::<Result<Vec<ItemEntity>,_>>()
        .unwrap();

    let p1_stack2 = futures::future::join_all((0..2).map(|_| {
        let mut entity_gateway = entity_gateway.clone();
        async move {
            entity_gateway.create_item(
                item::NewItemEntity {
                    item: item::ItemDetail::Tool(
                        item::tool::Tool {
                            tool: item::tool::ToolType::Monofluid,
                        }
                    )
                }).await
        }}))
        .await
        .into_iter()
        .collect::<Result<Vec<ItemEntity>,_>>()
        .unwrap();

    entity_gateway.set_character_inventory(&char1.id, &item::InventoryEntity::new(vec![p1_stack1, p1_stack2])).await.unwrap();
    entity_gateway.set_character_inventory(&char2.id, &item::InventoryEntity::new(Vec::<item::InventoryItemEntity>::new())).await.unwrap();

    let mut ship = Box::new(ShipServerState::builder()
        .gateway(entity_gateway.clone())
        .build());
    log_in_char(&mut ship, ClientId(1), "a1", "a").await;
    log_in_char(&mut ship, ClientId(2), "a2", "a").await;

    join_lobby(&mut ship, ClientId(1)).await;
    join_lobby(&mut ship, ClientId(2)).await;

    create_room(&mut ship, ClientId(1), "room", "").await;
    join_room(&mut ship, ClientId(2), 0).await;

    let p1_items = entity_gateway.get_character_inventory(&char1.id).await.unwrap();
    assert_eq!(p1_items.items.len(), 2);
    let p2_items = entity_gateway.get_character_inventory(&char2.id).await.unwrap();
    assert_eq!(p2_items.items.len(), 0);

    initialize_trade(&mut ship, ClientId(1), ClientId(2)).await;

    ship.handle(ClientId(1), RecvShipPacket::DirectMessage(DirectMessage::new(1, GameMessage::TradeRequest(TradeRequest {
        client: 1,
        target: 0,
        trade: TradeRequestCommand::AddItem(0x10000, 2)
    })))).await.unwrap();
    ship.handle(ClientId(1), RecvShipPacket::DirectMessage(DirectMessage::new(1, GameMessage::TradeRequest(TradeRequest {
        client: 1,
        target: 0,
        trade: TradeRequestCommand::AddItem(0x10001, 2)
    })))).await.unwrap();
    ship.handle(ClientId(1), RecvShipPacket::DirectMessage(DirectMessage::new(1, GameMessage::TradeRequest(TradeRequest {
        client: 1,
        target: 0,
        trade: TradeRequestCommand::RemoveItem(0x10000, 1)
    })))).await.unwrap();

    confirm_trade(&mut ship, ClientId(1), ClientId(2)).await;
    finalconfirm_trade(&mut ship, ClientId(1), ClientId(2)).await;

    let titems = TradeItemBuilder::default()
        .stacked(&p1_items.items[0], 0x10000, 1)
        .stacked(&p1_items.items[1], 0x10001, 2)
        .build();
    let ack = ship.handle(ClientId(1), RecvShipPacket::ItemsToTrade(ItemsToTrade {
        trade_target: 1,
        unknown2: 0,
        count: 2,
        items: titems,
    })).await.unwrap();
    assert_eq!(ack.len(), 0);

    let ack = ship.handle(ClientId(2), RecvShipPacket::ItemsToTrade(ItemsToTrade {
        trade_target: 0,
        unknown2: 0,
        count: 0,
        items: Default::default(),
    })).await.unwrap();
    assert_eq!(ack.len(), 2);
    assert!(matches!(ack[0], (ClientId(2), SendShipPacket::AcknowledgeTrade(AcknowledgeTrade {}))));
    assert!(matches!(ack[1], (ClientId(1), SendShipPacket::AcknowledgeTrade(AcknowledgeTrade {}))));

    let ack = ship.handle(ClientId(1), RecvShipPacket::TradeConfirmed(TradeConfirmed {
    })).await.unwrap();
    assert_eq!(ack.len(), 0);

    let ack = ship.handle(ClientId(2), RecvShipPacket::TradeConfirmed(TradeConfirmed {
    })).await.unwrap();
    assert_eq!(ack.len(), 8);

    let p1_items = entity_gateway.get_character_inventory(&char1.id).await.unwrap();
    assert_eq!(p1_items.items.len(), 1);
    assert_eq!(p1_items.items[0].with_stacked(|i| i.clone()).unwrap().len(), 1);
    assert!(matches!(p1_items.items[0].with_stacked(|i| i.clone()).unwrap()[0], item::ItemEntity{item: item::ItemDetail::Tool(item::tool::Tool {tool: item::tool::ToolType::Monomate, ..}), ..}));
    let p2_items = entity_gateway.get_character_inventory(&char2.id).await.unwrap();
    assert_eq!(p2_items.items.len(), 2);
    assert_eq!(p2_items.items[0].with_stacked(|i| i.clone()).unwrap().len(), 1);
    assert_eq!(p2_items.items[1].with_stacked(|i| i.clone()).unwrap().len(), 2);
    assert!(matches!(p2_items.items[0].with_stacked(|i| i.clone()).unwrap()[0], item::ItemEntity{item: item::ItemDetail::Tool(item::tool::Tool {tool: item::tool::ToolType::Monomate, ..}), ..}));
    assert!(matches!(p2_items.items[1].with_stacked(|i| i.clone()).unwrap()[0], item::ItemEntity{item: item::ItemDetail::Tool(item::tool::Tool {tool: item::tool::ToolType::Monofluid, ..}), ..}));
}

#[async_std::test]
async fn test_add_then_remove_meseta() {
    let mut entity_gateway = InMemoryGateway::default();

    let (_user1, char1) = new_user_character(&mut entity_gateway, "a1", "a", 1).await;
    let (_user2, char2) = new_user_character(&mut entity_gateway, "a2", "a", 1).await;

    entity_gateway.set_character_meseta(&char1.id, Meseta(2323)).await.unwrap();

    let mut ship = Box::new(ShipServerState::builder()
        .gateway(entity_gateway.clone())
        .build());
    log_in_char(&mut ship, ClientId(1), "a1", "a").await;
    log_in_char(&mut ship, ClientId(2), "a2", "a").await;

    join_lobby(&mut ship, ClientId(1)).await;
    join_lobby(&mut ship, ClientId(2)).await;

    create_room(&mut ship, ClientId(1), "room", "").await;
    join_room(&mut ship, ClientId(2), 0).await;

    initialize_trade(&mut ship, ClientId(1), ClientId(2)).await;

    ship.handle(ClientId(1), RecvShipPacket::DirectMessage(DirectMessage::new(1, GameMessage::TradeRequest(TradeRequest {
        client: 1,
        target: 0,
        trade: TradeRequestCommand::AddItem(0xFFFFFF01, 23)
    })))).await.unwrap();
    ship.handle(ClientId(1), RecvShipPacket::DirectMessage(DirectMessage::new(1, GameMessage::TradeRequest(TradeRequest {
        client: 1,
        target: 0,
        trade: TradeRequestCommand::RemoveItem(0xFFFFFF01, 5)
    })))).await.unwrap();

    confirm_trade(&mut ship, ClientId(1), ClientId(2)).await;
    finalconfirm_trade(&mut ship, ClientId(1), ClientId(2)).await;

    let titems = TradeItemBuilder::default()
        .meseta(18)
        .build();
    let ack = ship.handle(ClientId(1), RecvShipPacket::ItemsToTrade(ItemsToTrade {
        trade_target: 1,
        unknown2: 0,
        count: 1,
        items: titems,
    })).await.unwrap();
    assert_eq!(ack.len(), 0);

    let ack = ship.handle(ClientId(2), RecvShipPacket::ItemsToTrade(ItemsToTrade {
        trade_target: 0,
        unknown2: 0,
        count: 0,
        items: Default::default(),
    })).await.unwrap();
    assert_eq!(ack.len(), 2);
    assert!(matches!(ack[0], (ClientId(2), SendShipPacket::AcknowledgeTrade(AcknowledgeTrade {}))));
    assert!(matches!(ack[1], (ClientId(1), SendShipPacket::AcknowledgeTrade(AcknowledgeTrade {}))));

    let ack = ship.handle(ClientId(1), RecvShipPacket::TradeConfirmed(TradeConfirmed {
    })).await.unwrap();
    assert_eq!(ack.len(), 0);

    let ack = ship.handle(ClientId(2), RecvShipPacket::TradeConfirmed(TradeConfirmed {
    })).await.unwrap();
    assert_eq!(ack.len(), 5);
    assert!(matches!(ack[0], (ClientId(2), SendShipPacket::Message(Message {
        msg: GameMessage::PlayerNoLongerHasItem(PlayerNoLongerHasItem {..}),
        ..
    }))));
    assert!(matches!(ack[1], (ClientId(1), SendShipPacket::Message(Message {
        msg: GameMessage::CreateItem(CreateItem {..}),
        ..
    }))));
    assert!(matches!(ack[2], (ClientId(2), SendShipPacket::Message(Message {
        msg: GameMessage::CreateItem(CreateItem {..}),
        ..
    }))));
    assert!(matches!(ack[3], (ClientId(2), SendShipPacket::TradeSuccessful {..})));
    assert!(matches!(ack[4], (ClientId(1), SendShipPacket::TradeSuccessful {..})));

    let c1_meseta = entity_gateway.get_character_meseta(&char1.id).await.unwrap();
    assert_eq!(c1_meseta, Meseta(2305));
    let c2_meseta = entity_gateway.get_character_meseta(&char2.id).await.unwrap();
    assert_eq!(c2_meseta, Meseta(18));
}

#[async_std::test]
async fn test_items_to_trade_data_does_not_match() {
    let mut entity_gateway = InMemoryGateway::default();

    let (_user1, char1) = new_user_character(&mut entity_gateway, "a1", "a", 1).await;
    let (_user2, char2) = new_user_character(&mut entity_gateway, "a2", "a", 1).await;

    let mut p1_inv = Vec::new();
    p1_inv.push(entity_gateway.create_item(
            item::NewItemEntity {
                item: item::ItemDetail::Weapon(
                    item::weapon::Weapon {
                        weapon: item::weapon::WeaponType::Handgun,
                        grind: 0,
                        special: None,
                        attrs: [None, None, None],
                        tekked: true,
                    }
                ),
            }).await.unwrap());

    entity_gateway.set_character_inventory(&char1.id, &item::InventoryEntity::new(p1_inv)).await.unwrap();
    entity_gateway.set_character_inventory(&char2.id, &item::InventoryEntity::new(Vec::<item::InventoryItemEntity>::new())).await.unwrap();

    let mut ship = Box::new(ShipServerState::builder()
        .gateway(entity_gateway.clone())
        .build());
    log_in_char(&mut ship, ClientId(1), "a1", "a").await;
    log_in_char(&mut ship, ClientId(2), "a2", "a").await;

    join_lobby(&mut ship, ClientId(1)).await;
    join_lobby(&mut ship, ClientId(2)).await;

    create_room(&mut ship, ClientId(1), "room", "").await;
    join_room(&mut ship, ClientId(2), 0).await;

    let p1_items = entity_gateway.get_character_inventory(&char1.id).await.unwrap();
    assert_eq!(p1_items.items.len(), 1);
    let p2_items = entity_gateway.get_character_inventory(&char2.id).await.unwrap();
    assert_eq!(p2_items.items.len(), 0);

    initialize_trade(&mut ship, ClientId(1), ClientId(2)).await;

    ship.handle(ClientId(1), RecvShipPacket::DirectMessage(DirectMessage::new(1, GameMessage::TradeRequest(TradeRequest {
        client: 1,
        target: 0,
        trade: TradeRequestCommand::AddItem(0x10000, 1)
    })))).await.unwrap();

    confirm_trade(&mut ship, ClientId(1), ClientId(2)).await;
    finalconfirm_trade(&mut ship, ClientId(1), ClientId(2)).await;

    let new_item = item::InventoryItemEntity::Individual(
        ItemEntity {
            id: p1_items.items[0].with_individual(|i| i.id).unwrap(),
            item: item::ItemDetail::Weapon(
                item::weapon::Weapon {
                    weapon: item::weapon::WeaponType::Handgun,
                    grind: 2,
                    special: None,
                    attrs: [None, None, None],
                    tekked: true,
                }
            )});
    let titems = TradeItemBuilder::default()
        .individual(&new_item, 0x10000)
        .build();
    let ack = ship.handle(ClientId(1), RecvShipPacket::ItemsToTrade(ItemsToTrade {
        trade_target: 1,
        unknown2: 0,
        count: 1,
        items: titems,
    })).await.unwrap();
    assert_eq!(ack.len(), 2);
    assert!(matches!(ack[0], (ClientId(2), SendShipPacket::CancelTrade(..))));
    assert!(matches!(ack[1], (ClientId(1), SendShipPacket::CancelTrade(..))));

    let p1_items = entity_gateway.get_character_inventory(&char1.id).await.unwrap();
    assert_eq!(p1_items.items.len(), 1);
    let p2_items = entity_gateway.get_character_inventory(&char2.id).await.unwrap();
    assert_eq!(p2_items.items.len(), 0);
}

#[async_std::test]
async fn test_items_to_trade_id_does_not_match() {
    let mut entity_gateway = InMemoryGateway::default();

    let (_user1, char1) = new_user_character(&mut entity_gateway, "a1", "a", 1).await;
    let (_user2, char2) = new_user_character(&mut entity_gateway, "a2", "a", 1).await;

    let mut p1_inv = Vec::new();
    p1_inv.push(entity_gateway.create_item(
            item::NewItemEntity {
                item: item::ItemDetail::Weapon(
                    item::weapon::Weapon {
                        weapon: item::weapon::WeaponType::Handgun,
                        grind: 0,
                        special: None,
                        attrs: [None, None, None],
                        tekked: true,
                    }
                ),
            }).await.unwrap());

    entity_gateway.set_character_inventory(&char1.id, &item::InventoryEntity::new(p1_inv)).await.unwrap();
    entity_gateway.set_character_inventory(&char2.id, &item::InventoryEntity::new(Vec::<item::InventoryItemEntity>::new())).await.unwrap();

    let mut ship = Box::new(ShipServerState::builder()
        .gateway(entity_gateway.clone())
        .build());
    log_in_char(&mut ship, ClientId(1), "a1", "a").await;
    log_in_char(&mut ship, ClientId(2), "a2", "a").await;

    join_lobby(&mut ship, ClientId(1)).await;
    join_lobby(&mut ship, ClientId(2)).await;

    create_room(&mut ship, ClientId(1), "room", "").await;
    join_room(&mut ship, ClientId(2), 0).await;

    let p1_items = entity_gateway.get_character_inventory(&char1.id).await.unwrap();
    assert_eq!(p1_items.items.len(), 1);
    let p2_items = entity_gateway.get_character_inventory(&char2.id).await.unwrap();
    assert_eq!(p2_items.items.len(), 0);

    initialize_trade(&mut ship, ClientId(1), ClientId(2)).await;

    ship.handle(ClientId(1), RecvShipPacket::DirectMessage(DirectMessage::new(1, GameMessage::TradeRequest(TradeRequest {
        client: 1,
        target: 0,
        trade: TradeRequestCommand::AddItem(0x10000, 1)
    })))).await.unwrap();

    confirm_trade(&mut ship, ClientId(1), ClientId(2)).await;
    finalconfirm_trade(&mut ship, ClientId(1), ClientId(2)).await;

    let titems = TradeItemBuilder::default()
        .individual(&p1_items.items[0], 0x10001)
        .build();
    let ack = ship.handle(ClientId(1), RecvShipPacket::ItemsToTrade(ItemsToTrade {
        trade_target: 1,
        unknown2: 0,
        count: 1,
        items: titems,
    })).await.unwrap();
    assert_eq!(ack.len(), 2);
    assert!(matches!(ack[0], (ClientId(2), SendShipPacket::CancelTrade(..))));
    assert!(matches!(ack[1], (ClientId(1), SendShipPacket::CancelTrade(..))));

    let p1_items = entity_gateway.get_character_inventory(&char1.id).await.unwrap();
    assert_eq!(p1_items.items.len(), 1);
    let p2_items = entity_gateway.get_character_inventory(&char2.id).await.unwrap();
    assert_eq!(p2_items.items.len(), 0);
}

#[async_std::test]
async fn test_stack_is_same_amount_in_request_and_items_to_trade() {
    let mut entity_gateway = InMemoryGateway::default();

    let (_user1, char1) = new_user_character(&mut entity_gateway, "a1", "a", 1).await;
    let (_user2, char2) = new_user_character(&mut entity_gateway, "a2", "a", 1).await;

    let p1_stack = futures::future::join_all((0..2).map(|_| {
        let mut entity_gateway = entity_gateway.clone();
        async move {
            entity_gateway.create_item(
                item::NewItemEntity {
                    item: item::ItemDetail::Tool(
                        item::tool::Tool {
                            tool: item::tool::ToolType::Monomate,
                        }
                    )
                }).await
        }}))
        .await
        .into_iter()
        .collect::<Result<Vec<ItemEntity>,_>>()
        .unwrap();

    entity_gateway.set_character_inventory(&char1.id, &item::InventoryEntity::new(vec![p1_stack])).await.unwrap();
    entity_gateway.set_character_inventory(&char2.id, &item::InventoryEntity::new(Vec::<item::InventoryItemEntity>::new())).await.unwrap();

    let mut ship = Box::new(ShipServerState::builder()
        .gateway(entity_gateway.clone())
        .build());
    log_in_char(&mut ship, ClientId(1), "a1", "a").await;
    log_in_char(&mut ship, ClientId(2), "a2", "a").await;

    join_lobby(&mut ship, ClientId(1)).await;
    join_lobby(&mut ship, ClientId(2)).await;

    create_room(&mut ship, ClientId(1), "room", "").await;
    join_room(&mut ship, ClientId(2), 0).await;

    let p1_items = entity_gateway.get_character_inventory(&char1.id).await.unwrap();
    assert_eq!(p1_items.items.len(), 1);
    let p2_items = entity_gateway.get_character_inventory(&char2.id).await.unwrap();
    assert_eq!(p2_items.items.len(), 0);

    initialize_trade(&mut ship, ClientId(1), ClientId(2)).await;

    ship.handle(ClientId(1), RecvShipPacket::DirectMessage(DirectMessage::new(1, GameMessage::TradeRequest(TradeRequest {
        client: 1,
        target: 0,
        trade: TradeRequestCommand::AddItem(0x10000, 2)
    })))).await.unwrap();

    confirm_trade(&mut ship, ClientId(1), ClientId(2)).await;
    finalconfirm_trade(&mut ship, ClientId(1), ClientId(2)).await;

    let titems = TradeItemBuilder::default()
        .stacked(&p1_items.items[0], 0x10000, 1)
        .build();
    let ack = ship.handle(ClientId(1), RecvShipPacket::ItemsToTrade(ItemsToTrade {
        trade_target: 1,
        unknown2: 0,
        count: 1,
        items: titems,
    })).await.unwrap();
    assert_eq!(ack.len(), 2);
    assert!(matches!(ack[0], (ClientId(2), SendShipPacket::CancelTrade(..))));
    assert!(matches!(ack[1], (ClientId(1), SendShipPacket::CancelTrade(..))));

    let p1_items = entity_gateway.get_character_inventory(&char1.id).await.unwrap();
    assert_eq!(p1_items.items.len(), 1);
    let p2_items = entity_gateway.get_character_inventory(&char2.id).await.unwrap();
    assert_eq!(p2_items.items.len(), 0);
}

#[async_std::test]
async fn test_stack_is_same_amount_in_request_and_items_to_trade2() {
    let mut entity_gateway = InMemoryGateway::default();

    let (_user1, char1) = new_user_character(&mut entity_gateway, "a1", "a", 1).await;
    let (_user2, char2) = new_user_character(&mut entity_gateway, "a2", "a", 1).await;

    let p1_stack = futures::future::join_all((0..2).map(|_| {
        let mut entity_gateway = entity_gateway.clone();
        async move {
            entity_gateway.create_item(
                item::NewItemEntity {
                    item: item::ItemDetail::Tool(
                        item::tool::Tool {
                            tool: item::tool::ToolType::Monomate,
                        }
                    )
                }).await
        }}))
        .await
        .into_iter()
        .collect::<Result<Vec<ItemEntity>,_>>()
        .unwrap();

    entity_gateway.set_character_inventory(&char1.id, &item::InventoryEntity::new(vec![p1_stack])).await.unwrap();
    entity_gateway.set_character_inventory(&char2.id, &item::InventoryEntity::new(Vec::<item::InventoryItemEntity>::new())).await.unwrap();

    let mut ship = Box::new(ShipServerState::builder()
        .gateway(entity_gateway.clone())
        .build());
    log_in_char(&mut ship, ClientId(1), "a1", "a").await;
    log_in_char(&mut ship, ClientId(2), "a2", "a").await;

    join_lobby(&mut ship, ClientId(1)).await;
    join_lobby(&mut ship, ClientId(2)).await;

    create_room(&mut ship, ClientId(1), "room", "").await;
    join_room(&mut ship, ClientId(2), 0).await;

    let p1_items = entity_gateway.get_character_inventory(&char1.id).await.unwrap();
    assert_eq!(p1_items.items.len(), 1);
    let p2_items = entity_gateway.get_character_inventory(&char2.id).await.unwrap();
    assert_eq!(p2_items.items.len(), 0);

    initialize_trade(&mut ship, ClientId(1), ClientId(2)).await;

    ship.handle(ClientId(1), RecvShipPacket::DirectMessage(DirectMessage::new(1, GameMessage::TradeRequest(TradeRequest {
        client: 1,
        target: 0,
        trade: TradeRequestCommand::AddItem(0x10000, 1)
    })))).await.unwrap();

    confirm_trade(&mut ship, ClientId(1), ClientId(2)).await;
    finalconfirm_trade(&mut ship, ClientId(1), ClientId(2)).await;

    let titems = TradeItemBuilder::default()
        .stacked(&p1_items.items[0], 0x10000, 2)
        .build();
    let ack = ship.handle(ClientId(1), RecvShipPacket::ItemsToTrade(ItemsToTrade {
        trade_target: 1,
        unknown2: 0,
        count: 1,
        items: titems,
    })).await.unwrap();
    assert_eq!(ack.len(), 2);
    assert!(matches!(ack[0], (ClientId(2), SendShipPacket::CancelTrade(..))));
    assert!(matches!(ack[1], (ClientId(1), SendShipPacket::CancelTrade(..))));

    let p1_items = entity_gateway.get_character_inventory(&char1.id).await.unwrap();
    assert_eq!(p1_items.items.len(), 1);
    let p2_items = entity_gateway.get_character_inventory(&char2.id).await.unwrap();
    assert_eq!(p2_items.items.len(), 0);
}

#[async_std::test]
async fn test_items_to_trade_count_less_than() {
    let mut entity_gateway = InMemoryGateway::default();

    let (_user1, char1) = new_user_character(&mut entity_gateway, "a1", "a", 1).await;
    let (_user2, char2) = new_user_character(&mut entity_gateway, "a2", "a", 1).await;

    let p1_inv = vec![
        entity_gateway.create_item(
            item::NewItemEntity {
                item: item::ItemDetail::Weapon(
                    item::weapon::Weapon {
                        weapon: item::weapon::WeaponType::Saber,
                        grind: 0,
                        special: None,
                        attrs: [None, None, None],
                        tekked: true,
                    }
                ),
            }).await.unwrap(),
        entity_gateway.create_item(
            item::NewItemEntity {
                item: item::ItemDetail::Weapon(
                    item::weapon::Weapon {
                        weapon: item::weapon::WeaponType::Brand,
                        grind: 0,
                        special: None,
                        attrs: [None, None, None],
                        tekked: true,
                    }
                ),
            }).await.unwrap(),
        entity_gateway.create_item(
            item::NewItemEntity {
                item: item::ItemDetail::Weapon(
                    item::weapon::Weapon {
                        weapon: item::weapon::WeaponType::Buster,
                        grind: 0,
                        special: None,
                        attrs: [None, None, None],
                        tekked: true,
                    }
                ),
            }).await.unwrap(),
    ];

    entity_gateway.set_character_inventory(&char1.id, &item::InventoryEntity::new(p1_inv)).await.unwrap();
    entity_gateway.set_character_inventory(&char2.id, &item::InventoryEntity::new(Vec::<item::InventoryItemEntity>::new())).await.unwrap();

    let mut ship = Box::new(ShipServerState::builder()
        .gateway(entity_gateway.clone())
        .build());
    log_in_char(&mut ship, ClientId(1), "a1", "a").await;
    log_in_char(&mut ship, ClientId(2), "a2", "a").await;

    join_lobby(&mut ship, ClientId(1)).await;
    join_lobby(&mut ship, ClientId(2)).await;

    create_room(&mut ship, ClientId(1), "room", "").await;
    join_room(&mut ship, ClientId(2), 0).await;

    let p1_items = entity_gateway.get_character_inventory(&char1.id).await.unwrap();
    assert_eq!(p1_items.items.len(), 3);
    let p2_items = entity_gateway.get_character_inventory(&char2.id).await.unwrap();
    assert_eq!(p2_items.items.len(), 0);

    initialize_trade(&mut ship, ClientId(1), ClientId(2)).await;

    ship.handle(ClientId(1), RecvShipPacket::DirectMessage(DirectMessage::new(1, GameMessage::TradeRequest(TradeRequest {
        client: 1,
        target: 0,
        trade: TradeRequestCommand::AddItem(0x10000, 1)
    })))).await.unwrap();
    ship.handle(ClientId(1), RecvShipPacket::DirectMessage(DirectMessage::new(1, GameMessage::TradeRequest(TradeRequest {
        client: 1,
        target: 0,
        trade: TradeRequestCommand::AddItem(0x10001, 1)
    })))).await.unwrap();

    confirm_trade(&mut ship, ClientId(1), ClientId(2)).await;
    finalconfirm_trade(&mut ship, ClientId(1), ClientId(2)).await;

    let titems = TradeItemBuilder::default()
        .individual(&p1_items.items[0], 0x10000)
        .individual(&p1_items.items[1], 0x10001)
        .build();
    let ack = ship.handle(ClientId(1), RecvShipPacket::ItemsToTrade(ItemsToTrade {
        trade_target: 1,
        unknown2: 0,
        count: 1,
        items: titems,
    })).await.unwrap();
    assert_eq!(ack.len(), 2);
    assert!(matches!(ack[0], (ClientId(2), SendShipPacket::CancelTrade(..))));
    assert!(matches!(ack[1], (ClientId(1), SendShipPacket::CancelTrade(..))));

    let p1_items = entity_gateway.get_character_inventory(&char1.id).await.unwrap();
    assert_eq!(p1_items.items.len(), 3);
    let p2_items = entity_gateway.get_character_inventory(&char2.id).await.unwrap();
    assert_eq!(p2_items.items.len(), 0);
}

#[async_std::test]
async fn test_items_to_trade_count_greater_than() {
    let mut entity_gateway = InMemoryGateway::default();

    let (_user1, char1) = new_user_character(&mut entity_gateway, "a1", "a", 1).await;
    let (_user2, char2) = new_user_character(&mut entity_gateway, "a2", "a", 1).await;

    entity_gateway.set_character_meseta(&char1.id, Meseta(23)).await.unwrap();

    let p1_inv = vec![
        entity_gateway.create_item(
            item::NewItemEntity {
                item: item::ItemDetail::Weapon(
                    item::weapon::Weapon {
                        weapon: item::weapon::WeaponType::Saber,
                        grind: 0,
                        special: None,
                        attrs: [None, None, None],
                        tekked: true,
                    }
                ),
            }).await.unwrap(),
        entity_gateway.create_item(
            item::NewItemEntity {
                item: item::ItemDetail::Weapon(
                    item::weapon::Weapon {
                        weapon: item::weapon::WeaponType::Brand,
                        grind: 0,
                        special: None,
                        attrs: [None, None, None],
                        tekked: true,
                    }
                ),
            }).await.unwrap(),
        entity_gateway.create_item(
            item::NewItemEntity {
                item: item::ItemDetail::Weapon(
                    item::weapon::Weapon {
                        weapon: item::weapon::WeaponType::Buster,
                        grind: 0,
                        special: None,
                        attrs: [None, None, None],
                        tekked: true,
                    }
                ),
            }).await.unwrap(),
    ];

    entity_gateway.set_character_inventory(&char1.id, &item::InventoryEntity::new(p1_inv)).await.unwrap();
    entity_gateway.set_character_inventory(&char2.id, &item::InventoryEntity::new(Vec::<item::InventoryItemEntity>::new())).await.unwrap();

    let mut ship = Box::new(ShipServerState::builder()
        .gateway(entity_gateway.clone())
        .build());
    log_in_char(&mut ship, ClientId(1), "a1", "a").await;
    log_in_char(&mut ship, ClientId(2), "a2", "a").await;

    join_lobby(&mut ship, ClientId(1)).await;
    join_lobby(&mut ship, ClientId(2)).await;

    create_room(&mut ship, ClientId(1), "room", "").await;
    join_room(&mut ship, ClientId(2), 0).await;

    let p1_items = entity_gateway.get_character_inventory(&char1.id).await.unwrap();
    assert_eq!(p1_items.items.len(), 3);
    let p2_items = entity_gateway.get_character_inventory(&char2.id).await.unwrap();
    assert_eq!(p2_items.items.len(), 0);

    initialize_trade(&mut ship, ClientId(1), ClientId(2)).await;

    ship.handle(ClientId(1), RecvShipPacket::DirectMessage(DirectMessage::new(1, GameMessage::TradeRequest(TradeRequest {
        client: 1,
        target: 0,
        trade: TradeRequestCommand::AddItem(0x10000, 1)
    })))).await.unwrap();
    ship.handle(ClientId(1), RecvShipPacket::DirectMessage(DirectMessage::new(1, GameMessage::TradeRequest(TradeRequest {
        client: 1,
        target: 0,
        trade: TradeRequestCommand::AddItem(0x10001, 1)
    })))).await.unwrap();
    ship.handle(ClientId(1), RecvShipPacket::DirectMessage(DirectMessage::new(1, GameMessage::TradeRequest(TradeRequest {
        client: 1,
        target: 0,
        trade: TradeRequestCommand::AddItem(0xFFFFFF01, 5)
    })))).await.unwrap();

    confirm_trade(&mut ship, ClientId(1), ClientId(2)).await;
    finalconfirm_trade(&mut ship, ClientId(1), ClientId(2)).await;

    let titems = TradeItemBuilder::default()
        .individual(&p1_items.items[0], 0x10000)
        .individual(&p1_items.items[1], 0x10001)
        .meseta(5)
        .build();
    let ack = ship.handle(ClientId(1), RecvShipPacket::ItemsToTrade(ItemsToTrade {
        trade_target: 1,
        unknown2: 0,
        count: 4,
        items: titems,
    })).await.unwrap();
    assert_eq!(ack.len(), 2);
    assert!(matches!(ack[0], (ClientId(2), SendShipPacket::CancelTrade(..))));
    assert!(matches!(ack[1], (ClientId(1), SendShipPacket::CancelTrade(..))));

    let p1_items = entity_gateway.get_character_inventory(&char1.id).await.unwrap();
    assert_eq!(p1_items.items.len(), 3);
    let p2_items = entity_gateway.get_character_inventory(&char2.id).await.unwrap();
    assert_eq!(p2_items.items.len(), 0);
}

#[async_std::test]
async fn test_items_to_trade_count_mismatch_with_meseta() {
    let mut entity_gateway = InMemoryGateway::default();

    let (_user1, char1) = new_user_character(&mut entity_gateway, "a1", "a", 1).await;
    let (_user2, char2) = new_user_character(&mut entity_gateway, "a2", "a", 1).await;

    let p1_inv = vec![
        entity_gateway.create_item(
            item::NewItemEntity {
                item: item::ItemDetail::Weapon(
                    item::weapon::Weapon {
                        weapon: item::weapon::WeaponType::Saber,
                        grind: 0,
                        special: None,
                        attrs: [None, None, None],
                        tekked: true,
                    }
                ),
            }).await.unwrap(),
        entity_gateway.create_item(
            item::NewItemEntity {
                item: item::ItemDetail::Weapon(
                    item::weapon::Weapon {
                        weapon: item::weapon::WeaponType::Brand,
                        grind: 0,
                        special: None,
                        attrs: [None, None, None],
                        tekked: true,
                    }
                ),
            }).await.unwrap(),
        entity_gateway.create_item(
            item::NewItemEntity {
                item: item::ItemDetail::Weapon(
                    item::weapon::Weapon {
                        weapon: item::weapon::WeaponType::Buster,
                        grind: 0,
                        special: None,
                        attrs: [None, None, None],
                        tekked: true,
                    }
                ),
            }).await.unwrap(),
    ];

    entity_gateway.set_character_inventory(&char1.id, &item::InventoryEntity::new(p1_inv)).await.unwrap();
    entity_gateway.set_character_inventory(&char2.id, &item::InventoryEntity::new(Vec::<item::InventoryItemEntity>::new())).await.unwrap();

    let mut ship = Box::new(ShipServerState::builder()
        .gateway(entity_gateway.clone())
        .build());
    log_in_char(&mut ship, ClientId(1), "a1", "a").await;
    log_in_char(&mut ship, ClientId(2), "a2", "a").await;

    join_lobby(&mut ship, ClientId(1)).await;
    join_lobby(&mut ship, ClientId(2)).await;

    create_room(&mut ship, ClientId(1), "room", "").await;
    join_room(&mut ship, ClientId(2), 0).await;

    let p1_items = entity_gateway.get_character_inventory(&char1.id).await.unwrap();
    assert_eq!(p1_items.items.len(), 3);
    let p2_items = entity_gateway.get_character_inventory(&char2.id).await.unwrap();
    assert_eq!(p2_items.items.len(), 0);

    initialize_trade(&mut ship, ClientId(1), ClientId(2)).await;

    ship.handle(ClientId(1), RecvShipPacket::DirectMessage(DirectMessage::new(1, GameMessage::TradeRequest(TradeRequest {
        client: 1,
        target: 0,
        trade: TradeRequestCommand::AddItem(0x10000, 1)
    })))).await.unwrap();
    ship.handle(ClientId(1), RecvShipPacket::DirectMessage(DirectMessage::new(1, GameMessage::TradeRequest(TradeRequest {
        client: 1,
        target: 0,
        trade: TradeRequestCommand::AddItem(0x10001, 1)
    })))).await.unwrap();

    confirm_trade(&mut ship, ClientId(1), ClientId(2)).await;
    finalconfirm_trade(&mut ship, ClientId(1), ClientId(2)).await;

    let titems = TradeItemBuilder::default()
        .individual(&p1_items.items[0], 0x10000)
        .individual(&p1_items.items[1], 0x10001)
        .build();
    let ack = ship.handle(ClientId(1), RecvShipPacket::ItemsToTrade(ItemsToTrade {
        trade_target: 1,
        unknown2: 0,
        count: 3,
        items: titems,
    })).await.unwrap();
    assert_eq!(ack.len(), 2);
    assert!(matches!(ack[0], (ClientId(2), SendShipPacket::CancelTrade(..))));
    assert!(matches!(ack[1], (ClientId(1), SendShipPacket::CancelTrade(..))));

    let p1_items = entity_gateway.get_character_inventory(&char1.id).await.unwrap();
    assert_eq!(p1_items.items.len(), 3);
    let p2_items = entity_gateway.get_character_inventory(&char2.id).await.unwrap();
    assert_eq!(p2_items.items.len(), 0);
}

#[async_std::test]
async fn test_dropping_item_after_trade() {
    let mut entity_gateway = InMemoryGateway::default();

    let (_user1, char1) = new_user_character(&mut entity_gateway, "a1", "a", 1).await;
    let (_user2, char2) = new_user_character(&mut entity_gateway, "a2", "a", 1).await;

    let mut p1_inv = Vec::new();
    p1_inv.push(entity_gateway.create_item(
            item::NewItemEntity {
                item: item::ItemDetail::Weapon(
                    item::weapon::Weapon {
                        weapon: item::weapon::WeaponType::Handgun,
                        grind: 0,
                        special: None,
                        attrs: [None, None, None],
                        tekked: true,
                    }
                ),
            }).await.unwrap());

    entity_gateway.set_character_inventory(&char1.id, &item::InventoryEntity::new(p1_inv)).await.unwrap();
    entity_gateway.set_character_inventory(&char2.id, &item::InventoryEntity::new(Vec::<item::InventoryItemEntity>::new())).await.unwrap();

    let mut ship = Box::new(ShipServerState::builder()
        .gateway(entity_gateway.clone())
        .build());
    log_in_char(&mut ship, ClientId(1), "a1", "a").await;
    log_in_char(&mut ship, ClientId(2), "a2", "a").await;

    join_lobby(&mut ship, ClientId(1)).await;
    join_lobby(&mut ship, ClientId(2)).await;

    create_room(&mut ship, ClientId(1), "room", "").await;
    join_room(&mut ship, ClientId(2), 0).await;

    let p1_items = entity_gateway.get_character_inventory(&char1.id).await.unwrap();
    assert_eq!(p1_items.items.len(), 1);
    let p2_items = entity_gateway.get_character_inventory(&char2.id).await.unwrap();
    assert_eq!(p2_items.items.len(), 0);

    initialize_trade(&mut ship, ClientId(1), ClientId(2)).await;

    ship.handle(ClientId(1), RecvShipPacket::DirectMessage(DirectMessage::new(1, GameMessage::TradeRequest(TradeRequest {
        client: 1,
        target: 0,
        trade: TradeRequestCommand::AddItem(0x10000, 1)
    })))).await.unwrap();

    confirm_trade(&mut ship, ClientId(1), ClientId(2)).await;
    finalconfirm_trade(&mut ship, ClientId(1), ClientId(2)).await;

    let titems = TradeItemBuilder::default()
        .individual(&p1_items.items[0], 0x10000)
        .build();
    let ack = ship.handle(ClientId(1), RecvShipPacket::ItemsToTrade(ItemsToTrade {
        trade_target: 1,
        unknown2: 0,
        count: 1,
        items: titems,
    })).await.unwrap();
    assert_eq!(ack.len(), 0);

    let ack = ship.handle(ClientId(2), RecvShipPacket::ItemsToTrade(ItemsToTrade {
        trade_target: 0,
        unknown2: 0,
        count: 0,
        items: Default::default(),
    })).await.unwrap();
    assert_eq!(ack.len(), 2);
    assert!(matches!(ack[0], (ClientId(2), SendShipPacket::AcknowledgeTrade(AcknowledgeTrade {}))));
    assert!(matches!(ack[1], (ClientId(1), SendShipPacket::AcknowledgeTrade(AcknowledgeTrade {}))));

    let ack = ship.handle(ClientId(1), RecvShipPacket::TradeConfirmed(TradeConfirmed {
    })).await.unwrap();
    assert_eq!(ack.len(), 0);

    let ack = ship.handle(ClientId(2), RecvShipPacket::TradeConfirmed(TradeConfirmed {
    })).await.unwrap();
    assert_eq!(ack.len(), 5);
    assert!(matches!(ack[0], (ClientId(2), SendShipPacket::Message(Message {
        msg: GameMessage::PlayerNoLongerHasItem(PlayerNoLongerHasItem {..}),
        ..
    }))));
    assert!(matches!(ack[1], (ClientId(1), SendShipPacket::Message(Message {
        msg: GameMessage::CreateItem(CreateItem {..}),
        ..
    }))));
    assert!(matches!(ack[2], (ClientId(2), SendShipPacket::Message(Message {
        msg: GameMessage::CreateItem(CreateItem {..}),
        ..
    }))));
    assert!(matches!(ack[3], (ClientId(2), SendShipPacket::TradeSuccessful {..})));
    assert!(matches!(ack[4], (ClientId(1), SendShipPacket::TradeSuccessful {..})));

    let _ack = ship.handle(ClientId(2), RecvShipPacket::Message(Message::new(GameMessage::PlayerDropItem(PlayerDropItem {
        client: 0,
        target: 0,
        unknown1: 0,
        map_area: 0,
        item_id: 0x810001,
        x: 0.0,
        y: 0.0,
        z: 0.0,
    })))).await.unwrap();

    let p1_items = entity_gateway.get_character_inventory(&char1.id).await.unwrap();
    assert_eq!(p1_items.items.len(), 0);
    let p2_items = entity_gateway.get_character_inventory(&char2.id).await.unwrap();
    assert_eq!(p2_items.items.len(), 0);
}