Browse Source

Merge pull request 'item_refactor' (#250) from item_refactor into master

pbs
jake 4 years ago
parent
commit
9de04ad87b
  1. 7
      Cargo.toml
  2. 130
      src/bin/main.rs
  3. 16
      src/common/mainloop/client.rs
  4. 4
      src/common/mainloop/mod.rs
  5. 27
      src/entity/gateway/entitygateway.rs
  6. 142
      src/entity/gateway/inmemory.rs
  7. 24
      src/entity/gateway/postgres/migrations/V0001__initial.sql
  8. 93
      src/entity/gateway/postgres/models.rs
  9. 162
      src/entity/gateway/postgres/postgres.rs
  10. 137
      src/entity/item/mod.rs
  11. 3
      src/entity/item/unit.rs
  12. 49
      src/login/character.rs
  13. 6
      src/patch/patch.rs
  14. 2
      src/ship/drops/generic_unit.rs
  15. 1
      src/ship/drops/rare_drop_table.rs
  16. 38
      src/ship/items/bank.rs
  17. 142
      src/ship/items/inventory.rs
  18. 256
      src/ship/items/manager.rs
  19. 4
      src/ship/packet/handler/lobby.rs
  20. 31
      src/ship/packet/handler/message.rs
  21. 10
      src/ship/ship.rs
  22. 1
      src/ship/shops/armor.rs
  23. 847
      tests/test_bank.rs
  24. 4
      tests/test_character.rs
  25. 16
      tests/test_exp_gain.rs
  26. 204
      tests/test_item_actions.rs
  27. 286
      tests/test_item_pickup.rs
  28. 190
      tests/test_item_use.rs
  29. 95
      tests/test_mags.rs
  30. 25
      tests/test_rooms.rs
  31. 108
      tests/test_shops.rs

7
Cargo.toml

@ -6,15 +6,12 @@ edition = "2018"
[dependencies]
libpso = { git = "http://git.sharnoth.com/jake/libpso" }
async-std = { version = "1.5.0", features = ["unstable", "attributes"] }
async-std = { version = "1.7.0", features = ["unstable", "attributes"] }
futures = "0.3.5"
rand = "0.7.3"
rand_chacha = "0.2.2"
mio = "0.6"
mio-extras = "2.0.5"
crc = "^1.0.0"
bcrypt = "0.4"
threadpool = "1.0"
chrono = "0.4.11"
serde = "*"
serde_json = "*"
@ -27,7 +24,7 @@ enum-utils = "0.1.2"
derive_more = { version = "0.99.3", features = ["display"]}
thiserror = "1.0.15"
ages-prs = "0.1"
async-trait = "0.1.31"
async-trait = "0.1.41"
lazy_static = "1.4.0"
barrel = { version = "0.6.5", features = ["pg"] }
refinery = { version = "0.3.0", features = ["postgres"] }

130
src/bin/main.rs

@ -109,7 +109,7 @@ fn main() {
}).await.unwrap();
}
entity_gateway.create_item(
let item0 = entity_gateway.create_item(
NewItemEntity {
item: ItemDetail::Weapon(
item::weapon::Weapon {
@ -125,11 +125,9 @@ fn main() {
),
location: ItemLocation::Inventory {
character_id: character.id,
slot: 0,
equipped: false,
}
}).await.unwrap();
entity_gateway.create_item(
let item1 = entity_gateway.create_item(
NewItemEntity {
item: ItemDetail::Weapon(
item::weapon::Weapon {
@ -145,11 +143,9 @@ fn main() {
),
location: ItemLocation::Inventory {
character_id: character.id,
slot: 1,
equipped: false,
}
}).await.unwrap();
entity_gateway.create_item(
let item2_w = entity_gateway.create_item(
NewItemEntity {
item: ItemDetail::Weapon(
item::weapon::Weapon {
@ -165,11 +161,9 @@ fn main() {
),
location: ItemLocation::Inventory {
character_id: character.id,
slot: 2,
equipped: true,
}
}).await.unwrap();
entity_gateway.create_item(
let item3 = entity_gateway.create_item(
NewItemEntity {
item: ItemDetail::Weapon(
item::weapon::Weapon {
@ -185,11 +179,9 @@ fn main() {
),
location: ItemLocation::Inventory {
character_id: character.id,
slot: 3,
equipped: false,
}
}).await.unwrap();
entity_gateway.create_item(
let item4 = entity_gateway.create_item(
NewItemEntity {
item: ItemDetail::Weapon(
item::weapon::Weapon {
@ -205,22 +197,18 @@ fn main() {
),
location: ItemLocation::Inventory {
character_id: character.id,
slot: 4,
equipped: false,
}
}).await.unwrap();
let mag = entity_gateway.create_item(
let item5_m = entity_gateway.create_item(
item::NewItemEntity {
item: item::ItemDetail::Mag(item::mag::Mag::baby_mag(0)),
location: item::ItemLocation::Inventory {
character_id: character.id,
slot: 5,
equipped: true,
}
}).await.unwrap();
for _ in 0..10 {
for _ in 0..10usize {
let fed_tool = entity_gateway.create_item(
NewItemEntity {
item: ItemDetail::Tool (
@ -229,15 +217,14 @@ fn main() {
}
),
location: item::ItemLocation::FedToMag {
mag: mag.id,
mag: item5_m.id,
}
}).await.unwrap();
entity_gateway.feed_mag(&mag.id, &fed_tool.id).await.unwrap();
entity_gateway.feed_mag(&item5_m.id, &fed_tool.id).await.unwrap();
}
//entity_gateway.bank(&mag.id).await;
entity_gateway.change_mag_owner(&mag.id, &character).await.unwrap();
entity_gateway.change_mag_owner(&item5_m.id, &character).await.unwrap();
entity_gateway.create_item(
let item6 = entity_gateway.create_item(
item::NewItemEntity {
item: ItemDetail::Tool (
item::tool::Tool {
@ -246,8 +233,6 @@ fn main() {
),
location: item::ItemLocation::Inventory {
character_id: character.id,
slot: 6,
equipped: true,
}
}).await.unwrap();
let cell = entity_gateway.create_item(
@ -259,7 +244,7 @@ fn main() {
),
location: item::ItemLocation::Consumed,
}).await.unwrap();
entity_gateway.use_mag_cell(&mag.id, &cell.id).await.unwrap();
entity_gateway.use_mag_cell(&item5_m.id, &cell.id).await.unwrap();
entity_gateway.create_item(
NewItemEntity {
@ -279,8 +264,8 @@ fn main() {
character_id: character.id,
name: item::BankName("".to_string()),
}
}).await;
entity_gateway.create_item(
}).await.unwrap();
let item7_a = entity_gateway.create_item(
NewItemEntity {
item: ItemDetail::Armor(
item::armor::Armor {
@ -293,12 +278,10 @@ fn main() {
),
location: ItemLocation::Inventory {
character_id: character.id,
slot: 5,
equipped: true,
}
}
).await;
entity_gateway.create_item(
).await.unwrap();
let item8_s = entity_gateway.create_item(
NewItemEntity {
item: ItemDetail::Shield(
item::shield::Shield {
@ -309,133 +292,130 @@ fn main() {
),
location: ItemLocation::Inventory {
character_id: character.id,
slot: 6,
equipped: true,
}
}
).await;
entity_gateway.create_item(
).await.unwrap();
let item9_u0 = entity_gateway.create_item(
NewItemEntity {
item: ItemDetail::Unit(
item::unit::Unit {
unit: item::unit::UnitType::PriestMind,
modifier: Some(item::unit::UnitModifier::Minus),
armor_slot: 0,
}
),
location: ItemLocation::Inventory {
character_id: character.id,
slot: 7,
equipped: true,
}
}
).await;
entity_gateway.create_item(
).await.unwrap();
let item10_u1 = entity_gateway.create_item(
NewItemEntity {
item: ItemDetail::Unit(
item::unit::Unit {
unit: item::unit::UnitType::PriestMind,
modifier: Some(item::unit::UnitModifier::Minus),
armor_slot: 1,
}
),
location: ItemLocation::Inventory {
character_id: character.id,
slot: 8,
equipped: true,
}
}
).await;
entity_gateway.create_item(
).await.unwrap();
let item11_u2 = entity_gateway.create_item(
NewItemEntity {
item: ItemDetail::Unit(
item::unit::Unit {
unit: item::unit::UnitType::PriestMind,
modifier: Some(item::unit::UnitModifier::Minus),
armor_slot: 2,
}
),
location: ItemLocation::Inventory {
character_id: character.id,
slot: 9,
equipped: true,
}
}
).await;
entity_gateway.create_item(
).await.unwrap();
let item12_u3 = entity_gateway.create_item(
NewItemEntity {
item: ItemDetail::Unit(
item::unit::Unit {
unit: item::unit::UnitType::PriestMind,
modifier: Some(item::unit::UnitModifier::Minus),
armor_slot: 3,
}
),
location: ItemLocation::Inventory {
character_id: character.id,
slot: 10,
equipped: true,
}
}
).await;
entity_gateway.create_item(
).await.unwrap();
let item13 = entity_gateway.create_item(
NewItemEntity {
item: ItemDetail::Mag(
item::mag::Mag::baby_mag(5)
),
location: ItemLocation::Inventory {
character_id: character.id,
slot: 11,
equipped: true,
}
}
).await;
).await.unwrap();
let equipped = item::EquippedEntity {
weapon: Some(item2_w.id),
armor: Some(item7_a.id),
shield: Some(item8_s.id),
unit: [Some(item9_u0.id), Some(item10_u1.id), Some(item11_u2.id), Some(item12_u3.id)],
mag: Some(item5_m.id),
};
entity_gateway.set_character_equips(&character.id, &equipped).await.unwrap();
let inventory = item::InventoryEntity::new(vec![item0, item1, item2_w, item3, item4, item5_m, item6, item7_a, item8_s, item9_u0, item10_u1, item11_u2, item12_u3, item13]);
entity_gateway.set_character_inventory(&character.id, &inventory).await.unwrap();
entity_gateway.set_character_bank(&character.id, &item::BankEntity::default(), item::BankName("".into())).await.unwrap();
}
info!("[patch] starting server");
let patch_config = load_config();
let patch_motd = load_motd();
let (patch_file_tree, patch_file_lookup) = generate_patch_tree(patch_config.path.as_str());
let patch_state = PatchServerState::new(patch_file_tree, patch_file_lookup, patch_motd);
let patch_loop = patch_mainloop(patch_state, patch_config.port);
let patch_state = Box::new(PatchServerState::new(patch_file_tree, patch_file_lookup, patch_motd));
let patch_loop = patch_mainloop(*patch_state, patch_config.port);
let thread_entity_gateway = entity_gateway.clone();
info!("[auth] starting server");
let login_state = LoginServerState::new(thread_entity_gateway, "127.0.0.1".parse().unwrap());
let login_loop = login_mainloop(login_state, elseware::login::login::LOGIN_PORT);
let login_state = Box::new(LoginServerState::new(thread_entity_gateway, "127.0.0.1".parse().unwrap()));
let login_loop = login_mainloop(*login_state, elseware::login::login::LOGIN_PORT);
let thread_entity_gateway = entity_gateway.clone();
info!("[character] starting server");
let char_state = CharacterServerState::new(thread_entity_gateway, AuthToken("".into()));
let character_loop = character_mainloop(char_state, elseware::login::character::CHARACTER_PORT, elseware::login::login::COMMUNICATION_PORT);
let char_state = Box::new(CharacterServerState::new(thread_entity_gateway, AuthToken("".into())));
let character_loop = character_mainloop(*char_state, elseware::login::character::CHARACTER_PORT, elseware::login::login::COMMUNICATION_PORT);
let thread_entity_gateway = entity_gateway.clone();
info!("[ship] starting server");
let ship_state = ShipServerStateBuilder::new()
let ship_state = Box::new(ShipServerStateBuilder::new()
.name("US/Sona-Nyl".into())
.ip(Ipv4Addr::new(127,0,0,1))
.port(elseware::ship::ship::SHIP_PORT)
.gateway(thread_entity_gateway)
.build();
let ship_loop = ship_mainloop(ship_state, elseware::ship::ship::SHIP_PORT, std::net::Ipv4Addr::new(127, 0, 0, 1), elseware::login::login::COMMUNICATION_PORT);
.build());
let ship_loop = ship_mainloop(*ship_state, elseware::ship::ship::SHIP_PORT, std::net::Ipv4Addr::new(127, 0, 0, 1), elseware::login::login::COMMUNICATION_PORT);
let thread_entity_gateway = entity_gateway.clone();
let ship_state = ShipServerStateBuilder::new()
let ship_state = Box::new(ShipServerStateBuilder::new()
.name("EU/Dylath-Leen".into())
.ip(Ipv4Addr::new(127,0,0,1))
.port(elseware::ship::ship::SHIP_PORT+2000)
.gateway(thread_entity_gateway)
.build();
let ship_loop2 = ship_mainloop(ship_state, elseware::ship::ship::SHIP_PORT+2000, std::net::Ipv4Addr::new(127, 0, 0, 1), elseware::login::login::COMMUNICATION_PORT);
.build());
let ship_loop2 = ship_mainloop(*ship_state, elseware::ship::ship::SHIP_PORT+2000, std::net::Ipv4Addr::new(127, 0, 0, 1), elseware::login::login::COMMUNICATION_PORT);
let thread_entity_gateway = entity_gateway.clone();
let ship_state = ShipServerStateBuilder::new()
let ship_state = Box::new(ShipServerStateBuilder::new()
.name("JP/Thalarion".into())
.ip(Ipv4Addr::new(127,0,0,1))
.port(elseware::ship::ship::SHIP_PORT+3000)
.gateway(thread_entity_gateway)
.build();
let ship_loop3 = ship_mainloop(ship_state, elseware::ship::ship::SHIP_PORT+3000, std::net::Ipv4Addr::new(127, 0, 0, 1), elseware::login::login::COMMUNICATION_PORT);
.build());
let ship_loop3 = ship_mainloop(*ship_state, elseware::ship::ship::SHIP_PORT+3000, std::net::Ipv4Addr::new(127, 0, 0, 1), elseware::login::login::COMMUNICATION_PORT);
futures::future::join_all(vec![patch_loop, login_loop, character_loop, ship_loop, ship_loop2, ship_loop3]).await;
});

16
src/common/mainloop/client.rs

@ -139,7 +139,7 @@ enum ServerStateAction<S> {
Disconnect,
}
async fn client_recv_loop<S, R>(client_id: ClientId,
fn client_recv_loop<S, R>(client_id: ClientId,
socket: Arc<async_std::net::TcpStream>,
cipher: Arc<Mutex<Box<dyn PSOCipher + Send>>>,
server_sender: async_std::sync::Sender<ClientAction<ServerStateAction<S>, R>>,
@ -177,7 +177,7 @@ where
});
}
async fn client_send_loop<S>(client_id: ClientId,
fn client_send_loop<S>(client_id: ClientId,
socket: Arc<async_std::net::TcpStream>,
cipher_in: Arc<Mutex<Box<dyn PSOCipher + Send>>>,
cipher_out: Arc<Mutex<Box<dyn PSOCipher + Send>>>,
@ -207,7 +207,7 @@ where
});
}
async fn state_client_loop<STATE, S, R, E>(state: Arc<Mutex<STATE>>,
fn state_client_loop<STATE, S, R, E>(state: Arc<Mutex<STATE>>,
server_state_receiver: async_std::sync::Receiver<ClientAction<ServerStateAction<S>, R>>) where
STATE: ServerState<SendPacket=S, RecvPacket=R, PacketError=E> + Send + 'static,
S: SendServerPacket + std::fmt::Debug + Send + 'static,
@ -223,7 +223,6 @@ async fn state_client_loop<STATE, S, R, E>(state: Arc<Mutex<STATE>>,
match action {
ClientAction::NewClient(client_id, sender) => {
clients.insert(client_id, sender.clone());
let actions = state.on_connect(client_id).await;
match actions {
Ok(actions) => {
@ -237,11 +236,12 @@ async fn state_client_loop<STATE, S, R, E>(state: Arc<Mutex<STATE>>,
}
}
}
}
},
Err(err) => {
warn!("[client {:?} state on_connect error] {:?}", client_id, err);
}
}
clients.insert(client_id, sender);
},
ClientAction::Packet(client_id, pkt) => {
let pkts = state.handle(client_id, &pkt).await;
@ -295,7 +295,7 @@ where
let mut id = 0;
let (server_state_sender, server_state_receiver) = async_std::sync::channel(1024);
state_client_loop(state, server_state_receiver).await;
state_client_loop(state, server_state_receiver);
loop {
let (sock, addr) = listener.accept().await.unwrap();
@ -309,8 +309,8 @@ where
let cipher_in: Arc<Mutex<Box<dyn PSOCipher + Send>>> = Arc::new(Mutex::new(Box::new(NullCipher {})));
let cipher_out: Arc<Mutex<Box<dyn PSOCipher + Send>>> = Arc::new(Mutex::new(Box::new(NullCipher {})));
client_recv_loop(client_id, socket.clone(), cipher_in.clone(), server_state_sender.clone(), client_sender).await;
client_send_loop(client_id, socket.clone(), cipher_in.clone(), cipher_out.clone(), client_receiver).await;
client_recv_loop(client_id, socket.clone(), cipher_in.clone(), server_state_sender.clone(), client_sender);
client_send_loop(client_id, socket.clone(), cipher_in.clone(), cipher_out.clone(), client_receiver);
}
}))
}

4
src/common/mainloop/mod.rs

@ -32,7 +32,7 @@ pub fn login_mainloop<EG: EntityGateway + 'static>(login_state: LoginServerState
pub fn character_mainloop<EG: EntityGateway + 'static>(character_state: CharacterServerState<EG>, character_port: u16, comm_port: u16) -> Pin<Box<dyn Future<Output = ()>>> {
let character_state = Arc::new(Mutex::new(character_state));
let client_mainloop = client_accept_mainloop(character_state.clone(), character_port);
let ship_communication_mainloop = login_listen_mainloop(character_state.clone(), comm_port);
let ship_communication_mainloop = login_listen_mainloop(character_state, comm_port);
Box::pin(join_all(vec![client_mainloop, ship_communication_mainloop]).map(|_| ()))
}
@ -40,6 +40,6 @@ pub fn character_mainloop<EG: EntityGateway + 'static>(character_state: Characte
pub fn ship_mainloop<EG: EntityGateway + 'static>(ship_state: ShipServerState<EG>, ship_port: u16, comm_ip: std::net::Ipv4Addr, comm_port: u16) -> Pin<Box<dyn Future<Output = ()>>> {
let ship_state = Arc::new(Mutex::new(ship_state));
let client_mainloop = client_accept_mainloop(ship_state.clone(), ship_port);
let login_communication_mainloop = ship_connect_mainloop(ship_state.clone(), comm_ip, comm_port);
let login_communication_mainloop = ship_connect_mainloop(ship_state, comm_ip, comm_port);
Box::pin(join_all(vec![client_mainloop, login_communication_mainloop]).map(|_| ()))
}

27
src/entity/gateway/entitygateway.rs

@ -65,6 +65,7 @@ pub trait EntityGateway: Send + Sync + Clone {
unimplemented!();
}
// TODO: remove
async fn change_item(&mut self, _id: &ItemEntityId, _item: &ItemDetail) -> Result<(), GatewayError> {
unimplemented!();
}
@ -85,7 +86,33 @@ pub trait EntityGateway: Send + Sync + Clone {
unimplemented!();
}
/*
async fn get_items_by_character(&self, _char_id: &CharacterEntityId) -> Result<Vec<ItemEntity>, GatewayError> {
unimplemented!();
}
*/
async fn get_character_inventory(&mut self, _char_id: &CharacterEntityId) -> Result<InventoryEntity, GatewayError> {
unimplemented!();
}
async fn get_character_bank(&mut self, _char_id: &CharacterEntityId, _bank_name: BankName) -> Result<BankEntity, GatewayError> {
unimplemented!();
}
async fn set_character_inventory(&mut self, _char_id: &CharacterEntityId, _inventory: &InventoryEntity) -> Result<(), GatewayError> {
unimplemented!();
}
async fn set_character_bank(&mut self, _char_id: &CharacterEntityId, _inventory: &BankEntity, _bank_name: BankName) -> Result<(), GatewayError> {
unimplemented!();
}
async fn get_character_equips(&mut self, _char_id: &CharacterEntityId) -> Result<EquippedEntity, GatewayError> {
unimplemented!();
}
async fn set_character_equips(&mut self, _char_id: &CharacterEntityId, _equips: &EquippedEntity) -> Result<(), GatewayError> {
unimplemented!();
}
}

142
src/entity/gateway/inmemory.rs

@ -14,6 +14,9 @@ pub struct InMemoryGateway {
user_settings: Arc<Mutex<BTreeMap<UserSettingsId, UserSettingsEntity>>>,
characters: Arc<Mutex<BTreeMap<CharacterEntityId, CharacterEntity>>>,
items: Arc<Mutex<BTreeMap<ItemEntityId, ItemEntity>>>,
inventories: Arc<Mutex<BTreeMap<CharacterEntityId, InventoryEntity>>>,
banks: Arc<Mutex<BTreeMap<CharacterEntityId, BankEntity>>>,
equips: Arc<Mutex<BTreeMap<CharacterEntityId, EquippedEntity>>>,
mag_modifiers: Arc<Mutex<BTreeMap<ItemEntityId, Vec<mag::MagModifier>>>>,
}
@ -24,11 +27,66 @@ impl InMemoryGateway {
user_settings: Arc::new(Mutex::new(BTreeMap::new())),
characters: Arc::new(Mutex::new(BTreeMap::new())),
items: Arc::new(Mutex::new(BTreeMap::new())),
inventories: Arc::new(Mutex::new(BTreeMap::new())),
banks: Arc::new(Mutex::new(BTreeMap::new())),
equips: Arc::new(Mutex::new(BTreeMap::new())),
mag_modifiers: Arc::new(Mutex::new(BTreeMap::new())),
}
}
}
impl InMemoryGateway {
fn apply_modifiers(&self, inventory: InventoryEntity ) -> InventoryEntity {
let items = self.items.lock().unwrap();
let inventory_items = inventory.items.into_iter()
.map(|item| {
item.map_individual(|mut item| {
item.item = match item.item {
ItemDetail::Mag(mag) => {
let mut mag = mag::Mag::baby_mag(mag.color as u16);
println!("mag! {:?}", mag);
if let Some(mag_modifiers) = self.mag_modifiers.lock().unwrap().get(&item.id) {
for mag_modifier in mag_modifiers.iter() {
match mag_modifier {
mag::MagModifier::FeedMag {food} => {
items.get(&food).map(|mag_feed| {
match mag_feed.item {
ItemDetail::Tool(mag_feed) => mag.feed(mag_feed.tool),
_ => {}
}
});
},
mag::MagModifier::OwnerChange(class, section_id) => {
mag.change_owner(*class, *section_id)
},
mag::MagModifier::MagCell(mag_cell_id) => {
items.get(&mag_cell_id).map(|mag_cell| {
match mag_cell.item {
ItemDetail::Tool(mag_cell) => mag.apply_mag_cell(mag_cell.tool.try_into().unwrap()),
_ => {}
}
});
},
_ => {}
}
println!("{:?} -> {:?}", mag_modifier, mag);
}
}
ItemDetail::Mag(mag)
}
_ => {
item.item
}
};
item
})
})
.collect();
InventoryEntity::new(inventory_items)
}
}
#[async_trait::async_trait]
impl EntityGateway for InMemoryGateway {
async fn create_user(&mut self, user: NewUserAccountEntity) -> Result<UserAccountEntity, GatewayError> {
@ -208,55 +266,51 @@ impl EntityGateway for InMemoryGateway {
Ok(())
}
async fn get_items_by_character(&self, char_id: &CharacterEntityId) -> Result<Vec<ItemEntity>, GatewayError> {
let items = self.items.lock().unwrap();
Ok(items
async fn get_character_inventory(&mut self, char_id: &CharacterEntityId) -> Result<InventoryEntity, GatewayError> {
println!("getting inv");
let inventories = self.inventories.lock().unwrap();
Ok(inventories
.iter()
.filter(|(_, k)| {
match k.location {
ItemLocation::Inventory{character_id, ..} => character_id == *char_id,
ItemLocation::Bank{character_id, ..} => character_id == *char_id,
_ => false
.find(|(id, _)| **id == *char_id)
.map(|(_, inv)| inv.clone())
.map(|inv| self.apply_modifiers(inv))
.unwrap_or(InventoryEntity::default()))
}
})
.map(|(_, k)| {
k.clone()
})
.map(|mut item| {
item.item = match item.item {
ItemDetail::Mag(mut mag) => {
self.mag_modifiers.lock().unwrap().get(&item.id).map(|mag_modifiers| {
for mag_modifier in mag_modifiers.iter() {
match mag_modifier {
mag::MagModifier::FeedMag {food} => {
items.get(&food).map(|mag_feed| {
match mag_feed.item {
ItemDetail::Tool(mag_feed) => mag.feed(mag_feed.tool),
_ => {}
}
});
},
mag::MagModifier::OwnerChange(class, section_id) => {
mag.change_owner(*class, *section_id)
},
mag::MagModifier::MagCell(mag_cell_id) => {
items.get(&mag_cell_id).map(|mag_cell| {
match mag_cell.item {
ItemDetail::Tool(mag_cell) => mag.apply_mag_cell(mag_cell.tool.try_into().unwrap()),
_ => {}
async fn get_character_bank(&mut self, char_id: &CharacterEntityId, _bank_name: BankName) -> Result<BankEntity, GatewayError> {
let banks = self.banks.lock().unwrap();
Ok(banks
.iter()
.find(|(id, _)| **id == *char_id)
.map(|(_, b)| b.clone())
.unwrap_or(BankEntity::default()))
}
});
},
_ => {}
async fn set_character_inventory(&mut self, char_id: &CharacterEntityId, inventory: &InventoryEntity) -> Result<(), GatewayError> {
let mut inventories = self.inventories.lock().unwrap();
inventories.insert(*char_id, inventory.clone());
Ok(())
}
// TOOD: impl bank name
async fn set_character_bank(&mut self, char_id: &CharacterEntityId, bank: &BankEntity, _bank_name: BankName) -> Result<(), GatewayError> {
let mut banks = self.banks.lock().unwrap();
banks.insert(*char_id, bank.clone());
Ok(())
}
});
ItemDetail::Mag(mag)
async fn get_character_equips(&mut self, char_id: &CharacterEntityId) -> Result<EquippedEntity, GatewayError> {
let equips = self.equips.lock().unwrap();
Ok(equips
.iter()
.find(|(id, _)| **id == *char_id)
.map(|(_, inv)| inv.clone())
.unwrap_or(EquippedEntity::default()))
}
_ => item.item
};
item
})
.collect())
async fn set_character_equips(&mut self, char_id: &CharacterEntityId, equipped: &EquippedEntity) -> Result<(), GatewayError> {
let mut equips = self.equips.lock().unwrap();
equips.insert(*char_id, equipped.clone());
Ok(())
}
}

24
src/entity/gateway/postgres/migrations/V0001__initial.sql

@ -122,3 +122,27 @@ create table mag_modifier (
modifier jsonb not null,
created_at timestamptz default current_timestamp not null
);
create table equipped (
pchar integer references player_character (id) unique not null,
weapon integer references item (id),
armor integer references item (id),
shield integer references item (id),
unit0 integer references item (id),
unit1 integer references item (id),
unit2 integer references item (id),
unit3 integer references item (id),
mag integer references item (id)
);
create table inventory (
pchar integer references player_character (id) unique not null,
items jsonb not null
);
create table bank (
pchar integer references player_character (id) not null,
items jsonb not null,
name varchar(128) not null,
unique (pchar, name)
);

93
src/entity/gateway/postgres/models.rs

@ -383,7 +383,6 @@ impl Into<shield::Shield> for PgShield {
pub struct PgUnit {
unit: unit::UnitType,
modifier: Option<unit::UnitModifier>,
armor_slot: u8,
}
impl From<unit::Unit> for PgUnit {
@ -391,7 +390,6 @@ impl From<unit::Unit> for PgUnit {
PgUnit {
unit: other.unit,
modifier: other.modifier,
armor_slot: other.armor_slot,
}
}
}
@ -401,7 +399,6 @@ impl Into<unit::Unit> for PgUnit {
unit::Unit {
unit: self.unit,
modifier: self.modifier,
armor_slot: self.armor_slot,
}
}
}
@ -573,9 +570,6 @@ pub struct PgItem {
pub enum PgItemLocationDetail {
Inventory {
character_id: u32,
#[serde(skip_serializing)]
slot: usize,
equipped: bool,
},
Bank {
character_id: u32,
@ -604,7 +598,7 @@ pub enum PgItemLocationDetail {
impl From<ItemLocation> for PgItemLocationDetail {
fn from(other: ItemLocation) -> PgItemLocationDetail {
match other {
ItemLocation::Inventory{character_id, slot, equipped} => PgItemLocationDetail::Inventory{character_id: character_id.0, slot, equipped},
ItemLocation::Inventory{character_id} => PgItemLocationDetail::Inventory{character_id: character_id.0},
ItemLocation::Bank{character_id, name} => PgItemLocationDetail::Bank{character_id: character_id.0, name: name.0},
ItemLocation::LocalFloor{character_id, map_area, x,y,z} => PgItemLocationDetail::LocalFloor{character_id: character_id.0, map_area, x,y,z},
ItemLocation::SharedFloor{map_area, x,y,z} => PgItemLocationDetail::SharedFloor{map_area, x,y,z},
@ -618,7 +612,7 @@ impl From<ItemLocation> for PgItemLocationDetail {
impl Into<ItemLocation> for PgItemLocationDetail {
fn into(self) -> ItemLocation {
match self {
PgItemLocationDetail::Inventory{character_id, slot, equipped} => ItemLocation::Inventory{character_id: CharacterEntityId(character_id), slot, equipped},
PgItemLocationDetail::Inventory{character_id} => ItemLocation::Inventory{character_id: CharacterEntityId(character_id)},
PgItemLocationDetail::Bank{character_id, name} => ItemLocation::Bank{character_id: CharacterEntityId(character_id), name: BankName(name)},
PgItemLocationDetail::LocalFloor{character_id, map_area, x,y,z} => ItemLocation::LocalFloor{character_id: CharacterEntityId(character_id), map_area, x,y,z},
PgItemLocationDetail::SharedFloor{map_area, x,y,z} => ItemLocation::SharedFloor{map_area, x,y,z},
@ -676,6 +670,12 @@ pub struct PgMagModifier {
}
#[derive(Debug, sqlx::FromRow)]
pub struct PgItemEntity {
pub id: i32,
pub item: sqlx::types::Json<PgItemDetail>,
}
#[derive(Debug, sqlx::FromRow)]
pub struct PgItemWithLocation {
pub id: i32,
@ -683,6 +683,16 @@ pub struct PgItemWithLocation {
pub location: sqlx::types::Json<PgItemLocationDetail>,
}
impl Into<ItemEntity> for PgItemWithLocation {
fn into(self) -> ItemEntity {
ItemEntity {
id: ItemEntityId(self.id as u32),
item: self.item.0.into(),
location: self.location.0.into(),
}
}
}
#[derive(Debug, sqlx::FromRow)]
pub struct PgMagModifierWithParameters {
@ -691,3 +701,70 @@ pub struct PgMagModifierWithParameters {
pub feed: Option<sqlx::types::Json<PgTool>>,
pub cell: Option<sqlx::types::Json<PgTool>>,
}
#[derive(Debug, Serialize, Deserialize)]
#[serde(untagged)]
pub enum PgInventoryItemEntity {
Individual(i32),
Stacked(Vec<i32>),
}
#[derive(Debug, sqlx::FromRow)]
pub struct PgInventoryEntity {
pub pchar: i32,
pub items: sqlx::types::Json<Vec<PgInventoryItemEntity>>,
}
#[derive(Debug, sqlx::FromRow)]
pub struct PgBankEntity {
pub pchar: i32,
pub items: sqlx::types::Json<Vec<PgInventoryItemEntity>>,
pub name: String,
}
#[derive(Debug, sqlx::FromRow)]
pub struct PgEquipped {
pchar: i32,
weapon: Option<i32>,
armor: Option<i32>,
shield: Option<i32>,
unit0: Option<i32>,
unit1: Option<i32>,
unit2: Option<i32>,
unit3: Option<i32>,
mag: Option<i32>,
}
impl Into<EquippedEntity> for PgEquipped {
fn into(self) -> EquippedEntity {
EquippedEntity {
weapon: self.weapon.map(|i| ItemEntityId(i as u32)),
armor: self.armor.map(|i| ItemEntityId(i as u32)),
shield: self.shield.map(|i| ItemEntityId(i as u32)),
unit: [self.unit0.map(|i| ItemEntityId(i as u32)),
self.unit1.map(|i| ItemEntityId(i as u32)),
self.unit2.map(|i| ItemEntityId(i as u32)),
self.unit3.map(|i| ItemEntityId(i as u32)),
],
mag: self.mag.map(|i| ItemEntityId(i as u32)),
}
}
}
impl From<(CharacterEntityId, EquippedEntity)> for PgEquipped {
fn from(char_equips: (CharacterEntityId, EquippedEntity)) -> PgEquipped {
PgEquipped {
pchar: char_equips.0.0 as i32,
weapon: char_equips.1.weapon.map(|i| i.0 as i32),
armor: char_equips.1.armor.map(|i| i.0 as i32),
shield: char_equips.1.shield.map(|i| i.0 as i32),
unit0: char_equips.1.unit[0].map(|i| i.0 as i32),
unit1: char_equips.1.unit[1].map(|i| i.0 as i32),
unit2: char_equips.1.unit[2].map(|i| i.0 as i32),
unit3: char_equips.1.unit[3].map(|i| i.0 as i32),
mag: char_equips.1.mag.map(|i| i.0 as i32),
}
}
}

162
src/entity/gateway/postgres/postgres.rs

@ -272,6 +272,23 @@ impl EntityGateway for PostgresGateway {
}
async fn create_item(&mut self, item: NewItemEntity) -> Result<ItemEntity, GatewayError> {
let mut tx = self.pool.begin().await?;
let new_item = sqlx::query_as::<_, PgItem>("insert into item (item) values ($1) returning *;")
.bind(sqlx::types::Json(PgItemDetail::from(item.item)))
.fetch_one(&mut tx).await?;
let location = sqlx::query_as::<_, PgItemLocation>("insert into item_location (item, location) values ($1, $2) returning *")
.bind(new_item.id)
.bind(sqlx::types::Json(PgItemLocationDetail::from(item.location)))
.fetch_one(&mut tx).await?;
tx.commit().await?;
Ok(ItemEntity {
id: ItemEntityId(new_item.id as u32),
item: new_item.item.0.into(),
location: location.location.0.into(),
})
/*
let mut tx = self.pool.begin().await?;
let new_item = sqlx::query_as::<_, PgItem>("insert into item (item) values ($1) returning *;")
.bind(sqlx::types::Json(PgItemDetail::from(item.item)))
@ -310,6 +327,7 @@ impl EntityGateway for PostgresGateway {
item: new_item.item.0.into(),
location: location.location.0.into(),
})
*/
}
async fn change_item(&mut self, id: &ItemEntityId, item: &ItemDetail) -> Result<(), GatewayError> {
@ -321,6 +339,13 @@ impl EntityGateway for PostgresGateway {
}
async fn change_item_location(&mut self, item_id: &ItemEntityId, item_location: ItemLocation) -> Result<(), GatewayError> {
sqlx::query("insert into item_location (item, location) values ($1, $2)")
.bind(item_id.0)
.bind(sqlx::types::Json(PgItemLocationDetail::from(item_location)))
.execute(&self.pool).await?;
Ok(())
/*
let mut tx = self.pool.begin().await?;
if let ItemLocation::Inventory{slot, ..} = &item_location {
sqlx::query("delete from inventory_slot where item = $1")
@ -346,6 +371,7 @@ impl EntityGateway for PostgresGateway {
}
tx.commit().await?;
Ok(())
*/
}
async fn feed_mag(&mut self, mag_item_id: &ItemEntityId, tool_item_id: &ItemEntityId) -> Result<(), GatewayError> {
@ -371,7 +397,7 @@ impl EntityGateway for PostgresGateway {
.execute(&self.pool).await?;
Ok(())
}
/*
async fn get_items_by_character(&self, char_id: &CharacterEntityId) -> Result<Vec<ItemEntity>, GatewayError> {
let q = r#"select * from (
select distinct on (item_location.item)
@ -410,4 +436,138 @@ impl EntityGateway for PostgresGateway {
.await
).await)
}
*/
async fn get_character_inventory(&mut self, char_id: &CharacterEntityId) -> Result<InventoryEntity, GatewayError> {
let inventory = sqlx::query_as::<_, PgInventoryEntity>("select * from inventory where pchar = $1")
.bind(char_id.0)
.fetch_one(&self.pool).await?;
// TODO: inefficient
let mut real_inventory = Vec::new();
for inv_item in inventory.items.0.into_iter() {
match inv_item {
PgInventoryItemEntity::Individual(item) => {
let entity = sqlx::query_as::<_, PgItemWithLocation>("select item.id, item.item, item_location.location from item join item_location on item.id = item_location.item where id = $1")
.bind(item)
.fetch_one(&self.pool).await
.map(|item| item.into())?;
real_inventory.push(InventoryItemEntity::Individual(entity));
},
PgInventoryItemEntity::Stacked(items) => {
let mut stacked_item = Vec::new();
for s_item in items {
stacked_item.push(sqlx::query_as::<_, PgItemWithLocation>("select item.id, item.item, item_location.location from item join item_location on item.id = item_location.item where id = $1")
.bind(s_item)
.fetch_one(&self.pool).await
.map(|item| item.into())?)
}
real_inventory.push(InventoryItemEntity::Stacked(stacked_item));
}
}
}
Ok(InventoryEntity::new(real_inventory))
}
async fn get_character_bank(&mut self, char_id: &CharacterEntityId, bank_name: BankName) -> Result<BankEntity, GatewayError> {
let bank = sqlx::query_as::<_, PgInventoryEntity>("select * from bank where pchar = $1 and name = $2")
.bind(char_id.0)
.bind(bank_name.0)
.fetch_one(&self.pool).await?;
// TODO: inefficient
let mut real_bank = Vec::new();
for bank_item in bank.items.0.into_iter() {
match bank_item {
PgInventoryItemEntity::Individual(item) => {
let entity = sqlx::query_as::<_, PgItemWithLocation>("select item.id, item.item, item_location.location from item join item_location on item.id = item_location.item where id = $1")
.bind(item)
.fetch_one(&self.pool).await
.map(|item| item.into())?;
real_bank.push(BankItemEntity::Individual(entity));
},
PgInventoryItemEntity::Stacked(items) => {
let mut stacked_item = Vec::new();
for s_item in items {
stacked_item.push(sqlx::query_as::<_, PgItemWithLocation>("select item.id, item.item, item_location.location from item join item_location on item.id = item_location.item where id = $1")
.bind(s_item)
.fetch_one(&self.pool).await
.map(|item| item.into())?)
}
real_bank.push(BankItemEntity::Stacked(stacked_item));
}
}
}
Ok(BankEntity::new(real_bank))
}
async fn set_character_inventory(&mut self, char_id: &CharacterEntityId, inventory: &InventoryEntity) -> Result<(), GatewayError> {
let inventory = inventory.items.iter()
.map(|item| {
match item {
InventoryItemEntity::Individual(item) => {
PgInventoryItemEntity::Individual(item.id.0 as i32)
},
InventoryItemEntity::Stacked(items) => {
PgInventoryItemEntity::Stacked(items.iter().map(|i| i.id.0 as i32).collect())
},
}
})
.collect::<Vec<_>>();
sqlx::query("insert into inventory (pchar, items) values ($1, $2) on conflict (pchar) do update set items = $2")
.bind(char_id.0)
.bind(sqlx::types::Json(inventory))
.execute(&self.pool)
.await?;
Ok(())
}
async fn set_character_bank(&mut self, char_id: &CharacterEntityId, bank: &BankEntity, bank_name: BankName) -> Result<(), GatewayError> {
let bank = bank.items.iter()
.map(|item| {
match item {
BankItemEntity::Individual(item) => {
PgInventoryItemEntity::Individual(item.id.0 as i32)
},
BankItemEntity::Stacked(items) => {
PgInventoryItemEntity::Stacked(items.iter().map(|i| i.id.0 as i32).collect())
},
}
})
.collect::<Vec<_>>();
sqlx::query("insert into bank (pchar, items, name) values ($1, $2, $3) on conflict (pchar, name) do update set items = $2")
.bind(char_id.0)
.bind(sqlx::types::Json(bank))
.bind(bank_name.0)
.execute(&self.pool)
.await?;
Ok(())
}
async fn get_character_equips(&mut self, char_id: &CharacterEntityId) -> Result<EquippedEntity, GatewayError> {
let equips = sqlx::query_as::<_, PgEquipped>("select * from equipped where pchar = $1")
.bind(char_id.0)
.fetch_one(&self.pool)
.await?;
Ok(equips.into())
}
async fn set_character_equips(&mut self, char_id: &CharacterEntityId, equips: &EquippedEntity) -> Result<(), GatewayError> {
sqlx::query(r#"insert into equipped (pchar, weapon, armor, shield, unit0, unit1, unit2, unit3, mag) values ($1, $2, $3, $4, $5, $6, $7, $8, $9)
on conflict (pchar) do update set weapon=$2, armor=$3, shield=$4, unit0=$5, unit1=$6, unit2=$7, unit3=$8, mag=$9"#)
.bind(char_id.0)
.bind(equips.weapon.map(|i| i.0 as i32))
.bind(equips.armor.map(|i| i.0 as i32))
.bind(equips.shield.map(|i| i.0 as i32))
.bind(equips.unit[0].map(|i| i.0 as i32))
.bind(equips.unit[1].map(|i| i.0 as i32))
.bind(equips.unit[2].map(|i| i.0 as i32))
.bind(equips.unit[3].map(|i| i.0 as i32))
.bind(equips.mag.map(|i| i.0 as i32))
.execute(&self.pool)
.await?;
Ok(())
}
}

137
src/entity/item/mod.rs

@ -24,8 +24,6 @@ pub struct BankName(pub String);
pub enum ItemLocation {
Inventory {
character_id: CharacterEntityId,
slot: usize,
equipped: bool,
},
Bank {
character_id: CharacterEntityId,
@ -154,6 +152,13 @@ impl ItemDetail {
ItemDetail::ESWeapon(e) => e.as_bytes(),
}
}
pub fn as_tool(self) -> Option<tool::Tool> {
match self {
ItemDetail::Tool(tool) => Some(tool),
_ => None,
}
}
}
#[derive(Clone, Debug)]
@ -168,3 +173,131 @@ pub struct ItemEntity {
pub location: ItemLocation,
pub item: ItemDetail,
}
#[derive(Clone, Debug)]
pub enum InventoryItemEntity {
Individual(ItemEntity),
Stacked(Vec<ItemEntity>),
}
impl std::convert::From<ItemEntity> for InventoryItemEntity {
fn from(item: ItemEntity) -> InventoryItemEntity {
InventoryItemEntity::Individual(item)
}
}
impl std::convert::From<Vec<ItemEntity>> for InventoryItemEntity {
fn from(items: Vec<ItemEntity>) -> InventoryItemEntity {
InventoryItemEntity::Stacked(items)
}
}
impl InventoryItemEntity {
pub fn map_individual<F: Fn(ItemEntity) -> ItemEntity>(self, func: F) -> InventoryItemEntity {
match self {
InventoryItemEntity::Individual(item) => InventoryItemEntity::Individual(func(item)),
_ => self,
}
}
//pub fn with_individual<T>(&self, func: fn(&ItemEntity) -> T) -> Option<T> {
pub fn with_individual<F: Fn(&ItemEntity) -> T, T>(&self, func: F) -> Option<T> {
match self {
InventoryItemEntity::Individual(item) => Some(func(item)),
_ => None,
}
}
//pub fn with_stacked<T>(&self, func: fn(&Vec<ItemEntity>) -> T) -> Option<T> {
pub fn with_stacked<F: Fn(&Vec<ItemEntity>) -> T, T>(&self, func: F) -> Option<T> {
match self {
InventoryItemEntity::Stacked(items) => Some(func(items)),
_ => None,
}
}
}
#[derive(Clone, Debug, Default)]
pub struct EquippedEntity {
pub weapon: Option<ItemEntityId>,
pub armor: Option<ItemEntityId>,
pub shield: Option<ItemEntityId>,
pub unit: [Option<ItemEntityId>; 4],
pub mag: Option<ItemEntityId>,
}
impl EquippedEntity {
pub fn is_equipped(&self, item: &ItemEntityId) -> bool {
self.weapon == Some(*item)
|| self.armor == Some(*item)
|| self.shield == Some(*item)
|| self.unit[0] == Some(*item)
|| self.unit[1] == Some(*item)
|| self.unit[2] == Some(*item)
|| self.unit[3] == Some(*item)
|| self.mag == Some(*item)
}
}
#[derive(Clone, Debug, Default)]
pub struct InventoryEntity {
pub items: Vec<InventoryItemEntity>,
}
impl InventoryEntity {
pub fn new<T: Into<InventoryItemEntity>>(items: Vec<T>) -> InventoryEntity {
InventoryEntity {
items: items.into_iter().map(|i| i.into()).collect(),
}
}
}
#[derive(Clone, Debug)]
pub enum BankItemEntity {
Individual(ItemEntity),
Stacked(Vec<ItemEntity>),
}
impl std::convert::From<ItemEntity> for BankItemEntity {
fn from(item: ItemEntity) -> BankItemEntity {
BankItemEntity::Individual(item)
}
}
impl std::convert::From<Vec<ItemEntity>> for BankItemEntity {
fn from(items: Vec<ItemEntity>) -> BankItemEntity {
BankItemEntity::Stacked(items)
}
}
impl BankItemEntity {
pub fn with_individual<T>(&self, func: fn(&ItemEntity) -> T) -> Option<T> {
match self {
BankItemEntity::Individual(item) => Some(func(item)),
_ => None,
}
}
pub fn with_stacked<T>(&self, func: fn(&Vec<ItemEntity>) -> T) -> Option<T> {
match self {
BankItemEntity::Stacked(items) => Some(func(items)),
_ => None,
}
}
}
#[derive(Clone, Debug, Default)]
pub struct BankEntity {
//pub items: [Option<CharacterBankItem>; 30],
pub items: Vec<BankItemEntity>,
}
impl BankEntity {
pub fn new<T: Into<BankItemEntity>>(items: Vec<T>) -> BankEntity {
BankEntity {
items: items.into_iter().map(|i| i.into()).collect(),
}
}
}

3
src/entity/item/unit.rs

@ -335,7 +335,6 @@ pub enum UnitModifier {
pub struct Unit {
pub unit: UnitType,
pub modifier: Option<UnitModifier>,
pub armor_slot: u8,
}
@ -362,7 +361,6 @@ impl Unit {
},
}
}
result[4] = self.armor_slot;
result
}
@ -380,7 +378,6 @@ impl Unit {
Ok(Unit{
unit: u.unwrap(),
modifier: m,
armor_slot: data[4],
})
}
else {

49
src/login/character.rs

@ -19,7 +19,7 @@ use libpso::{utf8_to_array, utf8_to_utf16_array};
use crate::entity::gateway::EntityGateway;
use crate::entity::account::{UserAccountEntity, NewUserSettingsEntity, USERFLAG_NEWCHAR, USERFLAG_DRESSINGROOM};
use crate::entity::item::{NewItemEntity, ItemDetail, ItemLocation};
use crate::entity::item::{NewItemEntity, ItemDetail, ItemLocation, InventoryItemEntity, InventoryEntity, BankEntity, BankName, EquippedEntity};
use crate::entity::item::weapon::Weapon;
use crate::entity::item::armor::Armor;
use crate::entity::item::tech::Technique;
@ -88,7 +88,7 @@ pub enum SendCharacterPacket {
ChecksumAck(ChecksumAck),
CharacterPreview(CharacterPreview),
GuildcardDataHeader(GuildcardDataHeader),
GuildcardDataChunk(GuildcardDataChunk),
GuildcardDataChunk(Box<GuildcardDataChunk>),
ParamDataHeader(ParamDataHeader),
ParamDataChunk(ParamDataChunk),
Timestamp(Timestamp),
@ -198,7 +198,7 @@ async fn new_character<EG: EntityGateway>(entity_gateway: &mut EG, user: &UserAc
};
entity_gateway.create_item(
let weapon = entity_gateway.create_item(
NewItemEntity {
item : ItemDetail::Weapon(
Weapon {
@ -211,11 +211,9 @@ async fn new_character<EG: EntityGateway>(entity_gateway: &mut EG, user: &UserAc
}),
location: ItemLocation::Inventory {
character_id: character.id,
slot: 0,
equipped: true,
}}).await.unwrap();
entity_gateway.create_item(
let armor = entity_gateway.create_item(
NewItemEntity {
item: ItemDetail::Armor (
Armor {
@ -227,23 +225,20 @@ async fn new_character<EG: EntityGateway>(entity_gateway: &mut EG, user: &UserAc
}),
location: ItemLocation::Inventory {
character_id: character.id,
slot: 1,
equipped: true,
}}).await.unwrap();
let mut mag = Mag::baby_mag(character.appearance.skin);
mag.change_owner(character.char_class, character.section_id);
entity_gateway.create_item(
let mag = entity_gateway.create_item(
NewItemEntity {
item: ItemDetail::Mag(mag),
location: ItemLocation::Inventory {
character_id: character.id,
slot: 2,
equipped: true,
}}).await.unwrap();
for _ in 0..4 {
entity_gateway.create_item(
let mut monomates = Vec::new();
for _ in 0..4usize {
monomates.push(entity_gateway.create_item(
NewItemEntity {
item: ItemDetail::Tool (
Tool {
@ -251,10 +246,12 @@ async fn new_character<EG: EntityGateway>(entity_gateway: &mut EG, user: &UserAc
}),
location: ItemLocation::Inventory {
character_id: character.id,
slot: 3,
equipped: false,
}}).await.unwrap();
entity_gateway.create_item(
}}).await.unwrap())
}
let mut monofluids = Vec::new();
for _ in 0..4usize {
monofluids.push(entity_gateway.create_item(
NewItemEntity {
item: ItemDetail::Tool (
Tool {
@ -262,10 +259,20 @@ async fn new_character<EG: EntityGateway>(entity_gateway: &mut EG, user: &UserAc
}),
location: ItemLocation::Inventory {
character_id: character.id,
slot: 4,
equipped: false,
}}).await.unwrap();
}}).await.unwrap())
}
let inventory = InventoryEntity {
items: vec![InventoryItemEntity::Individual(weapon.clone()), InventoryItemEntity::Individual(armor.clone()), InventoryItemEntity::Individual(mag.clone()),
InventoryItemEntity::Stacked(monomates), InventoryItemEntity::Stacked(monofluids)],
};
entity_gateway.set_character_inventory(&character.id, &inventory);
entity_gateway.set_character_bank(&character.id, &BankEntity::default(), BankName("".into()));
let mut equipped = EquippedEntity::default();
equipped.weapon = Some(weapon.id);
equipped.armor = Some(armor.id);
equipped.mag = Some(mag.id);
entity_gateway.set_character_equips(&character.id, &equipped);
}
@ -405,7 +412,7 @@ impl<EG: EntityGateway> CharacterServerState<EG> {
let mut buf = [0u8; GUILD_CARD_CHUNK_SIZE as usize];
buf[..len as usize].copy_from_slice(&client.guildcard_data_buffer.as_ref().unwrap()[start..end]);
vec![SendCharacterPacket::GuildcardDataChunk(GuildcardDataChunk::new(chunk, buf, len))]
vec![SendCharacterPacket::GuildcardDataChunk(Box::new(GuildcardDataChunk::new(chunk, buf, len)))]
} else {
Vec::new()
})

6
src/patch/patch.rs

@ -103,7 +103,7 @@ pub enum SendPatchPacket {
ChangeDirectory(ChangeDirectory),
EndFileSend(EndFileSend),
FileInfo(FileInfo),
FileSend(FileSend),
FileSend(Box<FileSend>),
FilesToPatchMetadata(FilesToPatchMetadata),
FinalizePatching(FinalizePatching),
Message(Message),
@ -338,12 +338,12 @@ impl Iterator for SendFileIterator {
else {
let mut crc = crc32::Digest::new(crc32::IEEE);
crc.write(&buf[0..len]);
let pkt = SendPatchPacket::FileSend(FileSend {
let pkt = SendPatchPacket::FileSend(Box::new(FileSend {
chunk_num: self.chunk_num,
checksum: crc.sum32(),
chunk_size: len as u32,
buffer: buf,
});
}));
self.chunk_num += 1;
Some(pkt)
}

2
src/ship/drops/generic_unit.rs

@ -89,7 +89,6 @@ impl GenericUnitTable {
ItemDropType::Unit(Unit {
unit: unit_type,
modifier: unit_modifier,
armor_slot: 0,
})
})
}
@ -117,7 +116,6 @@ mod test {
assert!(gut.get_drop(&area, &mut rng) == Some(ItemDropType::Unit(Unit {
unit: unit,
modifier: umod,
armor_slot: 0,
})));
}
}

1
src/ship/drops/rare_drop_table.rs

@ -128,7 +128,6 @@ impl RareDropTable {
ItemDropType::Unit(Unit {
unit: unit,
modifier: None,
armor_slot: 0,
})
},
RareDropItem::Tool(tool) => {

38
src/ship/items/bank.rs

@ -1,6 +1,7 @@
use crate::ship::items::ClientItemId;
use libpso::character::character;//::InventoryItem;
use crate::entity::item::{ItemEntityId, ItemDetail};
use crate::entity::item::{ItemEntityId, ItemEntity, ItemDetail, ItemLocation, BankEntity, BankItemEntity, BankName};
use crate::entity::character::CharacterEntityId;
use crate::entity::item::tool::Tool;
use crate::ship::items::inventory::{InventoryItemHandle, InventoryItem};
@ -292,6 +293,41 @@ impl CharacterBank {
self.items.last()
}
pub fn as_bank_entity(&self, character_id: &CharacterEntityId, bank_name: &BankName) -> BankEntity {
BankEntity {
items: self.items.iter()
.map(|item| {
match item {
BankItem::Individual(item) => {
BankItemEntity::Individual(ItemEntity {
id: item.entity_id,
location: ItemLocation::Bank {
character_id: *character_id,
name: bank_name.clone(),
},
item: item.item.clone(),
})
},
BankItem::Stacked(items) => {
BankItemEntity::Stacked(items.entity_ids.iter()
.map(|id| {
ItemEntity {
id: *id,
location: ItemLocation::Bank {
character_id: *character_id,
name: bank_name.clone(),
},
item: ItemDetail::Tool(items.tool)
}
})
.collect())
},
}
})
.collect()
}
}
}

142
src/ship/items/inventory.rs

@ -1,7 +1,8 @@
use std::cmp::Ordering;
use thiserror::Error;
use libpso::character::character;//::InventoryItem;
use crate::entity::item::{ItemEntityId, ItemDetail, ItemType};
use crate::entity::character::CharacterEntityId;
use crate::entity::item::{ItemEntityId, ItemDetail, ItemEntity, ItemType, ItemLocation, InventoryEntity, InventoryItemEntity, EquippedEntity};
use crate::entity::item::tool::Tool;
use crate::entity::item::mag::Mag;
use crate::ship::items::{ClientItemId, BankItem, BankItemHandle};
@ -13,12 +14,12 @@ const INVENTORY_CAPACITY: usize = 30;
#[derive(Debug, Clone)]
pub struct InventorySlot(pub usize);
#[derive(Debug, Clone)]
pub struct IndividualInventoryItem {
pub entity_id: ItemEntityId,
pub item_id: ClientItemId,
pub item: ItemDetail,
pub equipped: bool,
}
impl IndividualInventoryItem {
@ -153,17 +154,6 @@ impl InventoryItem {
}
}
pub fn equipped(&self) -> bool {
match self {
InventoryItem::Individual(individual_inventory_item) => {
individual_inventory_item.equipped
},
InventoryItem::Stacked(_) => {
false
}
}
}
pub fn as_client_bytes(&self) -> [u8; 16] {
match self {
InventoryItem::Individual(item) => {
@ -217,6 +207,8 @@ impl InventoryItem {
}
#[derive(Error, Debug, Clone)]
#[error("")]
pub enum InventoryItemConsumeError {
@ -350,13 +342,15 @@ impl<'a> InventoryItemHandle<'a> {
pub struct CharacterInventory {
item_id_counter: u32,
items: Vec<InventoryItem>,
equipped: EquippedEntity,
}
impl CharacterInventory {
pub fn new(items: Vec<InventoryItem>) -> CharacterInventory {
pub fn new(items: Vec<InventoryItem>, equipped: &EquippedEntity) -> CharacterInventory {
CharacterInventory{
item_id_counter: 0,
items: items,
equipped: equipped.clone(),
}
}
@ -375,10 +369,22 @@ impl CharacterInventory {
inventory[slot].data1.copy_from_slice(&bytes[0..12]);
inventory[slot].data2.copy_from_slice(&bytes[12..16]);
inventory[slot].item_id = item.item_id().0;
// does this do anything?
inventory[slot].equipped = if item.equipped() { 1 } else { 0 };
// because this actually equips the item
inventory[slot].flags |= if item.equipped() { 8 } else { 0 };
inventory[slot].equipped = 0;
inventory[slot].flags = 0;
if let InventoryItem::Individual(individual_item) = item {
if self.equipped.is_equipped(&individual_item.entity_id) {
if let ItemDetail::Unit(_) = individual_item.item {
inventory[slot].data1[4] = self.equipped.unit.iter()
.enumerate()
.find(|(_, u_id)| **u_id == Some(individual_item.entity_id))
.map(|(a, b)| a)
.unwrap_or(0) as u8
}
inventory[slot].equipped = 1;
inventory[slot].flags |= 8;
}
}
inventory
})
}
@ -410,7 +416,7 @@ impl CharacterInventory {
.filter(|(_, item)| {
if let InventoryItem::Individual(individual_inventory_item) = item {
if let ItemDetail::Mag(_) = &individual_inventory_item.item {
return individual_inventory_item.equipped
return self.equipped.is_equipped(&individual_inventory_item.entity_id)
}
}
false
@ -428,7 +434,7 @@ impl CharacterInventory {
.filter(|(_, item)| {
if let InventoryItem::Individual(individual_inventory_item) = item {
if let ItemDetail::Armor(_) = &individual_inventory_item.item {
return individual_inventory_item.equipped
return self.equipped.is_equipped(&individual_inventory_item.entity_id)
}
}
false
@ -446,7 +452,7 @@ impl CharacterInventory {
.filter(|(_, item)| {
if let InventoryItem::Individual(individual_inventory_item) = item {
if let ItemDetail::Shield(_) = &individual_inventory_item.item {
return individual_inventory_item.equipped
return self.equipped.is_equipped(&individual_inventory_item.entity_id)
}
}
false
@ -464,7 +470,7 @@ impl CharacterInventory {
.filter(|(_, item)| {
if let InventoryItem::Individual(individual_inventory_item) = item {
if let ItemDetail::Weapon(_) = &individual_inventory_item.item {
return individual_inventory_item.equipped
return self.equipped.is_equipped(&individual_inventory_item.entity_id)
}
}
false
@ -506,7 +512,6 @@ impl CharacterInventory {
entity_id: floor_item.entity_id,
item_id: floor_item.item_id,
item: floor_item.item.clone(),
equipped: false,
}));
if let Some(InventoryItem::Individual(new_item)) = self.items.last() {
@ -570,16 +575,15 @@ impl CharacterInventory {
entity_id: individual_bank_item.entity_id,
item_id: individual_bank_item.item_id,
item: individual_bank_item.item.clone(),
equipped: false,
}));
(true, self.count())
(true, self.count()-1)
},
BankItem::Stacked(stacked_bank_item) => {
let existing_inventory_item = self.items.iter_mut()
.enumerate()
.find_map(|(index, item)| {
if let InventoryItem::Stacked(stacked_inventory_item) = item {
if stacked_inventory_item.tool == stacked_inventory_item.tool {
if stacked_bank_item.tool == stacked_inventory_item.tool {
return Some((index, stacked_inventory_item))
}
}
@ -608,7 +612,7 @@ impl CharacterInventory {
item_id: ClientItemId(self.item_id_counter),
tool: stacked_bank_item.tool,
}));
self.count()
self.count()-1
}
};
(stacked_bank_item.count() == 0, slot)
@ -635,5 +639,89 @@ impl CharacterInventory {
pub fn set_items(&mut self, sorted_items: Vec<InventoryItem>) {
self.items = sorted_items;
}
pub fn equip(&mut self, id: &ClientItemId, equip_slot: u8) {
for item in &self.items {
if let InventoryItem::Individual(inventory_item) = item {
if inventory_item.item_id == *id {
match inventory_item.item {
ItemDetail::Weapon(_) => self.equipped.weapon = Some(inventory_item.entity_id),
ItemDetail::Armor(_) => self.equipped.armor = Some(inventory_item.entity_id),
ItemDetail::Shield(_) => self.equipped.shield = Some(inventory_item.entity_id),
ItemDetail::Unit(_) => {
if let Some(unit) = self.equipped.unit.get_mut(equip_slot as usize) {
*unit = Some(inventory_item.entity_id)
}
}
ItemDetail::Mag(_) => self.equipped.mag = Some(inventory_item.entity_id),
_ => {}
}
}
}
}
}
pub fn unequip(&mut self, id: &ClientItemId) {
for item in &self.items {
if let InventoryItem::Individual(inventory_item) = item {
if inventory_item.item_id == *id {
match inventory_item.item {
ItemDetail::Weapon(_) => self.equipped.weapon = None,
ItemDetail::Armor(_) => {
self.equipped.armor = None;
self.equipped.unit = [None; 4];
}
ItemDetail::Shield(_) => self.equipped.shield = None,
ItemDetail::Unit(_) => {
for unit in self.equipped.unit.iter_mut() {
if *unit == Some(inventory_item.entity_id) {
*unit = None
}
}
}
ItemDetail::Mag(_) => self.equipped.mag = Some(inventory_item.entity_id),
_ => {}
}
}
}
}
}
pub fn as_inventory_entity(&self, character_id: &CharacterEntityId) -> InventoryEntity {
InventoryEntity {
items: self.items.iter()
.map(|item| {
match item {
InventoryItem::Individual(item) => {
InventoryItemEntity::Individual(ItemEntity {
id: item.entity_id,
location: ItemLocation::Inventory {
character_id: *character_id,
},
item: item.item.clone(),
})
},
InventoryItem::Stacked(items) => {
InventoryItemEntity::Stacked(items.entity_ids.iter()
.map(|id| {
ItemEntity {
id: *id,
location: ItemLocation::Inventory {
character_id: *character_id,
},
item: ItemDetail::Tool(items.tool)
}
})
.collect())
},
}
})
.collect()
}
}
pub fn as_equipped_entity(&self) -> EquippedEntity {
self.equipped.clone()
}
}

256
src/ship/items/manager.rs

@ -4,7 +4,7 @@ use thiserror::Error;
use crate::entity::gateway::EntityGateway;
use crate::entity::character::{CharacterEntity, CharacterEntityId};
use crate::entity::item::{ItemDetail, ItemLocation, BankName};
use crate::entity::item::{Meseta, NewItemEntity, ItemEntity};
use crate::entity::item::{Meseta, NewItemEntity, ItemEntity, InventoryItemEntity, EquippedEntity, InventoryEntity, BankItemEntity, BankEntity};
use crate::entity::item::tool::{Tool, ToolType};
use crate::entity::item::unit;
use crate::ship::map::MapArea;
@ -48,40 +48,10 @@ pub enum ItemManagerError {
CannotGetIndividualItem,
InvalidSlot(u8, u8), // slots available, slot attempted
NoArmorEquipped,
GatewayError(#[from] crate::entity::gateway::GatewayError)
GatewayError(#[from] crate::entity::gateway::GatewayError),
StackedItemError(Vec<ItemEntity>),
}
async fn update_inventory_slots<EG: EntityGateway>(entity_gateway: &mut EG, character: &CharacterEntity, inventory: &CharacterInventory) -> Result<(), ItemManagerError> {
for (slot, item) in inventory.iter().enumerate() {
match item {
InventoryItem::Individual(individual_inventory_item) => {
entity_gateway.change_item_location(
&individual_inventory_item.entity_id,
ItemLocation::Inventory {
character_id: character.id,
slot: slot,
equipped: individual_inventory_item.equipped,
}
).await?
},
InventoryItem::Stacked(stacked_inventory_item) => {
for entity_id in stacked_inventory_item.entity_ids.iter() {
entity_gateway.change_item_location(
entity_id,
ItemLocation::Inventory {
character_id: character.id,
slot: slot,
equipped: false,
}).await?
}
}
}
}
Ok(())
}
pub struct ItemManager {
id_counter: u32,
@ -115,98 +85,66 @@ impl ItemManager {
// TODO: Result
pub async fn load_character<EG: EntityGateway>(&mut self, entity_gateway: &mut EG, character: &CharacterEntity) -> Result<(), ItemManagerError> {
let items = entity_gateway.get_items_by_character(&character.id).await?;
let inventory_items = items.clone().into_iter()
.filter_map(|item| {
match item.location {
ItemLocation::Inventory{slot, equipped, ..} => Some((item.id, item.item, slot, equipped)),
_ => None,
}
})
.fold(BTreeMap::new(), |mut acc, (id, item, slot, equipped)| {
if item.is_stackable() {
if let ItemDetail::Tool(tool) = item {
let inventory_item = acc.entry(slot).or_insert(InventoryItem::Stacked(StackedInventoryItem {
entity_ids: Vec::new(),
let inventory = entity_gateway.get_character_inventory(&character.id).await?;
let bank = entity_gateway.get_character_bank(&character.id, BankName("".into())).await?;
let equipped = entity_gateway.get_character_equips(&character.id).await?;
let inventory_items = inventory.items.into_iter()
.map(|item| -> Result<InventoryItem, ItemManagerError> {
Ok(match item {
InventoryItemEntity::Individual(item) => {
InventoryItem::Individual(IndividualInventoryItem {
entity_id: item.id,
item_id: self.next_global_item_id(),
tool: tool,
}));
if let InventoryItem::Stacked(ref mut stacked_inventory_item) = inventory_item {
stacked_inventory_item.entity_ids.push(id);
}
}
}
else {
acc.insert(slot, InventoryItem::Individual(IndividualInventoryItem {
entity_id: id,
item: item.item,
})
},
InventoryItemEntity::Stacked(items) => {
InventoryItem::Stacked(StackedInventoryItem {
entity_ids: items.iter().map(|i| i.id).collect(),
item_id: self.next_global_item_id(),
item: item,
equipped: equipped,
}));
}
acc
});
// TODO: not using BankName anymore, clean this up
let mut bank_items = items.into_iter()
.filter_map(|item| {
match item.location {
ItemLocation::Bank{name, ..} => Some((item.id, item.item, name)),
_ => None,
}
tool: items.get(0)
.ok_or(ItemManagerError::StackedItemError(items.clone()))?
.item
.clone()
.as_tool()
.ok_or(ItemManagerError::StackedItemError(items.clone()))?
})
.fold(BTreeMap::new(), |mut acc, (id, item, name)| {
acc.entry(name).or_insert(Vec::new()).push((id, item));
acc
},
})
.into_iter()
.map(|(bank_name, bank_items)| {
let stacked_bank_items = bank_items.into_iter()
.fold(Vec::new(), |mut acc, (id, bank_item)| {
if bank_item.is_stackable() {
let existing_item = acc.iter_mut()
.find(|item| {
if let (BankItem::Stacked(stacked_bank_item), &ItemDetail::Tool(ref tool)) = (item, &bank_item) {
stacked_bank_item.tool == *tool
}
else {
false
}
});
match existing_item {
Some(item) => {
if let BankItem::Stacked(ref mut stacked_bank_item) = item {
stacked_bank_item.entity_ids.push(id);
}
}
None => {
if let ItemDetail::Tool(tool) = bank_item {
acc.push(BankItem::Stacked(StackedBankItem {
entity_ids: vec![id],
})
.collect::<Result<Vec<_>, _>>()?;
let character_inventory = CharacterInventory::new(inventory_items, &equipped);
let bank_items = bank.items.into_iter()
.map(|item| -> Result<BankItem, ItemManagerError> {
Ok(match item {
BankItemEntity::Individual(item) => {
BankItem::Individual(IndividualBankItem {
entity_id: item.id,
item_id: self.next_global_item_id(),
tool: tool,
}));
}
item: item.item,
})
},
}
}
else {
acc.push(BankItem::Individual(IndividualBankItem {
entity_id: id,
BankItemEntity::Stacked(items) => {
BankItem::Stacked(StackedBankItem {
entity_ids: items.iter().map(|i| i.id).collect(),
item_id: self.next_global_item_id(),
item: bank_item,
}));
}
acc
});
(bank_name, CharacterBank::new(stacked_bank_items))
tool: items.get(0)
.ok_or(ItemManagerError::StackedItemError(items.clone()))?
.item
.clone()
.as_tool()
.ok_or(ItemManagerError::StackedItemError(items.clone()))?
})
},
})
})
.collect::<BTreeMap<_, _>>();
let inventory = CharacterInventory::new(inventory_items.into_iter().map(|(_k, v)| v).take(30).collect());
self.character_inventory.insert(character.id, inventory);
self.character_bank.insert(character.id, bank_items.remove(&BankName("".to_string())).unwrap_or(CharacterBank::new(Vec::new())));
.collect::<Result<Vec<_>, _>>()?;
let character_bank = CharacterBank::new(bank_items);
self.character_inventory.insert(character.id, character_inventory);
self.character_bank.insert(character.id, character_bank);
Ok(())
}
@ -301,8 +239,6 @@ impl ItemManager {
&new_inventory_item.entity_id,
ItemLocation::Inventory {
character_id: character.id,
slot: slot.0,
equipped: false,
}
).await?;
if let Some(_) = new_inventory_item.mag() {
@ -325,8 +261,6 @@ impl ItemManager {
&entity_id,
ItemLocation::Inventory {
character_id: character.id,
slot: slot.0,
equipped: false,
}
).await?;
}
@ -356,6 +290,7 @@ impl ItemManager {
}
};
entity_gateway.set_character_inventory(&character.id, &inventory.as_inventory_entity(&character.id)).await?;
floor_item.remove_from_floor();
Ok(trigger_create_item)
}
@ -487,7 +422,7 @@ impl ItemManager {
},
}
update_inventory_slots(entity_gateway, character, &inventory).await?;
entity_gateway.set_character_inventory(&character.id, &inventory.as_inventory_entity(&character.id)).await?;
Ok(())
}
@ -549,6 +484,7 @@ impl ItemManager {
).await?;
}
entity_gateway.set_character_inventory(&character.id, &inventory.as_inventory_entity(&character.id)).await?;
Ok(stacked_floor_item)
}
@ -567,7 +503,7 @@ impl ItemManager {
ItemLocation::Consumed).await?;
}
update_inventory_slots(entity_gateway, character, &inventory).await?;
entity_gateway.set_character_inventory(&character.id, &inventory.as_inventory_entity(&character.id)).await?;
Ok(consumed_item)
}
@ -604,7 +540,8 @@ impl ItemManager {
}
}
update_inventory_slots(entity_gateway, character, &inventory).await?;
entity_gateway.set_character_inventory(&character.id, &inventory.as_inventory_entity(&character.id)).await?;
entity_gateway.set_character_bank(&character.id, &bank.as_bank_entity(&character.id, &BankName("".into())), BankName("".into())).await?;
Ok(())
}
@ -621,6 +558,7 @@ impl ItemManager {
.ok_or(ItemManagerError::NoCharacter(character.id))?;
let item_to_withdraw = bank.get_item_handle_by_id(item_id).ok_or(ItemManagerError::NoSuchItemId(item_id))?;
let inventory_item_slot = {
let inventory_item = inventory.withdraw_item(item_to_withdraw, amount).ok_or(ItemManagerError::Idunnoman)?;
match inventory_item {
@ -628,8 +566,6 @@ impl ItemManager {
entity_gateway.change_item_location(&individual_inventory_item.entity_id,
ItemLocation::Inventory {
character_id: character.id,
slot: slot,
equipped: false,
}).await?;
},
(InventoryItem::Stacked(stacked_inventory_item), slot) => {
@ -637,14 +573,16 @@ impl ItemManager {
entity_gateway.change_item_location(entity_id,
ItemLocation::Inventory {
character_id: character.id,
slot: slot,
equipped: false,
}).await?;
}
}
}
inventory_item.1
};
Ok(inventory_item.0)
entity_gateway.set_character_inventory(&character.id, &inventory.as_inventory_entity(&character.id)).await?;
entity_gateway.set_character_bank(&character.id, &bank.as_bank_entity(&character.id, &BankName("".into())), BankName("".into())).await?;
inventory.slot(inventory_item_slot).ok_or(ItemManagerError::Idunnoman)
}
pub async fn player_feeds_mag_item<EG: EntityGateway>(&mut self,
@ -681,7 +619,7 @@ impl ItemManager {
}).await?;
}
update_inventory_slots(entity_gateway, character, &inventory).await?;
entity_gateway.set_character_inventory(&character.id, &inventory.as_inventory_entity(&character.id)).await?;
Ok(())
}
@ -795,7 +733,7 @@ impl ItemManager {
}
_ => {}
}
update_inventory_slots(entity_gateway, character, &inventory).await?;
entity_gateway.set_character_inventory(&character.id, &inventory.as_inventory_entity(&character.id)).await?;
Ok(())
}
@ -835,8 +773,6 @@ impl ItemManager {
entity_gateway.change_item_location(entity_id,
ItemLocation::Inventory {
character_id: character.id,
slot: slot.0,
equipped: false,
}).await?;
}
picked_up_item.item_id
@ -863,8 +799,6 @@ impl ItemManager {
entity_gateway.change_item_location(&picked_up_item.entity_id,
ItemLocation::Inventory {
character_id: character.id,
slot: slot.0,
equipped: false,
}).await?;
picked_up_item.item_id
};
@ -891,14 +825,14 @@ impl ItemManager {
entity_gateway.change_item_location(&picked_up_item.entity_id,
ItemLocation::Inventory {
character_id: character.id,
slot: slot.0,
equipped: false,
}).await?;
picked_up_item.item_id
};
inventory.get_item_by_id(item_id).ok_or(ItemManagerError::ItemIdNotInInventory(item_id))?
},
};
entity_gateway.set_character_inventory(&character.id, &inventory.as_inventory_entity(&character.id)).await?;
Ok(inventory_item)
}
@ -910,31 +844,8 @@ impl ItemManager {
equip_slot: u8)
-> Result<(), ItemManagerError> {
let inventory = self.character_inventory.get_mut(&character.id).ok_or(ItemManagerError::NoCharacter(character.id))?;
let mut inventory_item_handle = inventory.get_item_handle_by_id(item_id).ok_or(ItemManagerError::NoSuchItemId(item_id))?;
let slot = inventory_item_handle.get_slot();
let inventory_item = inventory_item_handle.item_mut().ok_or(ItemManagerError::CannotGetMutItem)?.individual().ok_or(ItemManagerError::CannotGetIndividualItem)?;
inventory_item.equipped = true;
if let ItemDetail::Unit(u) = inventory_item.item {
if equip_slot > 0 {
inventory_item.item = ItemDetail::Unit(unit::Unit {
unit: u.unit,
modifier: u.modifier,
armor_slot: ((equip_slot & 0x7) - 1) % 4, // or just be lazy and do equip_slot - 9
});
} else {
inventory_item.item = ItemDetail::Unit(unit::Unit {
unit: u.unit,
modifier: u.modifier,
armor_slot: 0,
});
}
};
entity_gateway.change_item_location(&inventory_item.entity_id, ItemLocation::Inventory{
character_id: character.id,
slot: slot,
equipped: true,
}).await?;
entity_gateway.change_item(&inventory_item.entity_id, &inventory_item.item).await?;
inventory.equip(&item_id, equip_slot);
entity_gateway.set_character_equips(&character.id, &inventory.as_equipped_entity()).await?;
Ok(())
}
@ -944,23 +855,8 @@ impl ItemManager {
item_id: ClientItemId)
-> Result<(), ItemManagerError> {
let inventory = self.character_inventory.get_mut(&character.id).ok_or(ItemManagerError::NoCharacter(character.id))?;
let mut inventory_item_handle = inventory.get_item_handle_by_id(item_id).ok_or(ItemManagerError::NoSuchItemId(item_id))?;
let slot = inventory_item_handle.get_slot();
let inventory_item = inventory_item_handle.item_mut().ok_or(ItemManagerError::CannotGetMutItem)?.individual().ok_or(ItemManagerError::CannotGetIndividualItem)?;
inventory_item.equipped = false;
if let ItemDetail::Unit(u) = inventory_item.item {
inventory_item.item = ItemDetail::Unit(unit::Unit {
unit: u.unit,
modifier: u.modifier,
armor_slot: 0,
});
};
entity_gateway.change_item_location(&inventory_item.entity_id, ItemLocation::Inventory{
character_id: character.id,
slot: slot,
equipped: false,
}).await;
entity_gateway.change_item(&inventory_item.entity_id, &inventory_item.item).await?;
inventory.unequip(&item_id);
entity_gateway.set_character_equips(&character.id, &inventory.as_equipped_entity()).await?;
Ok(())
}
@ -978,7 +874,7 @@ impl ItemManager {
.collect();
inventory.set_items(sorted_inventory_items);
update_inventory_slots(entity_gateway, character, &inventory).await;
entity_gateway.set_character_inventory(&character.id, &inventory.as_inventory_entity(&character.id)).await?;
Ok(())
}
}

4
src/ship/packet/handler/lobby.rs

@ -38,9 +38,9 @@ pub fn block_selected(id: ClientId,
.build();
Ok(vec![
SendShipPacket::FullCharacter(FullCharacter {
SendShipPacket::FullCharacter(Box::new(FullCharacter {
character: fc,
}),
})),
SendShipPacket::CharDataRequest(CharDataRequest {}),
SendShipPacket::LobbyList(LobbyList::new()),
])

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

@ -316,7 +316,13 @@ where
EG: EntityGateway
{
let client = clients.get(&id).ok_or(ShipError::ClientNotFound(id))?;
item_manager.player_equips_item(entity_gateway, &client.character, ClientItemId(pkt.item_id), pkt.sub_menu).await?;
let equip_slot = if pkt.sub_menu > 0 {
((pkt.sub_menu & 0x7) - 1) % 4
}
else {
0
};
item_manager.player_equips_item(entity_gateway, &client.character, ClientItemId(pkt.item_id), equip_slot).await?;
Ok(Box::new(None.into_iter()))
}
@ -330,30 +336,7 @@ where
EG: EntityGateway
{
let client = clients.get(&id).ok_or(ShipError::ClientNotFound(id))?;
let equipped_unit_ids: Vec<ClientItemId> = {
item_manager.player_unequips_item(entity_gateway, &client.character, ClientItemId(pkt.item_id)).await?;
let inventory = item_manager.get_character_inventory(&client.character).unwrap();
let ue_item = inventory.get_item_by_id(ClientItemId(pkt.item_id)).ok_or(ShipError::ItemError)?;
if let ItemType::Armor(_) = ue_item.item_type() {
inventory.items()
.iter()
.filter(|inv_item| {
if let ItemType::Unit(_) = inv_item.item_type() {
return inv_item.equipped()
}
false
})
.map(|u| u.item_id())
.collect()
} else {
Vec::new()
}
};
for unit_id in equipped_unit_ids {
item_manager.player_unequips_item(entity_gateway, &client.character, unit_id).await;
}
Ok(Box::new(None.into_iter()))
}

10
src/ship/ship.rs

@ -145,7 +145,7 @@ pub enum SendShipPacket {
ShipWelcome(ShipWelcome),
LoginResponse(LoginResponse),
ShipBlockList(ShipBlockList),
FullCharacter(FullCharacter),
FullCharacter(Box<FullCharacter>),
CharDataRequest(CharDataRequest),
JoinLobby(JoinLobby),
AddToLobby(AddToLobby),
@ -341,10 +341,10 @@ impl<EG: EntityGateway> ShipServerStateBuilder<EG> {
ShipServerState {
entity_gateway: self.entity_gateway.unwrap(),
clients: HashMap::new(),
client_location: ClientLocation::new(),
client_location: Box::new(ClientLocation::new()),
level_table: CharacterLevelTable::new(),
name: self.name.unwrap_or("NAMENOTSET".into()),
rooms: [None; MAX_ROOMS],
rooms: Box::new([None; MAX_ROOMS]),
item_manager: items::ItemManager::new(),
quests: quests::load_quests("data/quests.toml".into()).unwrap(),
ip: self.ip.unwrap_or(Ipv4Addr::new(127,0,0,1)),
@ -358,10 +358,10 @@ impl<EG: EntityGateway> ShipServerStateBuilder<EG> {
pub struct ShipServerState<EG: EntityGateway> {
entity_gateway: EG,
pub clients: Clients,
client_location: ClientLocation,
client_location: Box<ClientLocation>,
level_table: CharacterLevelTable,
name: String,
pub rooms: Rooms,
pub rooms: Box<Rooms>,
item_manager: items::ItemManager,
quests: quests::QuestList,
ip: Ipv4Addr,

1
src/ship/shops/armor.rs

@ -85,7 +85,6 @@ impl ShopItem for ArmorShopItem {
ItemDetail::Unit(Unit {
unit: *unit,
modifier: None,
armor_slot: 0,
})
},
}

847
tests/test_bank.rs
File diff suppressed because it is too large
View File

4
tests/test_character.rs

@ -14,9 +14,9 @@ async fn test_save_options() {
let (user1, _char1) = new_user_character(&mut entity_gateway, "a1", "a").await;
let mut ship = ShipServerState::builder()
let mut ship = Box::new(ShipServerState::builder()
.gateway(entity_gateway.clone())
.build();
.build());
log_in_char(&mut ship, ClientId(1), "a1", "a").await;
join_lobby(&mut ship, ClientId(1)).await;

16
tests/test_exp_gain.rs

@ -17,9 +17,9 @@ async fn test_character_gains_exp() {
let (_user1, _char1) = new_user_character(&mut entity_gateway, "a1", "a").await;
let mut ship = ShipServerState::builder()
let mut ship = Box::new(ShipServerState::builder()
.gateway(entity_gateway.clone())
.build();
.build());
log_in_char(&mut ship, ClientId(1), "a1", "a").await;
join_lobby(&mut ship, ClientId(1)).await;
create_room(&mut ship, ClientId(1), "room", "").await;
@ -56,9 +56,9 @@ async fn test_character_levels_up() {
char1.exp = 49;
entity_gateway.save_character(&char1).await.unwrap();
let mut ship = ShipServerState::builder()
let mut ship = Box::new(ShipServerState::builder()
.gateway(entity_gateway.clone())
.build();
.build());
log_in_char(&mut ship, ClientId(1), "a1", "a").await;
join_lobby(&mut ship, ClientId(1)).await;
create_room(&mut ship, ClientId(1), "room", "").await;
@ -94,9 +94,9 @@ async fn test_character_levels_up_multiple_times() {
let (_user1, _char1) = new_user_character(&mut entity_gateway, "a1", "a").await;
let mut ship = ShipServerState::builder()
let mut ship = Box::new(ShipServerState::builder()
.gateway(entity_gateway.clone())
.build();
.build());
log_in_char(&mut ship, ClientId(1), "a1", "a").await;
join_lobby(&mut ship, ClientId(1)).await;
create_room(&mut ship, ClientId(1), "room", "").await;
@ -139,9 +139,9 @@ async fn test_one_character_gets_full_exp_and_other_attacker_gets_partial() {
let (_user1, _char1) = new_user_character(&mut entity_gateway, "a1", "a").await;
let (_user2, _char2) = new_user_character(&mut entity_gateway, "a2", "a").await;
let mut ship = ShipServerState::builder()
let mut ship = Box::new(ShipServerState::builder()
.gateway(entity_gateway.clone())
.build();
.build());
log_in_char(&mut ship, ClientId(1), "a1", "a").await;
log_in_char(&mut ship, ClientId(2), "a2", "a").await;

204
tests/test_item_actions.rs

@ -16,7 +16,8 @@ async fn test_equip_unit_from_equip_menu() {
let (_user1, char1) = new_user_character(&mut entity_gateway, "a1", "a").await;
entity_gateway.create_item(
let mut p1_inv = Vec::new();
p1_inv.push(entity_gateway.create_item(
item::NewItemEntity {
item: item::ItemDetail::Armor(
item::armor::Armor{
@ -28,44 +29,46 @@ async fn test_equip_unit_from_equip_menu() {
}),
location: item::ItemLocation::Inventory {
character_id: char1.id,
slot: 0,
equipped: true,
}
}).await.unwrap();
}).await.unwrap());
entity_gateway.create_item(
p1_inv.push(entity_gateway.create_item(
item::NewItemEntity {
item: item::ItemDetail::Unit(
item::unit::Unit{
unit: item::unit::UnitType::KnightPower,
modifier: None,
armor_slot: 0,
}),
location: item::ItemLocation::Inventory {
character_id: char1.id,
slot: 1,
equipped: false,
}
}).await.unwrap();
}).await.unwrap());
entity_gateway.create_item(
p1_inv.push(entity_gateway.create_item(
item::NewItemEntity {
item: item::ItemDetail::Unit(
item::unit::Unit{
unit: item::unit::UnitType::KnightPower,
modifier: Some(item::unit::UnitModifier::Plus),
armor_slot: 0,
}),
location: item::ItemLocation::Inventory {
character_id: char1.id,
slot: 2,
equipped: false,
}
}).await.unwrap();
}).await.unwrap());
let equipped = item::EquippedEntity {
weapon: Some(p1_inv[0].id),
armor: None,
shield: None,
unit: [None; 4],
mag: None,
};
entity_gateway.set_character_equips(&char1.id, &equipped).await.unwrap();
entity_gateway.set_character_inventory(&char1.id, &item::InventoryEntity::new(p1_inv)).await.unwrap();
let mut ship = ShipServerState::builder()
let mut ship = Box::new(ShipServerState::builder()
.gateway(entity_gateway.clone())
.build();
.build());
log_in_char(&mut ship, ClientId(1), "a1", "a").await;
join_lobby(&mut ship, ClientId(1)).await;
create_room(&mut ship, ClientId(1), "room", "").await;
@ -87,44 +90,11 @@ async fn test_equip_unit_from_equip_menu() {
unknown1: 0,
})))).await.unwrap().for_each(drop);
let items = entity_gateway.get_items_by_character(&char1.id).await.unwrap();
let (unit1, unit2) = (&items[1], &items[2]);
let unit1_equipped = match unit1.location {
item::ItemLocation::Inventory{equipped, ..} => equipped,
_ => false,
};
let unit2_equipped = match unit2.location {
item::ItemLocation::Inventory{equipped, ..} => equipped,
_ => false,
};
assert!({
match unit1.item {
item::ItemDetail::Unit(u) => {
if u.armor_slot == 0 && unit1_equipped {
true
} else {
false
}
},
_ => false,
}
});
assert!({
match unit2.item {
item::ItemDetail::Unit(u) => {
if u.armor_slot == 1 && unit2_equipped {
true
} else {
false
}
},
_ => false,
}
});
let equips = entity_gateway.get_character_equips(&char1.id).await.unwrap();
assert_eq!(equips.unit[0].unwrap(), item::ItemEntityId(2));
assert_eq!(equips.unit[1].unwrap(), item::ItemEntityId(3));
assert!(equips.unit[2].is_none());
assert!(equips.unit[3].is_none());
}
#[async_std::test]
@ -133,7 +103,8 @@ async fn test_unequip_armor_with_units() {
let (_user1, char1) = new_user_character(&mut entity_gateway, "a1", "a").await;
entity_gateway.create_item(
let mut p1_inv = Vec::new();
p1_inv.push(entity_gateway.create_item(
item::NewItemEntity {
item: item::ItemDetail::Armor(
item::armor::Armor{
@ -145,44 +116,46 @@ async fn test_unequip_armor_with_units() {
}),
location: item::ItemLocation::Inventory {
character_id: char1.id,
slot: 0,
equipped: true,
}
}).await;
}).await.unwrap());
entity_gateway.create_item(
p1_inv.push(entity_gateway.create_item(
item::NewItemEntity {
item: item::ItemDetail::Unit(
item::unit::Unit{
unit: item::unit::UnitType::KnightPower,
modifier: None,
armor_slot: 0,
}),
location: item::ItemLocation::Inventory {
character_id: char1.id,
slot: 1,
equipped: true,
}
}).await.unwrap();
}).await.unwrap());
entity_gateway.create_item(
p1_inv.push(entity_gateway.create_item(
item::NewItemEntity {
item: item::ItemDetail::Unit(
item::unit::Unit{
unit: item::unit::UnitType::KnightPower,
modifier: Some(item::unit::UnitModifier::Plus),
armor_slot: 1,
}),
location: item::ItemLocation::Inventory {
character_id: char1.id,
slot: 2,
equipped: true,
}
}).await.unwrap();
}).await.unwrap());
let equipped = item::EquippedEntity {
weapon: None,
armor: Some(p1_inv[0].id),
shield: None,
unit: [Some(p1_inv[1].id), Some(p1_inv[2].id), None, None],
mag: None,
};
entity_gateway.set_character_equips(&char1.id, &equipped).await.unwrap();
entity_gateway.set_character_inventory(&char1.id, &item::InventoryEntity::new(p1_inv)).await.unwrap();
let mut ship = ShipServerState::builder()
let mut ship = Box::new(ShipServerState::builder()
.gateway(entity_gateway.clone())
.build();
.build());
log_in_char(&mut ship, ClientId(1), "a1", "a").await;
join_lobby(&mut ship, ClientId(1)).await;
create_room(&mut ship, ClientId(1), "room", "").await;
@ -194,27 +167,12 @@ async fn test_unequip_armor_with_units() {
unknown1: 0,
})))).await.unwrap().for_each(drop);
let items = entity_gateway.get_items_by_character(&char1.id).await.unwrap();
let (armor, unit1, unit2) = (&items[0], &items[1], &items[2]);
let armor_equipped = match armor.location {
item::ItemLocation::Inventory{equipped, ..} => equipped,
_ => true,
};
let unit1_equipped = match unit1.location {
item::ItemLocation::Inventory{equipped, ..} => equipped,
_ => true,
};
let unit2_equipped = match unit2.location {
item::ItemLocation::Inventory{equipped, ..} => equipped,
_ => true,
};
assert!(armor_equipped == false);
assert!(unit1_equipped == false);
assert!(unit2_equipped == false);
let equips = entity_gateway.get_character_equips(&char1.id).await.unwrap();
assert!(equips.armor.is_none());
assert!(equips.unit[0].is_none());
assert!(equips.unit[1].is_none());
assert!(equips.unit[2].is_none());
assert!(equips.unit[3].is_none());
}
#[async_std::test]
@ -223,7 +181,8 @@ async fn test_sort_items() {
let (_user1, char1) = new_user_character(&mut entity_gateway, "a1", "a").await;
entity_gateway.create_item(
let mut p1_inv = Vec::new();
p1_inv.push(entity_gateway.create_item(
item::NewItemEntity {
item: item::ItemDetail::Armor(
item::armor::Armor{
@ -235,55 +194,54 @@ async fn test_sort_items() {
}),
location: item::ItemLocation::Inventory {
character_id: char1.id,
slot: 0,
equipped: true,
}
}).await;
}).await.unwrap());
entity_gateway.create_item(
p1_inv.push(entity_gateway.create_item(
item::NewItemEntity {
item: item::ItemDetail::Unit(
item::unit::Unit{
unit: item::unit::UnitType::KnightPower,
modifier: None,
armor_slot: 0,
}),
location: item::ItemLocation::Inventory {
character_id: char1.id,
slot: 1,
equipped: false,
}
}).await;
}).await.unwrap());
entity_gateway.create_item(
p1_inv.push(entity_gateway.create_item(
item::NewItemEntity {
item: item::ItemDetail::Unit(
item::unit::Unit{
unit: item::unit::UnitType::KnightPower,
modifier: Some(item::unit::UnitModifier::Plus),
armor_slot: 0,
}),
location: item::ItemLocation::Inventory {
character_id: char1.id,
slot: 2,
equipped: false,
}
}).await;
}).await.unwrap());
entity_gateway.set_character_inventory(&char1.id, &item::InventoryEntity::new(p1_inv)).await.unwrap();
let mut ship = ShipServerState::builder()
let mut ship = Box::new(ShipServerState::builder()
.gateway(entity_gateway.clone())
.build();
.build());
log_in_char(&mut ship, ClientId(1), "a1", "a").await;
join_lobby(&mut ship, ClientId(1)).await;
create_room(&mut ship, ClientId(1), "room", "").await;
let old_items = entity_gateway.get_items_by_character(&char1.id).await.unwrap();
assert!(old_items[0].item.item_type() == item::ItemType::Armor(item::armor::ArmorType::Frame));
assert!(old_items[0].location == item::ItemLocation::Inventory{
character_id: char1.id,
slot: 0,
equipped: true,
});
let inventory_items = entity_gateway.get_character_inventory(&char1.id).await.unwrap();
assert_eq!(inventory_items.items.len(), 3);
inventory_items.items[0].with_individual(|item| {
assert_eq!(item.id, item::ItemEntityId(1));
}).unwrap();
inventory_items.items[1].with_individual(|item| {
assert_eq!(item.id, item::ItemEntityId(2));
}).unwrap();
inventory_items.items[2].with_individual(|item| {
assert_eq!(item.id, item::ItemEntityId(3));
}).unwrap();
ship.handle(ClientId(1), &RecvShipPacket::Message(Message::new(GameMessage::SortItems(SortItems {
client: 255,
@ -293,11 +251,15 @@ async fn test_sort_items() {
0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF],
})))).await.unwrap().for_each(drop);
let items = entity_gateway.get_items_by_character(&char1.id).await.unwrap();
assert!(items[0].item.item_type() == item::ItemType::Armor(item::armor::ArmorType::Frame));
assert!(items[0].location == item::ItemLocation::Inventory{
character_id: char1.id,
slot: 2,
equipped: true,
});
let inventory_items = entity_gateway.get_character_inventory(&char1.id).await.unwrap();
assert_eq!(inventory_items.items.len(), 3);
inventory_items.items[0].with_individual(|item| {
assert_eq!(item.id, item::ItemEntityId(2));
}).unwrap();
inventory_items.items[1].with_individual(|item| {
assert_eq!(item.id, item::ItemEntityId(3));
}).unwrap();
inventory_items.items[2].with_individual(|item| {
assert_eq!(item.id, item::ItemEntityId(1));
}).unwrap();
}

286
tests/test_item_pickup.rs

@ -17,7 +17,8 @@ async fn test_pick_up_item_stack_of_items_already_in_inventory() {
let (_user1, char1) = new_user_character(&mut entity_gateway, "a1", "a").await;
let (_user2, char2) = new_user_character(&mut entity_gateway, "a2", "a").await;
entity_gateway.create_item(
let mut p1_monomate = Vec::new();
p1_monomate.push(entity_gateway.create_item(
item::NewItemEntity {
item: item::ItemDetail::Tool(
item::tool::Tool {
@ -26,14 +27,14 @@ async fn test_pick_up_item_stack_of_items_already_in_inventory() {
),
location: item::ItemLocation::Inventory {
character_id: char1.id,
slot: 0,
equipped: false,
}
}).await.unwrap();
}).await.unwrap());
let mut p2_items = Vec::new();
for (slot, tool) in vec![item::tool::ToolType::Monomate, item::tool::ToolType::Monofluid].into_iter().enumerate() {
for _ in 0..5 {
entity_gateway.create_item(
let mut item = Vec::new();
for _ in 0..5usize {
item.push(entity_gateway.create_item(
item::NewItemEntity {
item: item::ItemDetail::Tool(
item::tool::Tool {
@ -42,16 +43,18 @@ async fn test_pick_up_item_stack_of_items_already_in_inventory() {
),
location: item::ItemLocation::Inventory {
character_id: char2.id,
slot: slot,
equipped: false,
}
}).await.unwrap();
}).await.unwrap());
}
p2_items.push(item);
}
let mut ship = ShipServerState::builder()
entity_gateway.set_character_inventory(&char1.id, &item::InventoryEntity::new(vec![p1_monomate])).await.unwrap();
entity_gateway.set_character_inventory(&char2.id, &item::InventoryEntity::new(p2_items)).await.unwrap();
let mut ship = Box::new(ShipServerState::builder()
.gateway(entity_gateway.clone())
.build();
.build());
log_in_char(&mut ship, ClientId(1), "a1", "a").await;
log_in_char(&mut ship, ClientId(2), "a2", "a").await;
@ -80,21 +83,13 @@ async fn test_pick_up_item_stack_of_items_already_in_inventory() {
unknown: [0; 3]
})))).await.unwrap().for_each(drop);
let p1_items = entity_gateway.get_items_by_character(&char1.id).await.unwrap();
assert!(p1_items.len() == 6);
let p1_item_ids = p1_items.iter().map(|item| {
item.id
}).collect::<Vec<_>>();
assert!(p1_item_ids == vec![item::ItemEntityId(1), item::ItemEntityId(2), item::ItemEntityId(3), item::ItemEntityId(4), item::ItemEntityId(5), item::ItemEntityId(6)]);
let all_items_are_monomates = p1_items.iter().all(|item| {
match item.item {
item::ItemDetail::Tool(tool) => tool.tool == item::tool::ToolType::Monomate,
_ => false
}
});
assert!(all_items_are_monomates);
let inventory_items = entity_gateway.get_character_inventory(&char1.id).await.unwrap();
assert_eq!(inventory_items.items.len(), 1);
inventory_items.items[0].with_stacked(|items| {
assert_eq!(items.iter().map(|i| i.id).collect::<Vec<_>>(),
vec![item::ItemEntityId(1), item::ItemEntityId(2), item::ItemEntityId(3), item::ItemEntityId(4), item::ItemEntityId(5), item::ItemEntityId(6)]);
assert!(items.iter().all(|item| item.item.item_type() == item::ItemType::Tool(item::tool::ToolType::Monomate)));
}).unwrap();
}
#[async_std::test]
@ -104,7 +99,8 @@ async fn test_pick_up_item_stack_of_items_not_already_held() {
let (_user1, char1) = new_user_character(&mut entity_gateway, "a1", "a").await;
let (_user2, char2) = new_user_character(&mut entity_gateway, "a2", "a").await;
entity_gateway.create_item(
let mut p2_monomate = Vec::new();
p2_monomate.push(entity_gateway.create_item(
item::NewItemEntity {
item: item::ItemDetail::Tool(
item::tool::Tool {
@ -113,14 +109,14 @@ async fn test_pick_up_item_stack_of_items_not_already_held() {
),
location: item::ItemLocation::Inventory {
character_id: char2.id,
slot: 0,
equipped: false,
}
}).await.unwrap();
}).await.unwrap());
let mut ship = ShipServerState::builder()
entity_gateway.set_character_inventory(&char2.id, &item::InventoryEntity::new(vec![p2_monomate])).await.unwrap();
let mut ship = Box::new(ShipServerState::builder()
.gateway(entity_gateway.clone())
.build();
.build());
log_in_char(&mut ship, ClientId(1), "a1", "a").await;
log_in_char(&mut ship, ClientId(2), "a2", "a").await;
@ -149,14 +145,13 @@ async fn test_pick_up_item_stack_of_items_not_already_held() {
unknown: [0; 3]
})))).await.unwrap().for_each(drop);
let p1_items = entity_gateway.get_items_by_character(&char1.id).await.unwrap();
assert!(p1_items.len() == 1);
let first_item = p1_items.get(0).unwrap();
assert!(first_item.id == item::ItemEntityId(1));
assert!(first_item.item == item::ItemDetail::Tool(item::tool::Tool {
tool: item::tool::ToolType::Monomate,
}));
let inventory_items = entity_gateway.get_character_inventory(&char1.id).await.unwrap();
assert_eq!(inventory_items.items.len(), 1);
inventory_items.items[0].with_stacked(|items| {
assert_eq!(items.len(), 1);
assert_eq!(items[0].id, item::ItemEntityId(1));
assert_eq!(items[0].item.item_type(), item::ItemType::Tool(item::tool::ToolType::Monomate));
}).unwrap();
}
#[async_std::test]
@ -166,8 +161,9 @@ async fn test_pick_up_meseta_when_inventory_full() {
let (user1, char1) = new_user_character(&mut entity_gateway, "a1", "a").await;
let (user2, mut char2) = new_user_character(&mut entity_gateway, "a2", "a").await;
for slot in 0..30 {
entity_gateway.create_item(
let mut p1_items = Vec::new();
for _ in 0..30usize {
p1_items.push(entity_gateway.create_item(
item::NewItemEntity {
item: item::ItemDetail::Weapon(
item::weapon::Weapon {
@ -181,18 +177,18 @@ async fn test_pick_up_meseta_when_inventory_full() {
),
location: item::ItemLocation::Inventory {
character_id: char1.id,
slot: slot,
equipped: false,
}
}).await.unwrap();
}).await.unwrap());
}
entity_gateway.set_character_inventory(&char1.id, &item::InventoryEntity::new(p1_items)).await.unwrap();
char2.meseta = 300;
entity_gateway.save_character(&char2).await.unwrap();
let mut ship = ShipServerState::builder()
let mut ship = Box::new(ShipServerState::builder()
.gateway(entity_gateway.clone())
.build();
.build());
log_in_char(&mut ship, ClientId(1), "a1", "a").await;
log_in_char(&mut ship, ClientId(2), "a2", "a").await;
@ -227,8 +223,8 @@ async fn test_pick_up_meseta_when_inventory_full() {
unknown: [0; 3]
})))).await.unwrap().for_each(drop);
let p1_items = entity_gateway.get_items_by_character(&char1.id).await.unwrap();
assert!(p1_items.len() == 30);
let inventory_items = entity_gateway.get_character_inventory(&char1.id).await.unwrap();
assert_eq!(inventory_items.items.len(), 30);
let characters1 = entity_gateway.get_characters_by_user(&user1).await.unwrap();
let c1 = characters1.get(0).as_ref().unwrap().as_ref().unwrap();
@ -245,8 +241,9 @@ async fn test_pick_up_partial_stacked_item_when_inventory_is_otherwise_full() {
let (_user1, char1) = new_user_character(&mut entity_gateway, "a1", "a").await;
let (_user2, char2) = new_user_character(&mut entity_gateway, "a2", "a").await;
for slot in 0..29 {
entity_gateway.create_item(
let mut p1_inv = Vec::new();
for slot in 0..29usize {
p1_inv.push(entity_gateway.create_item(
item::NewItemEntity {
item: item::ItemDetail::Weapon(
item::weapon::Weapon {
@ -260,13 +257,11 @@ async fn test_pick_up_partial_stacked_item_when_inventory_is_otherwise_full() {
),
location: item::ItemLocation::Inventory {
character_id: char1.id,
slot: slot,
equipped: false,
}
}).await.unwrap();
}).await.unwrap().into());
}
entity_gateway.create_item(
p1_inv.push(item::InventoryItemEntity::Stacked(vec![entity_gateway.create_item(
item::NewItemEntity {
item: item::ItemDetail::Tool(
item::tool::Tool {
@ -275,11 +270,11 @@ async fn test_pick_up_partial_stacked_item_when_inventory_is_otherwise_full() {
),
location: item::ItemLocation::Inventory {
character_id: char1.id,
slot: 29,
equipped: false,
}
}).await.unwrap();
entity_gateway.create_item(
}).await.unwrap()]));
let mut p2_monomates = Vec::new();
p2_monomates.push(entity_gateway.create_item(
item::NewItemEntity {
item: item::ItemDetail::Tool(
item::tool::Tool {
@ -288,14 +283,15 @@ async fn test_pick_up_partial_stacked_item_when_inventory_is_otherwise_full() {
),
location: item::ItemLocation::Inventory {
character_id: char2.id,
slot: 0,
equipped: false,
}
}).await.unwrap();
}).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![p2_monomates])).await.unwrap();
let mut ship = ShipServerState::builder()
let mut ship = Box::new(ShipServerState::builder()
.gateway(entity_gateway.clone())
.build();
.build());
log_in_char(&mut ship, ClientId(1), "a1", "a").await;
log_in_char(&mut ship, ClientId(2), "a2", "a").await;
@ -324,17 +320,11 @@ async fn test_pick_up_partial_stacked_item_when_inventory_is_otherwise_full() {
unknown: [0; 3]
})))).await.unwrap().for_each(drop);
let p1_items = entity_gateway.get_items_by_character(&char1.id).await.unwrap();
assert!(p1_items.len() == 31);
let monomate1 = p1_items.get(29).unwrap();
assert!(monomate1.item == item::ItemDetail::Tool(item::tool::Tool {
tool: item::tool::ToolType::Monomate,
}));
let monomate2 = p1_items.get(30).unwrap();
assert!(monomate2.item == item::ItemDetail::Tool(item::tool::Tool {
tool: item::tool::ToolType::Monomate,
}));
let inventory_items = entity_gateway.get_character_inventory(&char1.id).await.unwrap();
assert_eq!(inventory_items.items.len(), 30);
inventory_items.items[29].with_stacked(|items| {
assert_eq!(items.len(), 2);
}).unwrap();
}
#[async_std::test]
@ -344,8 +334,9 @@ async fn test_can_not_pick_up_item_when_inventory_full() {
let (_user1, char1) = new_user_character(&mut entity_gateway, "a1", "a").await;
let (_user2, char2) = new_user_character(&mut entity_gateway, "a2", "a").await;
for slot in 0..30 {
entity_gateway.create_item(
let mut p1_inv = Vec::new();
for slot in 0..30usize {
p1_inv.push(entity_gateway.create_item(
item::NewItemEntity {
item: item::ItemDetail::Weapon(
item::weapon::Weapon {
@ -359,13 +350,12 @@ async fn test_can_not_pick_up_item_when_inventory_full() {
),
location: item::ItemLocation::Inventory {
character_id: char1.id,
slot: slot,
equipped: false,
}
}).await.unwrap();
}).await.unwrap());
}
entity_gateway.create_item(
let mut p2_inv = Vec::new();
p2_inv.push(entity_gateway.create_item(
item::NewItemEntity {
item: item::ItemDetail::Weapon(
item::weapon::Weapon {
@ -379,14 +369,15 @@ async fn test_can_not_pick_up_item_when_inventory_full() {
),
location: item::ItemLocation::Inventory {
character_id: char2.id,
slot: 0,
equipped: false,
}
}).await.unwrap();
}).await.unwrap());
let mut ship = ShipServerState::builder()
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();
.build());
log_in_char(&mut ship, ClientId(1), "a1", "a").await;
log_in_char(&mut ship, ClientId(2), "a2", "a").await;
@ -415,11 +406,10 @@ async fn test_can_not_pick_up_item_when_inventory_full() {
unknown: [0; 3]
})))).await.unwrap().for_each(drop);
let p1_items = entity_gateway.get_items_by_character(&char1.id).await.unwrap();
assert!(p1_items.len() == 30);
let p2_items = entity_gateway.get_items_by_character(&char2.id).await.unwrap();
assert!(p2_items.len() == 0);
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(), 0);
ship.handle(ClientId(2), &RecvShipPacket::DirectMessage(DirectMessage::new(0, GameMessage::PickupItem(PickupItem {
client: 0,
@ -429,8 +419,10 @@ async fn test_can_not_pick_up_item_when_inventory_full() {
unknown: [0; 3]
})))).await.unwrap().for_each(drop);
let p2_items = entity_gateway.get_items_by_character(&char2.id).await.unwrap();
assert!(p2_items.len() == 1);
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(), 1);
}
#[async_std::test]
@ -442,9 +434,9 @@ async fn test_can_not_drop_more_meseta_than_is_held() {
char1.meseta = 300;
entity_gateway.save_character(&char1).await.unwrap();
let mut ship = ShipServerState::builder()
let mut ship = Box::new(ShipServerState::builder()
.gateway(entity_gateway.clone())
.build();
.build());
log_in_char(&mut ship, ClientId(1), "a1", "a").await;
join_lobby(&mut ship, ClientId(1)).await;
@ -481,8 +473,9 @@ async fn test_pick_up_stack_that_would_exceed_stack_limit() {
let (_user1, char1) = new_user_character(&mut entity_gateway, "a1", "a").await;
let (_user2, char2) = new_user_character(&mut entity_gateway, "a2", "a").await;
for _ in 0..6 {
entity_gateway.create_item(
let mut p1_monomates = Vec::new();
for _ in 0..6usize {
p1_monomates.push(entity_gateway.create_item(
item::NewItemEntity {
item: item::ItemDetail::Tool(
item::tool::Tool {
@ -491,13 +484,13 @@ async fn test_pick_up_stack_that_would_exceed_stack_limit() {
),
location: item::ItemLocation::Inventory {
character_id: char1.id,
slot: 0,
equipped: false,
}
}).await.unwrap();
}).await.unwrap());
}
for _ in 0..6 {
entity_gateway.create_item(
let mut p2_monomates = Vec::new();
for _ in 0..6usize {
p2_monomates.push(entity_gateway.create_item(
item::NewItemEntity {
item: item::ItemDetail::Tool(
item::tool::Tool {
@ -506,15 +499,15 @@ async fn test_pick_up_stack_that_would_exceed_stack_limit() {
),
location: item::ItemLocation::Inventory {
character_id: char2.id,
slot: 0,
equipped: false,
}
}).await.unwrap();
}).await.unwrap());
}
entity_gateway.set_character_inventory(&char1.id, &item::InventoryEntity::new(vec![p1_monomates])).await.unwrap();
entity_gateway.set_character_inventory(&char2.id, &item::InventoryEntity::new(vec![p2_monomates])).await.unwrap();
let mut ship = ShipServerState::builder()
let mut ship = Box::new(ShipServerState::builder()
.gateway(entity_gateway.clone())
.build();
.build());
log_in_char(&mut ship, ClientId(1), "a1", "a").await;
log_in_char(&mut ship, ClientId(2), "a2", "a").await;
@ -544,11 +537,13 @@ async fn test_pick_up_stack_that_would_exceed_stack_limit() {
})))).await.unwrap().collect::<Vec<_>>();
assert!(packets.len() == 0);
let p1_items = entity_gateway.get_items_by_character(&char1.id).await.unwrap();
assert!(p1_items.len() == 6);
let p2_items = entity_gateway.get_items_by_character(&char2.id).await.unwrap();
assert!(p2_items.len() == 0);
let p1_items = entity_gateway.get_character_inventory(&char1.id).await.unwrap();
assert_eq!(p1_items.items.len(), 1);
p1_items.items[0].with_stacked(|items| {
assert_eq!(items.len(), 6);
}).unwrap();
let p2_items = entity_gateway.get_character_inventory(&char2.id).await.unwrap();
assert_eq!(p2_items.items.len(), 0);
}
#[async_std::test]
@ -563,9 +558,9 @@ async fn test_can_not_pick_up_meseta_when_full() {
char2.meseta = 300;
entity_gateway.save_character(&char2).await.unwrap();
let mut ship = ShipServerState::builder()
let mut ship = Box::new(ShipServerState::builder()
.gateway(entity_gateway.clone())
.build();
.build());
log_in_char(&mut ship, ClientId(1), "a1", "a").await;
log_in_char(&mut ship, ClientId(2), "a2", "a").await;
@ -621,9 +616,9 @@ async fn test_meseta_caps_at_999999_when_trying_to_pick_up_more() {
char2.meseta = 300;
entity_gateway.save_character(&char2).await.unwrap();
let mut ship = ShipServerState::builder()
let mut ship = Box::new(ShipServerState::builder()
.gateway(entity_gateway.clone())
.build();
.build());
log_in_char(&mut ship, ClientId(1), "a1", "a").await;
log_in_char(&mut ship, ClientId(2), "a2", "a").await;
@ -673,8 +668,9 @@ async fn test_player_drops_partial_stack_and_other_player_picks_it_up() {
let (_user1, char1) = new_user_character(&mut entity_gateway, "a1", "a").await;
let (_user2, char2) = new_user_character(&mut entity_gateway, "a2", "a").await;
for _ in 0..5 {
entity_gateway.create_item(
let mut monomates = Vec::new();
for _ in 0..5usize {
monomates.push(entity_gateway.create_item(
item::NewItemEntity {
item: item::ItemDetail::Tool(
item::tool::Tool {
@ -683,15 +679,15 @@ async fn test_player_drops_partial_stack_and_other_player_picks_it_up() {
),
location: item::ItemLocation::Inventory {
character_id: char1.id,
slot: 0,
equipped: false,
}
}).await.unwrap();
}).await.unwrap());
}
let mut ship = ShipServerState::builder()
entity_gateway.set_character_inventory(&char1.id, &item::InventoryEntity::new(vec![monomates])).await.unwrap();
let mut ship = Box::new(ShipServerState::builder()
.gateway(entity_gateway.clone())
.build();
.build());
log_in_char(&mut ship, ClientId(1), "a1", "a").await;
log_in_char(&mut ship, ClientId(2), "a2", "a").await;
@ -726,35 +722,17 @@ async fn test_player_drops_partial_stack_and_other_player_picks_it_up() {
unknown: [0; 3]
})))).await.unwrap().for_each(drop);
let p1_items = entity_gateway.get_items_by_character(&char1.id).await.unwrap();
assert!(p1_items.len() == 3);
let p1_item_ids = p1_items.iter().map(|item| {
item.id
}).collect::<Vec<_>>();
assert!(p1_item_ids == vec![item::ItemEntityId(3), item::ItemEntityId(4), item::ItemEntityId(5)]);
let all_items_are_monomates = p1_items.iter().all(|item| {
match item.item {
item::ItemDetail::Tool(tool) => tool.tool == item::tool::ToolType::Monomate,
_ => false
}
});
assert!(all_items_are_monomates);
let p2_items = entity_gateway.get_items_by_character(&char2.id).await.unwrap();
assert!(p2_items.len() == 2);
let p2_item_ids = p2_items.iter().map(|item| {
item.id
}).collect::<Vec<_>>();
assert!(p2_item_ids == vec![item::ItemEntityId(1), item::ItemEntityId(2)]);
let all_items_are_monomates = p1_items.iter().all(|item| {
match item.item {
item::ItemDetail::Tool(tool) => tool.tool == item::tool::ToolType::Monomate,
_ => false
}
});
assert!(all_items_are_monomates);
let inventory_items = entity_gateway.get_character_inventory(&char1.id).await.unwrap();
assert_eq!(inventory_items.items.len(), 1);
inventory_items.items[0].with_stacked(|items| {
assert_eq!(items.iter().map(|i| i.id).collect::<Vec<_>>(),
vec![item::ItemEntityId(3), item::ItemEntityId(4), item::ItemEntityId(5)]);
}).unwrap();
let inventory_items = entity_gateway.get_character_inventory(&char2.id).await.unwrap();
assert_eq!(inventory_items.items.len(), 1);
inventory_items.items[0].with_stacked(|items| {
assert_eq!(items.iter().map(|i| i.id).collect::<Vec<_>>(),
vec![item::ItemEntityId(1), item::ItemEntityId(2)]);
}).unwrap();
}

190
tests/test_item_use.rs

@ -18,9 +18,11 @@ async fn test_use_monomate() {
let (_user1, char1) = new_user_character(&mut entity_gateway, "a1", "a").await;
for (slot, tool) in vec![item::tool::ToolType::Monomate, item::tool::ToolType::Monofluid].into_iter().enumerate() {
for _ in 0..2 {
entity_gateway.create_item(
let mut p1_items = Vec::new();
for tool in vec![item::tool::ToolType::Monomate, item::tool::ToolType::Monofluid].into_iter() {
let mut item = Vec::new();
for _ in 0..2usize {
item.push(entity_gateway.create_item(
item::NewItemEntity {
item: item::ItemDetail::Tool(
item::tool::Tool {
@ -29,16 +31,17 @@ async fn test_use_monomate() {
),
location: item::ItemLocation::Inventory {
character_id: char1.id,
slot: slot,
equipped: false,
}
}).await.unwrap();
}).await.unwrap());
}
p1_items.push(item::InventoryItemEntity::Stacked(item));
}
let mut ship = ShipServerState::builder()
entity_gateway.set_character_inventory(&char1.id, &item::InventoryEntity::new(p1_items)).await.unwrap();
let mut ship = Box::new(ShipServerState::builder()
.gateway(entity_gateway.clone())
.build();
.build());
log_in_char(&mut ship, ClientId(1), "a1", "a").await;
join_lobby(&mut ship, ClientId(1)).await;
create_room(&mut ship, ClientId(1), "room", "").await;
@ -49,23 +52,14 @@ async fn test_use_monomate() {
item_id: 0x10000,
})))).await.unwrap().for_each(drop);
let items = entity_gateway.get_items_by_character(&char1.id).await.unwrap();
assert!(items.iter().filter(|item| {
if let item::ItemDetail::Tool(t) = item.item {
t.tool == item::tool::ToolType::Monomate
}
else {
false
}
}).count() == 1);
assert!(items.iter().filter(|item| {
if let item::ItemDetail::Tool(t) = item.item {
t.tool == item::tool::ToolType::Monofluid
}
else {
false
}
}).count() == 2);
let inventory_items = entity_gateway.get_character_inventory(&char1.id).await.unwrap();
assert_eq!(inventory_items.items.len(), 2);
inventory_items.items[0].with_stacked(|items| {
assert_eq!(items.len(), 1)
}).unwrap();
inventory_items.items[1].with_stacked(|items| {
assert_eq!(items.len(), 2)
}).unwrap();
}
#[async_std::test]
@ -74,9 +68,11 @@ async fn test_use_monomate_twice() {
let (_user1, char1) = new_user_character(&mut entity_gateway, "a1", "a").await;
for (slot, tool) in vec![item::tool::ToolType::Monomate, item::tool::ToolType::Monofluid].into_iter().enumerate() {
for _ in 0..3 {
entity_gateway.create_item(
let mut p1_items = Vec::new();
for tool in vec![item::tool::ToolType::Monomate, item::tool::ToolType::Monofluid].into_iter() {
let mut item = Vec::new();
for _ in 0..3usize {
item.push(entity_gateway.create_item(
item::NewItemEntity {
item: item::ItemDetail::Tool(
item::tool::Tool {
@ -85,16 +81,17 @@ async fn test_use_monomate_twice() {
),
location: item::ItemLocation::Inventory {
character_id: char1.id,
slot: slot,
equipped: false,
}
}).await.unwrap();
}).await.unwrap());
}
p1_items.push(item::InventoryItemEntity::Stacked(item));
}
let mut ship = ShipServerState::builder()
entity_gateway.set_character_inventory(&char1.id, &item::InventoryEntity::new(p1_items)).await.unwrap();
let mut ship = Box::new(ShipServerState::builder()
.gateway(entity_gateway.clone())
.build();
.build());
log_in_char(&mut ship, ClientId(1), "a1", "a").await;
join_lobby(&mut ship, ClientId(1)).await;
create_room(&mut ship, ClientId(1), "room", "").await;
@ -110,23 +107,14 @@ async fn test_use_monomate_twice() {
item_id: 0x10000,
})))).await.unwrap().for_each(drop);
let items = entity_gateway.get_items_by_character(&char1.id).await.unwrap();
assert!(items.iter().filter(|item| {
if let item::ItemDetail::Tool(t) = item.item {
t.tool == item::tool::ToolType::Monomate
}
else {
false
}
}).count() == 1);
assert!(items.iter().filter(|item| {
if let item::ItemDetail::Tool(t) = item.item {
t.tool == item::tool::ToolType::Monofluid
}
else {
false
}
}).count() == 3);
let inventory_items = entity_gateway.get_character_inventory(&char1.id).await.unwrap();
assert_eq!(inventory_items.items.len(), 2);
inventory_items.items[0].with_stacked(|items| {
assert_eq!(items.len(), 1)
}).unwrap();
inventory_items.items[1].with_stacked(|items| {
assert_eq!(items.len(), 3)
}).unwrap();
}
#[async_std::test]
@ -135,9 +123,9 @@ async fn test_use_last_monomate() {
let (_user1, char1) = new_user_character(&mut entity_gateway, "a1", "a").await;
for (slot, tool) in vec![item::tool::ToolType::Monomate, item::tool::ToolType::Monofluid].into_iter().enumerate() {
for _ in 0..1 {
entity_gateway.create_item(
let mut p1_inv = Vec::new();
for tool in vec![item::tool::ToolType::Monomate, item::tool::ToolType::Monofluid].into_iter() {
p1_inv.push(item::InventoryItemEntity::Stacked(vec![entity_gateway.create_item(
item::NewItemEntity {
item: item::ItemDetail::Tool(
item::tool::Tool {
@ -146,16 +134,15 @@ async fn test_use_last_monomate() {
),
location: item::ItemLocation::Inventory {
character_id: char1.id,
slot: slot,
equipped: false,
}
}).await.unwrap();
}
}).await.unwrap()]));
}
let mut ship = ShipServerState::builder()
entity_gateway.set_character_inventory(&char1.id, &item::InventoryEntity::new(p1_inv)).await.unwrap();
let mut ship = Box::new(ShipServerState::builder()
.gateway(entity_gateway.clone())
.build();
.build());
log_in_char(&mut ship, ClientId(1), "a1", "a").await;
join_lobby(&mut ship, ClientId(1)).await;
create_room(&mut ship, ClientId(1), "room", "").await;
@ -166,23 +153,13 @@ async fn test_use_last_monomate() {
item_id: 0x10000,
})))).await.unwrap().for_each(drop);
let items = entity_gateway.get_items_by_character(&char1.id).await.unwrap();
assert!(items.iter().filter(|item| {
if let item::ItemDetail::Tool(t) = item.item {
t.tool == item::tool::ToolType::Monomate
}
else {
false
}
}).count() == 0);
assert!(items.iter().filter(|item| {
if let item::ItemDetail::Tool(t) = item.item {
t.tool == item::tool::ToolType::Monofluid
}
else {
false
}
}).count() == 1);
let inventory_items = entity_gateway.get_character_inventory(&char1.id).await.unwrap();
assert_eq!(inventory_items.items.len(), 1);
inventory_items.items[0].with_stacked(|items| {
assert_eq!(items.len(), 1);
assert_eq!(items[0].item.item_type(), item::ItemType::Tool(item::tool::ToolType::Monofluid));
}).unwrap();
}
#[async_std::test]
@ -191,7 +168,8 @@ async fn test_use_nonstackable_tool() {
let (_user1, char1) = new_user_character(&mut entity_gateway, "a1", "a").await;
entity_gateway.create_item(
let mut p1_items = Vec::new();
p1_items.push(entity_gateway.create_item(
item::NewItemEntity {
item: item::ItemDetail::Tool(
item::tool::Tool {
@ -200,14 +178,14 @@ async fn test_use_nonstackable_tool() {
),
location: item::ItemLocation::Inventory {
character_id: char1.id,
slot: 0,
equipped: false,
}
}).await.unwrap();
}).await.unwrap());
entity_gateway.set_character_inventory(&char1.id, &item::InventoryEntity::new(p1_items)).await.unwrap();
let mut ship = ShipServerState::builder()
let mut ship = Box::new(ShipServerState::builder()
.gateway(entity_gateway.clone())
.build();
.build());
log_in_char(&mut ship, ClientId(1), "a1", "a").await;
join_lobby(&mut ship, ClientId(1)).await;
create_room(&mut ship, ClientId(1), "room", "").await;
@ -218,8 +196,8 @@ async fn test_use_nonstackable_tool() {
item_id: 0x10000,
})))).await.unwrap().for_each(drop);
let items = entity_gateway.get_items_by_character(&char1.id).await.unwrap();
assert!(items.len() == 0);
let inventory_items = entity_gateway.get_character_inventory(&char1.id).await.unwrap();
assert_eq!(inventory_items.items.len(), 0);
}
#[async_std::test]
@ -228,9 +206,11 @@ async fn test_use_materials() {
let (user1, char1) = new_user_character(&mut entity_gateway, "a1", "a").await;
for (slot, tool) in vec![item::tool::ToolType::PowerMaterial, item::tool::ToolType::LuckMaterial].into_iter().enumerate() {
for _ in 0..5 {
entity_gateway.create_item(
let mut p1_inv = Vec::new();
for tool in vec![item::tool::ToolType::PowerMaterial, item::tool::ToolType::LuckMaterial].into_iter() {
let mut item = Vec::new();
for _ in 0..5usize {
item.push(entity_gateway.create_item(
item::NewItemEntity {
item: item::ItemDetail::Tool(
item::tool::Tool {
@ -239,16 +219,17 @@ async fn test_use_materials() {
),
location: item::ItemLocation::Inventory {
character_id: char1.id,
slot: slot,
equipped: false,
}
}).await.unwrap();
}).await.unwrap());
}
p1_inv.push(item::InventoryItemEntity::Stacked(item));
}
let mut ship = ShipServerState::builder()
entity_gateway.set_character_inventory(&char1.id, &item::InventoryEntity::new(p1_inv)).await.unwrap();
let mut ship = Box::new(ShipServerState::builder()
.gateway(entity_gateway.clone())
.build();
.build());
log_in_char(&mut ship, ClientId(1), "a1", "a").await;
join_lobby(&mut ship, ClientId(1)).await;
create_room(&mut ship, ClientId(1), "room", "").await;
@ -269,23 +250,14 @@ async fn test_use_materials() {
item_id: 0x10001,
})))).await.unwrap().for_each(drop);
let items = entity_gateway.get_items_by_character(&char1.id).await.unwrap();
assert!(items.iter().filter(|item| {
if let item::ItemDetail::Tool(t) = item.item {
t.tool == item::tool::ToolType::PowerMaterial
}
else {
false
}
}).count() == 4);
assert!(items.iter().filter(|item| {
if let item::ItemDetail::Tool(t) = item.item {
t.tool == item::tool::ToolType::LuckMaterial
}
else {
false
}
}).count() == 3);
let inventory_items = entity_gateway.get_character_inventory(&char1.id).await.unwrap();
assert_eq!(inventory_items.items.len(), 2);
inventory_items.items[0].with_stacked(|items| {
assert_eq!(items.len(), 4)
}).unwrap();
inventory_items.items[1].with_stacked(|items| {
assert_eq!(items.len(), 3)
}).unwrap();
let characters = entity_gateway.get_characters_by_user(&user1).await.unwrap();
let char = characters[0].as_ref().unwrap();

95
tests/test_mags.rs

@ -17,19 +17,20 @@ async fn test_mag_feed() {
let (_user1, char1) = new_user_character(&mut entity_gateway, "a1", "a").await;
entity_gateway.create_item(
let mag = entity_gateway.create_item(
item::NewItemEntity {
item: item::ItemDetail::Mag(
item::mag::Mag::baby_mag(0)
),
location: item::ItemLocation::Inventory {
character_id: char1.id,
slot: 0,
equipped: true,
//equipped: true,
}
}).await.unwrap();
for _ in 0..7 {
entity_gateway.create_item(
let mut monomates = Vec::new();
for _ in 0..7usize {
monomates.push(entity_gateway.create_item(
item::NewItemEntity {
item: item::ItemDetail::Tool(
item::tool::Tool {
@ -38,20 +39,32 @@ async fn test_mag_feed() {
),
location: item::ItemLocation::Inventory {
character_id: char1.id,
slot: 1,
equipped: false,
}
}).await.unwrap();
}).await.unwrap());
}
let mut ship = ShipServerState::builder()
let equipped = item::EquippedEntity {
weapon: None,
armor: None,
shield: None,
unit: [None; 4],
mag: Some(mag.id),
};
entity_gateway.set_character_equips(&char1.id, &equipped).await.unwrap();
let mut inventory = Vec::new();
inventory.push(mag.into());
inventory.push(item::InventoryItemEntity::Stacked(monomates));
entity_gateway.set_character_inventory(&char1.id, &item::InventoryEntity::new(inventory)).await.unwrap();
let mut ship = Box::new(ShipServerState::builder()
.gateway(entity_gateway.clone())
.build();
.build());
log_in_char(&mut ship, ClientId(1), "a1", "a").await;
join_lobby(&mut ship, ClientId(1)).await;
create_room(&mut ship, ClientId(1), "room", "").await;
for _ in 0..7 {
for _ in 0..7usize {
ship.handle(ClientId(1), &RecvShipPacket::Message(Message::new(GameMessage::PlayerFeedMag(PlayerFeedMag {
client: 0,
target: 0,
@ -60,10 +73,12 @@ async fn test_mag_feed() {
})))).await.unwrap().for_each(drop);
}
let p1_items = entity_gateway.get_items_by_character(&char1.id).await.unwrap();
let mag = p1_items.get(0).unwrap();
match &mag.item {
let inventory_items = entity_gateway.get_character_inventory(&char1.id).await.unwrap();
assert_eq!(inventory_items.items.len(), 1);
inventory_items.items[0].with_individual(|item| {
match &item.item {
item::ItemDetail::Mag(mag) => {
println!("mag! {:?}", mag);
assert!(mag.level() == 7);
assert!(mag.def() == 5);
assert!(mag.pow() == 2);
@ -72,6 +87,7 @@ async fn test_mag_feed() {
}
_ => panic!()
}
}).unwrap();
}
#[async_std::test]
@ -87,21 +103,21 @@ async fn test_mag_change_owner() {
char2.section_id = SectionID::Whitill;
entity_gateway.save_character(&char2).await.unwrap();
entity_gateway.create_item(
let mag = entity_gateway.create_item(
item::NewItemEntity {
item: item::ItemDetail::Mag(
item::mag::Mag::baby_mag(0)
),
location: item::ItemLocation::Inventory {
character_id: char1.id,
slot: 0,
equipped: true,
}
}).await.unwrap();
let mut ship = ShipServerState::builder()
entity_gateway.set_character_inventory(&char1.id, &item::InventoryEntity::new(vec![mag])).await.unwrap();
let mut ship = Box::new(ShipServerState::builder()
.gateway(entity_gateway.clone())
.build();
.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;
@ -128,15 +144,17 @@ async fn test_mag_change_owner() {
unknown: [0; 3]
})))).await.unwrap().for_each(drop);
let p2_items = entity_gateway.get_items_by_character(&char2.id).await.unwrap();
let mag = p2_items.get(0).unwrap();
match &mag.item {
let inventory_items = entity_gateway.get_character_inventory(&char2.id).await.unwrap();
assert_eq!(inventory_items.items.len(), 1);
inventory_items.items[0].with_individual(|item| {
match &item.item {
item::ItemDetail::Mag(mag) => {
assert!(mag.class == CharacterClass::FOmarl);
assert!(mag.id == SectionID::Whitill);
},
}
_ => panic!()
}
}).unwrap();
}
@ -153,12 +171,10 @@ async fn test_mag_cell() {
),
location: item::ItemLocation::Inventory {
character_id: char1.id,
slot: 0,
equipped: true,
}
}).await.unwrap();
for _ in 0..1000 {
for _ in 0..1000usize {
let fed_tool = entity_gateway.create_item(
item::NewItemEntity {
item: item::ItemDetail::Tool (
@ -172,7 +188,7 @@ async fn test_mag_cell() {
}).await.unwrap();
entity_gateway.feed_mag(&mag.id, &fed_tool.id).await.unwrap();
}
entity_gateway.create_item(
let mag_cell = entity_gateway.create_item(
item::NewItemEntity {
item: item::ItemDetail::Tool(
item::tool::Tool {
@ -181,14 +197,22 @@ async fn test_mag_cell() {
),
location: item::ItemLocation::Inventory {
character_id: char1.id,
slot: 1,
equipped: false,
}
}).await.unwrap();
let mut ship = ShipServerState::builder()
let equipped = item::EquippedEntity {
weapon: None,
armor: None,
shield: None,
unit: [None; 4],
mag: Some(mag.id),
};
entity_gateway.set_character_equips(&char1.id, &equipped).await.unwrap();
entity_gateway.set_character_inventory(&char1.id, &item::InventoryEntity::new(vec![mag, mag_cell])).await.unwrap();
let mut ship = Box::new(ShipServerState::builder()
.gateway(entity_gateway.clone())
.build();
.build());
log_in_char(&mut ship, ClientId(1), "a1", "a").await;
join_lobby(&mut ship, ClientId(1)).await;
create_room(&mut ship, ClientId(1), "room", "").await;
@ -199,12 +223,13 @@ async fn test_mag_cell() {
item_id: 0x10001,
})))).await.unwrap().for_each(drop);
let p1_items = entity_gateway.get_items_by_character(&char1.id).await.unwrap();
let mag = p1_items.get(0).unwrap();
match &mag.item {
let inventory_items = entity_gateway.get_character_inventory(&char1.id).await.unwrap();
inventory_items.items[0].with_individual(|item| {
match &item.item {
item::ItemDetail::Mag(mag) => {
assert!(mag.mag == item::mag::MagType::Soniti);
assert_eq!(mag.mag, item::mag::MagType::Soniti);
}
_ => panic!()
}
}).unwrap();
}

25
tests/test_rooms.rs

@ -18,8 +18,9 @@ async fn test_item_ids_reset_when_rejoining_rooms() {
let (_user1, char1) = new_user_character(&mut entity_gateway, "a1", "a").await;
let (_user2, char2) = new_user_character(&mut entity_gateway, "a2", "a").await;
for slot in 0..3 {
entity_gateway.create_item(
let mut p1_inv = Vec::new();
for _ in 0..3usize {
p1_inv.push(entity_gateway.create_item(
item::NewItemEntity {
item: item::ItemDetail::Weapon(
item::weapon::Weapon {
@ -33,14 +34,13 @@ async fn test_item_ids_reset_when_rejoining_rooms() {
),
location: item::ItemLocation::Inventory {
character_id: char1.id,
slot: slot,
equipped: false,
}
}).await.unwrap();
}).await.unwrap());
}
for slot in 0..10 {
entity_gateway.create_item(
let mut p2_inv = Vec::new();
for _ in 0..10usize {
p2_inv.push(entity_gateway.create_item(
item::NewItemEntity {
item: item::ItemDetail::Weapon(
item::weapon::Weapon {
@ -54,15 +54,16 @@ async fn test_item_ids_reset_when_rejoining_rooms() {
),
location: item::ItemLocation::Inventory {
character_id: char2.id,
slot: slot,
equipped: false,
}
}).await.unwrap();
}).await.unwrap());
}
let mut ship = ShipServerState::builder()
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();
.build());
log_in_char(&mut ship, ClientId(1), "a1", "a").await;
log_in_char(&mut ship, ClientId(2), "a2", "a").await;

108
tests/test_shops.rs

@ -19,9 +19,9 @@ async fn test_player_opens_weapon_shop() {
char1.exp = 80000000;
entity_gateway.save_character(&char1).await.unwrap();
let mut ship = ShipServerState::builder()
let mut ship = Box::new(ShipServerState::builder()
.gateway(entity_gateway.clone())
.build();
.build());
log_in_char(&mut ship, ClientId(1), "a1", "a").await;
join_lobby(&mut ship, ClientId(1)).await;
create_room_with_difficulty(&mut ship, ClientId(1), "room", "", Difficulty::Ultimate).await;
@ -49,9 +49,9 @@ async fn test_player_opens_tool_shop() {
char1.exp = 80000000;
entity_gateway.save_character(&char1).await.unwrap();
let mut ship = ShipServerState::builder()
let mut ship = Box::new(ShipServerState::builder()
.gateway(entity_gateway.clone())
.build();
.build());
log_in_char(&mut ship, ClientId(1), "a1", "a").await;
join_lobby(&mut ship, ClientId(1)).await;
create_room_with_difficulty(&mut ship, ClientId(1), "room", "", Difficulty::Ultimate).await;
@ -79,9 +79,9 @@ async fn test_player_opens_armor_shop() {
char1.exp = 80000000;
entity_gateway.save_character(&char1).await.unwrap();
let mut ship = ShipServerState::builder()
let mut ship = Box::new(ShipServerState::builder()
.gateway(entity_gateway.clone())
.build();
.build());
log_in_char(&mut ship, ClientId(1), "a1", "a").await;
join_lobby(&mut ship, ClientId(1)).await;
create_room_with_difficulty(&mut ship, ClientId(1), "room", "", Difficulty::Ultimate).await;
@ -110,9 +110,9 @@ async fn test_player_buys_from_weapon_shop() {
char1.meseta = 999999;
entity_gateway.save_character(&char1).await.unwrap();
let mut ship = ShipServerState::builder()
let mut ship = Box::new(ShipServerState::builder()
.gateway(entity_gateway.clone())
.build();
.build());
log_in_char(&mut ship, ClientId(1), "a1", "a").await;
join_lobby(&mut ship, ClientId(1)).await;
create_room_with_difficulty(&mut ship, ClientId(1), "room", "", Difficulty::Ultimate).await;
@ -135,8 +135,9 @@ async fn test_player_buys_from_weapon_shop() {
let characters1 = entity_gateway.get_characters_by_user(&user1).await.unwrap();
let c1 = characters1.get(0).as_ref().unwrap().as_ref().unwrap();
assert!(c1.meseta < 999999);
let p1_items = entity_gateway.get_items_by_character(&char1.id).await.unwrap();
assert_eq!(p1_items.len(), 1);
//let p1_items = entity_gateway.get_items_by_character(&char1.id).await.unwrap();
let p1_items = entity_gateway.get_character_inventory(&char1.id).await.unwrap();
assert_eq!(p1_items.items.len(), 1);
}
#[async_std::test]
@ -148,9 +149,9 @@ async fn test_player_buys_from_tool_shop() {
char1.meseta = 999999;
entity_gateway.save_character(&char1).await.unwrap();
let mut ship = ShipServerState::builder()
let mut ship = Box::new(ShipServerState::builder()
.gateway(entity_gateway.clone())
.build();
.build());
log_in_char(&mut ship, ClientId(1), "a1", "a").await;
join_lobby(&mut ship, ClientId(1)).await;
create_room_with_difficulty(&mut ship, ClientId(1), "room", "", Difficulty::Ultimate).await;
@ -173,8 +174,8 @@ async fn test_player_buys_from_tool_shop() {
let characters1 = entity_gateway.get_characters_by_user(&user1).await.unwrap();
let c1 = characters1.get(0).as_ref().unwrap().as_ref().unwrap();
assert!(c1.meseta < 999999);
let p1_items = entity_gateway.get_items_by_character(&char1.id).await.unwrap();
assert_eq!(p1_items.len(), 1);
let p1_items = entity_gateway.get_character_inventory(&char1.id).await.unwrap();
assert_eq!(p1_items.items.len(), 1);
}
#[async_std::test]
@ -186,9 +187,9 @@ async fn test_player_buys_multiple_from_tool_shop() {
char1.meseta = 999999;
entity_gateway.save_character(&char1).await.unwrap();
let mut ship = ShipServerState::builder()
let mut ship = Box::new(ShipServerState::builder()
.gateway(entity_gateway.clone())
.build();
.build());
log_in_char(&mut ship, ClientId(1), "a1", "a").await;
join_lobby(&mut ship, ClientId(1)).await;
create_room_with_difficulty(&mut ship, ClientId(1), "room", "", Difficulty::Ultimate).await;
@ -211,8 +212,12 @@ async fn test_player_buys_multiple_from_tool_shop() {
let characters1 = entity_gateway.get_characters_by_user(&user1).await.unwrap();
let c1 = characters1.get(0).as_ref().unwrap().as_ref().unwrap();
assert!(c1.meseta < 999999);
let p1_items = entity_gateway.get_items_by_character(&char1.id).await.unwrap();
assert_eq!(p1_items.len(), 5);
let p1_items = entity_gateway.get_character_inventory(&char1.id).await.unwrap();
assert_eq!(p1_items.items.len(), 1);
p1_items.items[0].with_stacked(|item| {
assert_eq!(item.len(), 5);
assert_eq!(item[0].item.item_type(), item::ItemType::Tool(item::tool::ToolType::Monomate));
}).unwrap();
}
#[async_std::test]
@ -224,9 +229,9 @@ async fn test_player_buys_from_armor_shop() {
char1.meseta = 999999;
entity_gateway.save_character(&char1).await.unwrap();
let mut ship = ShipServerState::builder()
let mut ship = Box::new(ShipServerState::builder()
.gateway(entity_gateway.clone())
.build();
.build());
log_in_char(&mut ship, ClientId(1), "a1", "a").await;
join_lobby(&mut ship, ClientId(1)).await;
create_room_with_difficulty(&mut ship, ClientId(1), "room", "", Difficulty::Ultimate).await;
@ -249,8 +254,8 @@ async fn test_player_buys_from_armor_shop() {
let characters1 = entity_gateway.get_characters_by_user(&user1).await.unwrap();
let c1 = characters1.get(0).as_ref().unwrap().as_ref().unwrap();
assert!(c1.meseta < 999999);
let p1_items = entity_gateway.get_items_by_character(&char1.id).await.unwrap();
assert_eq!(p1_items.len(), 1);
let p1_items = entity_gateway.get_character_inventory(&char1.id).await.unwrap();
assert_eq!(p1_items.items.len(), 1);
}
#[async_std::test]
@ -267,9 +272,9 @@ async fn test_other_clients_see_purchase() {
char1.meseta = 999999;
entity_gateway.save_character(&char1).await.unwrap();
let mut ship = ShipServerState::builder()
let mut ship = Box::new(ShipServerState::builder()
.gateway(entity_gateway.clone())
.build();
.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;
@ -318,14 +323,12 @@ async fn test_other_clients_see_stacked_purchase() {
),
location: item::ItemLocation::Inventory {
character_id: char1.id,
slot: 0,
equipped: false,
}
}).await.unwrap();
let mut ship = ShipServerState::builder()
let mut ship = Box::new(ShipServerState::builder()
.gateway(entity_gateway.clone())
.build();
.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;
@ -362,9 +365,9 @@ async fn test_buying_item_without_enough_mseseta() {
let (user1, char1) = new_user_character(&mut entity_gateway, "a1", "a").await;
let mut ship = ShipServerState::builder()
let mut ship = Box::new(ShipServerState::builder()
.gateway(entity_gateway.clone())
.build();
.build());
log_in_char(&mut ship, ClientId(1), "a1", "a").await;
join_lobby(&mut ship, ClientId(1)).await;
create_room_with_difficulty(&mut ship, ClientId(1), "room", "", Difficulty::Ultimate).await;
@ -388,8 +391,8 @@ async fn test_buying_item_without_enough_mseseta() {
let characters1 = entity_gateway.get_characters_by_user(&user1).await.unwrap();
let c1 = characters1.get(0).as_ref().unwrap().as_ref().unwrap();
assert_eq!(c1.meseta, 0);
let p1_items = entity_gateway.get_items_by_character(&char1.id).await.unwrap();
assert_eq!(p1_items.len(), 0);
let p1_items = entity_gateway.get_character_inventory(&char1.id).await.unwrap();
assert_eq!(p1_items.items.len(), 0);
}
#[async_std::test]
@ -401,9 +404,9 @@ async fn test_player_double_buys_from_tool_shop() {
char1.meseta = 999999;
entity_gateway.save_character(&char1).await.unwrap();
let mut ship = ShipServerState::builder()
let mut ship = Box::new(ShipServerState::builder()
.gateway(entity_gateway.clone())
.build();
.build());
log_in_char(&mut ship, ClientId(1), "a1", "a").await;
join_lobby(&mut ship, ClientId(1)).await;
create_room_with_difficulty(&mut ship, ClientId(1), "room", "", Difficulty::Ultimate).await;
@ -444,11 +447,20 @@ async fn test_player_double_buys_from_tool_shop() {
let characters1 = entity_gateway.get_characters_by_user(&user1).await.unwrap();
let c1 = characters1.get(0).as_ref().unwrap().as_ref().unwrap();
assert!(c1.meseta < 999999);
let p1_items = entity_gateway.get_items_by_character(&char1.id).await.unwrap();
assert_eq!(p1_items.len(), 9);
let p1_items = entity_gateway.get_character_inventory(&char1.id).await.unwrap();
assert_eq!(p1_items.items.len(), 2);
p1_items.items[0].with_stacked(|item| {
assert_eq!(item.len(), 7);
assert_eq!(item[0].item.item_type(), item::ItemType::Tool(item::tool::ToolType::Monomate));
}).unwrap();
p1_items.items[1].with_stacked(|item| {
assert_eq!(item.len(), 2);
assert_eq!(item[0].item.item_type(), item::ItemType::Tool(item::tool::ToolType::Dimate));
}).unwrap();
}
#[async_std::test]
async fn test_techs_disappear_from_shop_when_bought() {
let mut entity_gateway = InMemoryGateway::new();
@ -458,9 +470,9 @@ async fn test_techs_disappear_from_shop_when_bought() {
char1.meseta = 999999;
entity_gateway.save_character(&char1).await.unwrap();
let mut ship = ShipServerState::builder()
let mut ship = Box::new(ShipServerState::builder()
.gateway(entity_gateway.clone())
.build();
.build());
log_in_char(&mut ship, ClientId(1), "a1", "a").await;
join_lobby(&mut ship, ClientId(1)).await;
create_room_with_difficulty(&mut ship, ClientId(1), "room", "", Difficulty::Ultimate).await;
@ -502,8 +514,12 @@ async fn test_techs_disappear_from_shop_when_bought() {
unknown1: 0,
})))).await.unwrap().for_each(drop);
let p1_items = entity_gateway.get_items_by_character(&char1.id).await.unwrap();
assert!(p1_items[0].item != p1_items[1].item);
let p1_items = entity_gateway.get_character_inventory(&char1.id).await.unwrap();
p1_items.items[0].with_individual(|item1| {
p1_items.items[1].with_individual(|item2| {
assert_ne!(item1, item2);
}).unwrap();
}).unwrap();
}
// TOOD: this is not deterministic and can randomly fail
@ -516,9 +532,9 @@ async fn test_units_disappear_from_shop_when_bought() {
char1.meseta = 999999;
entity_gateway.save_character(&char1).await.unwrap();
let mut ship = ShipServerState::builder()
let mut ship = Box::new(ShipServerState::builder()
.gateway(entity_gateway.clone())
.build();
.build());
log_in_char(&mut ship, ClientId(1), "a1", "a").await;
join_lobby(&mut ship, ClientId(1)).await;
create_room_with_difficulty(&mut ship, ClientId(1), "room", "", Difficulty::Ultimate).await;
@ -560,6 +576,10 @@ async fn test_units_disappear_from_shop_when_bought() {
unknown1: 0,
})))).await.unwrap().for_each(drop);
let p1_items = entity_gateway.get_items_by_character(&char1.id).await.unwrap();
assert!(p1_items[0].item != p1_items[1].item);
let p1_items = entity_gateway.get_character_inventory(&char1.id).await.unwrap();
p1_items.items[0].with_individual(|item1| {
p1_items.items[1].with_individual(|item2| {
assert_ne!(item1, item2);
}).unwrap();
}).unwrap();
}
Loading…
Cancel
Save