Browse Source

Merge pull request 'properly pick up stacks and a ton of tests' (#160) from properly_pick_up_stacks into master

pbs
jake 5 years ago
parent
commit
ed51aa379c
  1. 13
      Cargo.toml
  2. 46
      src/bin/main.rs
  3. 6
      src/entity/account.rs
  4. 2
      src/entity/character.rs
  5. 18
      src/entity/gateway/inmemory.rs
  6. 2
      src/entity/item/mod.rs
  7. 2
      src/entity/item/tool.rs
  8. 13
      src/lib.rs
  9. 105
      src/ship/items.rs
  10. 2
      src/ship/packet/handler/direct_message.rs
  11. 36
      src/ship/ship.rs
  12. 683
      tests/test_item_pickup.rs

13
Cargo.toml

@ -4,19 +4,6 @@ version = "0.1.0"
authors = ["Jake Probst <jake.probst@gmail.com>"]
edition = "2018"
[[bin]]
name = "everything"
path = "src/main.rs"
#[[bin]]
#name = "patch"
#path = "src/patch_main.rs"
#[[bin]]
#name = "login"
#path = "src/login_main.rs"
[dependencies]
libpso = { git = "http://git.sharnoth.com/jake/libpso" }
async-std = { version = "1.5.0", features = ["unstable"] }

46
src/main.rs → src/bin/main.rs

@ -1,30 +1,16 @@
#![allow(incomplete_features)]
#![feature(const_generics)]
#![feature(maybe_uninit_extra)]
#![feature(const_in_array_repeat_expressions)]
#![feature(drain_filter)]
mod common;
mod entity;
mod patch;
mod login;
mod ship;
use std::time::SystemTime;
use log::{info};
use patch::patch::{PatchServerState, generate_patch_tree, load_config, load_motd};
use login::login::LoginServerState;
use login::character::CharacterServerState;
use ship::ship::ShipServerState;
use entity::account::{NewUserAccountEntity, NewUserSettingsEntity};
use entity::gateway::{EntityGateway, InMemoryGateway};
use entity::character::NewCharacterEntity;
use entity::item::{NewItemEntity, ItemDetail, ItemLocation};
use elseware::patch::patch::{PatchServerState, generate_patch_tree, load_config, load_motd};
use elseware::login::login::LoginServerState;
use elseware::login::character::CharacterServerState;
use elseware::ship::ship::ShipServerState;
use elseware::entity::account::{NewUserAccountEntity, NewUserSettingsEntity};
use elseware::entity::gateway::{EntityGateway, InMemoryGateway};
use elseware::entity::character::NewCharacterEntity;
use elseware::entity::item::{NewItemEntity, ItemDetail, ItemLocation};
use crate::entity::item;
use elseware::entity::item;
fn setup_logger() {
let colors = fern::colors::ColoredLevelConfig::new()
@ -48,7 +34,7 @@ fn setup_logger() {
.chain(std::io::stdout());
let fileout = fern::Dispatch::new()
.level(log::LevelFilter::Trace)
.chain(fern::log_file(format!("elseware-{}.log", chrono::Local::now().format("%Y-%m-%d_%H:%M:%S"))).unwrap());
.chain(fern::log_file(format!("log/elseware-{}.log", chrono::Local::now().format("%Y-%m-%d_%H-%M-%S"))).unwrap());
fern::Dispatch::new()
.chain(stdio)
.chain(fileout)
@ -86,11 +72,11 @@ fn main() {
NewItemEntity {
item: ItemDetail::Weapon(
item::weapon::Weapon {
weapon: item::weapon::WeaponType::Handgun,
weapon: item::weapon::WeaponType::Raygun,
grind: 5,
special: Some(item::weapon::WeaponSpecial::Hell),
attrs: [Some(item::weapon::WeaponAttribute{attr: item::weapon::Attribute::Hit, value: 100}),
None,
Some(item::weapon::WeaponAttribute{attr: item::weapon::Attribute::Dark, value: 100}),
None,],
tekked: true,
}
@ -111,7 +97,7 @@ fn main() {
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);
crate::common::mainloop::mainloop_async(patch_state, patch_config.port).await;
elseware::common::mainloop::mainloop_async(patch_state, patch_config.port).await;
});
let thread_entity_gateway = entity_gateway.clone();
@ -119,7 +105,7 @@ fn main() {
info!("[auth] starting server");
let auth_state = LoginServerState::new(thread_entity_gateway);
common::mainloop::mainloop_async(auth_state, login::login::LOGIN_PORT).await;
elseware::common::mainloop::mainloop_async(auth_state, elseware::login::login::LOGIN_PORT).await;
});
let thread_entity_gateway = entity_gateway.clone();
@ -127,14 +113,14 @@ fn main() {
info!("[character] starting server");
let char_state = CharacterServerState::new(thread_entity_gateway);
common::mainloop::mainloop_async(char_state, login::character::CHARACTER_PORT).await;
elseware::common::mainloop::mainloop_async(char_state, elseware::login::character::CHARACTER_PORT).await;
});
let thread_entity_gateway = entity_gateway.clone();
let ship = async_std::task::spawn(async {
info!("[ship] starting server");
let ship_state = ShipServerState::new(thread_entity_gateway);
common::mainloop::mainloop_async(ship_state, ship::ship::SHIP_PORT).await;
elseware::common::mainloop::mainloop_async(ship_state, elseware::ship::ship::SHIP_PORT).await;
});
futures::join!(patch, auth, character, ship);

6
src/entity/account.rs

@ -7,11 +7,11 @@ use libpso::character::guildcard;
pub const USERFLAG_NEWCHAR: u32 = 0x00000001;
pub const USERFLAG_DRESSINGROOM: u32 = 0x00000002;
#[derive(Copy, Clone, Debug, Hash, PartialEq, Eq)]
#[derive(Copy, Clone, Debug, Hash, PartialEq, Eq, PartialOrd, Ord)]
pub struct UserAccountId(pub u32);
#[derive(Copy, Clone, Debug, Hash, PartialEq, Eq)]
#[derive(Copy, Clone, Debug, Hash, PartialEq, Eq, PartialOrd, Ord)]
pub struct UserSettingsId(pub u32);
#[derive(Copy, Clone, Debug, Hash, PartialEq, Eq)]
#[derive(Copy, Clone, Debug, Hash, PartialEq, Eq, PartialOrd, Ord)]
pub struct GuildCardDataId(pub u32);

2
src/entity/character.rs

@ -226,7 +226,7 @@ impl CharacterTechMenu {
}
#[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)]
#[derive(Debug, Copy, Clone, Hash, PartialEq, Eq, PartialOrd, Ord)]
pub struct CharacterEntityId(pub u32);
#[derive(Clone)]

18
src/entity/gateway/inmemory.rs

@ -1,4 +1,4 @@
use std::collections::HashMap;
use std::collections::BTreeMap;
use crate::entity::account::*;
use crate::entity::character::*;
@ -9,19 +9,19 @@ use std::sync::{Arc, Mutex};
#[derive(Clone)]
pub struct InMemoryGateway {
users: Arc<Mutex<HashMap<UserAccountId, UserAccountEntity>>>,
user_settings: Arc<Mutex<HashMap<UserSettingsId, UserSettingsEntity>>>,
characters: Arc<Mutex<HashMap<CharacterEntityId, CharacterEntity>>>,
items: Arc<Mutex<HashMap<ItemEntityId, ItemEntity>>>,
users: Arc<Mutex<BTreeMap<UserAccountId, UserAccountEntity>>>,
user_settings: Arc<Mutex<BTreeMap<UserSettingsId, UserSettingsEntity>>>,
characters: Arc<Mutex<BTreeMap<CharacterEntityId, CharacterEntity>>>,
items: Arc<Mutex<BTreeMap<ItemEntityId, ItemEntity>>>,
}
impl InMemoryGateway {
pub fn new() -> InMemoryGateway {
InMemoryGateway {
users: Arc::new(Mutex::new(HashMap::new())),
user_settings: Arc::new(Mutex::new(HashMap::new())),
characters: Arc::new(Mutex::new(HashMap::new())),
items: Arc::new(Mutex::new(HashMap::new())),
users: Arc::new(Mutex::new(BTreeMap::new())),
user_settings: Arc::new(Mutex::new(BTreeMap::new())),
characters: Arc::new(Mutex::new(BTreeMap::new())),
items: Arc::new(Mutex::new(BTreeMap::new())),
}
}
}

2
src/entity/item/mod.rs

@ -11,7 +11,7 @@ use crate::entity::character::CharacterEntityId;
use crate::ship::map::MapArea;
use crate::ship::drops::ItemDropType;
#[derive(PartialEq, Copy, Clone, Debug, Hash, Eq)]
#[derive(PartialEq, Copy, Clone, Debug, Hash, Eq, PartialOrd, Ord)]
pub struct ItemEntityId(pub u32);
#[derive(Hash, PartialEq, Eq, Debug, Clone)]
pub struct ItemId(u32);

2
src/entity/item/tool.rs

@ -218,7 +218,7 @@ impl ToolType {
}
}
pub fn max_stack(&self) -> u8 {
pub fn max_stack(&self) -> usize {
match self {
ToolType::Monomate => 10,
ToolType::Dimate => 10,

13
src/lib.rs

@ -0,0 +1,13 @@
#![allow(incomplete_features)]
#![feature(const_generics)]
#![feature(maybe_uninit_extra)]
#![feature(const_in_array_repeat_expressions)]
#![feature(drain_filter)]
pub mod common;
pub mod entity;
pub mod patch;
pub mod login;
pub mod ship;

105
src/ship/items.rs

@ -21,16 +21,16 @@ struct RoomItemId(RoomId, u32);
#[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)]
pub struct ClientItemId(pub u32);
#[derive(Debug, Clone)]
enum ActiveItemEntityId {
#[derive(Debug, Clone, PartialEq)]
pub enum ActiveItemEntityId {
Individual(ItemEntityId),
Stacked(Vec<ItemEntityId>),
Meseta(Meseta),
}
#[derive(Debug, Clone)]
enum HeldItemType {
#[derive(Debug, Clone, PartialEq)]
pub enum HeldItemType {
Individual(ItemDetail),
Stacked(Tool, usize),
}
@ -56,7 +56,7 @@ impl HeldItemType {
}
}
#[derive(Debug, Clone)]
#[derive(Debug, Clone, PartialEq)]
pub enum FloorItemType {
Individual(ItemDetail),
Stacked(Tool, usize),
@ -89,10 +89,10 @@ impl FloorItemType {
#[derive(Debug, Clone)]
pub struct InventoryItem {
entity_id: ActiveItemEntityId,
item_id: ClientItemId,
item: HeldItemType,
equipped: bool,
pub entity_id: ActiveItemEntityId,
pub item_id: ClientItemId,
pub item: HeldItemType,
pub equipped: bool,
}
#[derive(Debug, Clone)]
@ -113,6 +113,7 @@ pub struct BankItem {
}
#[derive(Debug)]
pub struct CharacterInventory<'a>(&'a Vec<InventoryItem>);
impl<'a> CharacterInventory<'a> {
@ -132,6 +133,10 @@ impl<'a> CharacterInventory<'a> {
})
}
pub fn slot(&self, slot: usize) -> Option<&'a InventoryItem> {
self.0.get(slot)
}
pub fn count(&self) -> usize {
self.0.len()
}
@ -285,6 +290,35 @@ impl ItemManager {
let room_id = self.character_room.get(&character.id).ok_or(ItemManagerError::NoCharacter(character.id))?;
let shared_floor = self.room_floor.get_mut(&room_id).ok_or(ItemManagerError::NoCharacter(character.id))?;
match &floor_item.item {
FloorItemType::Individual(_item) => {
if inventory.len() >= 30 {
return Err(ItemManagerError::CouldNotAddToInventory(floor_item));
}
},
FloorItemType::Stacked(floor_tooltype, floor_amount) => {
let tool_overflow = inventory.iter()
.find(|item| {
if let HeldItemType::Stacked(inv_tooltype, inv_amount) = item.item {
if floor_tooltype.tool == inv_tooltype.tool {
if floor_tooltype.tool.max_stack() < (inv_amount + floor_amount) {
return true
}
}
}
false
});
if tool_overflow.is_some() {
return Err(ItemManagerError::CouldNotAddToInventory(floor_item));
}
},
FloorItemType::Meseta(_meseta) => {
if character.meseta == 999999 {
return Err(ItemManagerError::CouldNotAddToInventory(floor_item));
}
},
}
if let Some(_) = local_floor.iter().find(|i| i.item_id == floor_item.item_id) {
local_floor.retain(|item| {
item.item_id != floor_item.item_id
@ -299,10 +333,6 @@ impl ItemManager {
return Err(ItemManagerError::NoSuchItemId(floor_item.item_id))
}
if inventory.len() >= 30 {
return Err(ItemManagerError::CouldNotAddToInventory(floor_item));
}
match floor_item.item {
FloorItemType::Individual(item) => {
let inventory_item = InventoryItem {
@ -325,13 +355,45 @@ impl ItemManager {
inventory.push(inventory_item);
} // else something went very wrong TODO: log it
},
FloorItemType::Stacked(tool, usize) => {
let inventory_item = InventoryItem {
entity_id: floor_item.entity_id,
item_id: floor_item.item_id,
item: HeldItemType::Stacked(tool, usize),
equipped: false,
};
FloorItemType::Stacked(tool, amount) => {
let inventory_item = inventory.iter_mut()
.filter(|i| {
if let HeldItemType::Stacked(tooltype, _amount) = i.item {
tooltype == tool
}
else {
false
}
})
.next()
.map(|existing_inv_item| {
if let (ActiveItemEntityId::Stacked(ref mut inv_item_id),
ActiveItemEntityId::Stacked(floor_item_id))
= (&mut existing_inv_item.entity_id, &floor_item.entity_id)
{
inv_item_id.append(&mut floor_item_id.clone());
}
if let (HeldItemType::Stacked(_inv_tooltype, ref mut inv_amount),
FloorItemType::Stacked(_floor_tooltype, floor_amount))
= (&mut existing_inv_item.item, &floor_item.item)
{
// TODO: check tools are eq?
*inv_amount += floor_amount
}
existing_inv_item.clone()
})
.unwrap_or_else(|| {
let picked_up_item = InventoryItem {
entity_id: floor_item.entity_id,
item_id: floor_item.item_id,
item: HeldItemType::Stacked(tool, amount),
equipped: false,
};
inventory.push(picked_up_item.clone());
picked_up_item
});
if let ActiveItemEntityId::Stacked(item_ids) = &inventory_item.entity_id {
for item_id in item_ids {
@ -345,11 +407,10 @@ impl ItemManager {
},
}); // TODO: error check
};
inventory.push(inventory_item);
} // else something went very wrong TODO: log it
},
FloorItemType::Meseta(meseta) => {
character.meseta += meseta.0;
character.meseta = std::cmp::min(character.meseta + meseta.0, 999999);
entity_gateway.save_character(&character);
}
}

2
src/ship/packet/handler/direct_message.rs

@ -190,4 +190,4 @@ EG: EntityGateway
.collect::<Vec<_>>(); // TODO: can EntityGateway be Sync?
Ok(Box::new(item_drop_packets.into_iter()))
}
}

36
src/ship/ship.rs

@ -238,12 +238,12 @@ impl ClientState {
pub struct ShipServerState<EG: EntityGateway> {
entity_gateway: EG,
clients: Clients,
pub clients: Clients,
client_location: ClientLocation,
level_table: CharacterLevelTable,
name: String,
rooms: Rooms,
item_manager: items::ItemManager,
pub item_manager: items::ItemManager,
quests: quests::QuestList,
}
@ -261,52 +261,52 @@ impl<EG: EntityGateway> ShipServerState<EG> {
}
}
fn message(&mut self, id: ClientId, msg: &Message) -> Box<dyn Iterator<Item = (ClientId, SendShipPacket)> + Send> {
fn message(&mut self, id: ClientId, msg: &Message) -> Result<Box<dyn Iterator<Item = (ClientId, SendShipPacket)> + Send>, ShipError> {
match &msg.msg {
GameMessage::RequestExp(request_exp) => {
handler::message::request_exp(id, request_exp, &self.client_location, &self.rooms)
Ok(handler::message::request_exp(id, request_exp, &self.client_location, &self.rooms))
},
GameMessage::PlayerDropItem(player_drop_item) => {
handler::message::player_drop_item(id, player_drop_item, &mut self.entity_gateway, &mut self.client_location, &mut self.clients, &mut self.rooms, &mut self.item_manager).unwrap()
handler::message::player_drop_item(id, player_drop_item, &mut self.entity_gateway, &mut self.client_location, &mut self.clients, &mut self.rooms, &mut self.item_manager)
},
GameMessage::DropCoordinates(drop_coordinates) => {
handler::message::drop_coordinates(id, drop_coordinates, &self.client_location, &mut self.clients, &self.rooms).unwrap()
handler::message::drop_coordinates(id, drop_coordinates, &self.client_location, &mut self.clients, &self.rooms)
},
GameMessage::PlayerSplitItemStack(split_item_stack) => {
handler::message::split_item_stack(id, split_item_stack, &mut self.entity_gateway, &mut self.client_location, &mut self.clients, &mut self.item_manager).unwrap()
handler::message::split_item_stack(id, split_item_stack, &mut self.entity_gateway, &mut self.client_location, &mut self.clients, &mut self.item_manager)
},
_ => {
let cmsg = msg.clone();
Box::new(self.client_location.get_client_neighbors(id).unwrap().into_iter()
Ok(Box::new(self.client_location.get_client_neighbors(id).unwrap().into_iter()
.map(move |client| {
(client.client, SendShipPacket::Message(cmsg.clone()))
}))
})))
},
}
}
fn direct_message(&mut self, id: ClientId, msg: &DirectMessage) -> Box<dyn Iterator<Item = (ClientId, SendShipPacket)> + Send> {
fn direct_message(&mut self, id: ClientId, msg: &DirectMessage) -> Result<Box<dyn Iterator<Item = (ClientId, SendShipPacket)> + Send>, ShipError> {
let target = msg.flag;
match &msg.msg {
GameMessage::GuildcardSend(guildcard_send) => {
handler::direct_message::guildcard_send(id, guildcard_send, target, &self.client_location, &self.clients)
Ok(handler::direct_message::guildcard_send(id, guildcard_send, target, &self.client_location, &self.clients))
},
GameMessage::RequestItem(request_item) => {
handler::direct_message::request_item(id, request_item, &mut self.entity_gateway, &mut self.client_location, &mut self.clients, &mut self.rooms, &mut self.item_manager).unwrap()
handler::direct_message::request_item(id, request_item, &mut self.entity_gateway, &mut self.client_location, &mut self.clients, &mut self.rooms, &mut self.item_manager)
},
GameMessage::PickupItem(pickup_item) => {
handler::direct_message::pickup_item(id, pickup_item, &mut self.entity_gateway, &mut self.client_location, &mut self.clients, &mut self.item_manager).unwrap()
handler::direct_message::pickup_item(id, pickup_item, &mut self.entity_gateway, &mut self.client_location, &mut self.clients, &mut self.item_manager)
},
GameMessage::BoxDropRequest(box_drop_request) => {
handler::direct_message::request_box_item(id, box_drop_request, &mut self.entity_gateway, &mut self.client_location, &mut self.clients, &mut self.rooms, &mut self.item_manager).unwrap() // TODO: unwrap
handler::direct_message::request_box_item(id, box_drop_request, &mut self.entity_gateway, &mut self.client_location, &mut self.clients, &mut self.rooms, &mut self.item_manager)
}
_ => {
let cmsg = msg.clone();
Box::new(self.client_location.get_all_clients_by_client(id).unwrap().into_iter()
Ok(Box::new(self.client_location.get_all_clients_by_client(id).unwrap().into_iter()
.filter(move |client| client.local_client.id() == target as u8)
.map(move |client| {
(client.client, SendShipPacket::DirectMessage(cmsg.clone()))
}))
})))
},
}
}
@ -375,10 +375,10 @@ impl<EG: EntityGateway> ServerState for ShipServerState<EG> {
Box::new(handler::lobby::send_player_to_lobby(id, chardata, &mut self.client_location, &self.clients, &self.item_manager, &self.level_table)?.into_iter())
},
RecvShipPacket::Message(msg) => {
self.message(id, msg)
self.message(id, msg)?
},
RecvShipPacket::DirectMessage(msg) => {
self.direct_message(id, msg)
self.direct_message(id, msg)?
},
RecvShipPacket::PlayerChat(msg) => {
Box::new(handler::communication::player_chat(id, msg, &self.client_location, &self.clients)?.into_iter())

683
tests/test_item_pickup.rs

@ -0,0 +1,683 @@
use std::time::SystemTime;
use elseware::common::serverstate::{ClientId, ServerState};
use elseware::entity::gateway::{EntityGateway, InMemoryGateway};
use elseware::entity::account::{UserAccountEntity, NewUserAccountEntity, NewUserSettingsEntity};
use elseware::entity::character::{CharacterEntity, NewCharacterEntity};
//use elseware::entity::item::{NewItemEntity, ItemDetail, ItemLocation};
use elseware::entity::item;
use elseware::ship::ship::{ShipServerState, RecvShipPacket};
use elseware::ship::items::{ClientItemId, ActiveItemEntityId, HeldItemType, FloorItemType};
use libpso::packet::ship::*;
use libpso::packet::messages::*;
use libpso::packet::login::{Login, Session};
use libpso::{utf8_to_array, utf8_to_utf16_array};
pub fn new_user_character<EG: EntityGateway>(entity_gateway: &mut EG, username: &str, password: &str) -> (UserAccountEntity, CharacterEntity) {
let new_user = NewUserAccountEntity {
username: username.into(),
password: bcrypt::hash(password, 5).unwrap(),
guildcard: 1,
team_id: None,
banned: false,
muted_until: SystemTime::now(),
created_at: SystemTime::now(),
flags: 0,
};
let user = entity_gateway.create_user(new_user).unwrap();
let new_settings = NewUserSettingsEntity::new(user.id);
let _settings = entity_gateway.create_user_settings(new_settings).unwrap();
let new_character = NewCharacterEntity::new(user.id);
let character = entity_gateway.create_character(new_character).unwrap();
(user, character)
}
pub fn log_in_char<EG: EntityGateway>(ship: &mut ShipServerState<EG>, id: ClientId, username: &str, password: &str) {
let username = username.to_string();
let password = password.to_string();
ship.handle(id, &RecvShipPacket::Login(Login {
tag: 0,
guildcard: 0,
version: 0,
unknown1: [0; 6],
team: 0,
username: utf8_to_array!(username, 16),
unknown2: [0; 32],
password: utf8_to_array!(password, 16),
unknown3: [0; 40],
hwinfo: [0; 8],
session: Session::new(),
})).unwrap().for_each(drop);
}
pub fn join_lobby<EG: EntityGateway>(ship: &mut ShipServerState<EG>, id: ClientId) {
ship.handle(id, &RecvShipPacket::CharData(CharData {
_unknown: [0; 0x828]
})).unwrap().for_each(drop);
}
pub fn create_room<EG: EntityGateway>(ship: &mut ShipServerState<EG>, id: ClientId, name: &str, password: &str) {
ship.handle(id, &RecvShipPacket::CreateRoom(CreateRoom {
unknown: [0; 2],
name: utf8_to_utf16_array!(name, 16),
password: utf8_to_utf16_array!(password, 16),
difficulty: 0,
battle: 0,
challenge: 0,
episode: 1,
single_player: 0,
padding: [0; 3],
})).unwrap().for_each(drop);
ship.handle(id, &RecvShipPacket::DoneBursting(DoneBursting {})).unwrap().for_each(drop);
}
pub fn join_room<EG: EntityGateway>(ship: &mut ShipServerState<EG>, id: ClientId, room_id: u32) {
ship.handle(id, &RecvShipPacket::MenuSelect(MenuSelect {
menu: ROOM_MENU_ID,
item: room_id,
})).unwrap().for_each(drop);
}
#[test]
fn test_pick_up_item_stack_of_items_already_in_inventory() {
let mut entity_gateway = InMemoryGateway::new();
let (_user1, char1) = new_user_character(&mut entity_gateway, "a1", "a");
let (_user2, char2) = new_user_character(&mut entity_gateway, "a2", "a");
entity_gateway.create_item(
item::NewItemEntity {
item: item::ItemDetail::Tool(
item::tool::Tool {
tool: item::tool::ToolType::Monomate
}
),
location: item::ItemLocation::Inventory {
character_id: char1.id,
slot: 0,
equipped: false,
}
});
for (slot, tool) in vec![item::tool::ToolType::Monomate, item::tool::ToolType::Monofluid].into_iter().enumerate() {
for _ in 0..5 {
entity_gateway.create_item(
item::NewItemEntity {
item: item::ItemDetail::Tool(
item::tool::Tool {
tool: tool
}
),
location: item::ItemLocation::Inventory {
character_id: char2.id,
slot: slot,
equipped: false,
}
});
}
}
let mut ship = ShipServerState::new(entity_gateway.clone());
log_in_char(&mut ship, ClientId(1), "a1", "a");
log_in_char(&mut ship, ClientId(2), "a2", "a");
join_lobby(&mut ship, ClientId(1));
join_lobby(&mut ship, ClientId(2));
create_room(&mut ship, ClientId(1), "room", "");
join_room(&mut ship, ClientId(2), 0);
ship.handle(ClientId(2), &RecvShipPacket::Message(Message::new(GameMessage::PlayerDropItem(PlayerDropItem {
client: 0,
target: 0,
unknown1: 0,
area: 0,
item_id: 0x210000,
x: 0.0,
y: 0.0,
z: 0.0,
})))).unwrap().for_each(drop);
ship.handle(ClientId(1), &RecvShipPacket::DirectMessage(DirectMessage::new(0, GameMessage::PickupItem(PickupItem {
client: 0,
target: 0,
item_id: 0x210000,
area: 0,
unknown: [0; 3]
})))).unwrap().for_each(drop);
let p1_inventory = ship.item_manager.get_character_inventory(&char1).unwrap();
assert!(p1_inventory.count() == 1);
let inventory_item = p1_inventory.slot(0).unwrap();
assert!(inventory_item.entity_id == ActiveItemEntityId::Stacked(vec![item::ItemEntityId(1), item::ItemEntityId(2), item::ItemEntityId(3), item::ItemEntityId(4), item::ItemEntityId(5), item::ItemEntityId(6)]));
assert!(inventory_item.item == HeldItemType::Stacked(item::tool::Tool {tool: item::tool::ToolType::Monomate}, 6));
}
#[test]
fn test_pick_up_item_stack_of_items_not_already_held() {
let mut entity_gateway = InMemoryGateway::new();
let (_user1, char1) = new_user_character(&mut entity_gateway, "a1", "a");
let (_user2, char2) = new_user_character(&mut entity_gateway, "a2", "a");
entity_gateway.create_item(
item::NewItemEntity {
item: item::ItemDetail::Tool(
item::tool::Tool {
tool: item::tool::ToolType::Monomate
}
),
location: item::ItemLocation::Inventory {
character_id: char2.id,
slot: 0,
equipped: false,
}
});
let mut ship = ShipServerState::new(entity_gateway.clone());
log_in_char(&mut ship, ClientId(1), "a1", "a");
log_in_char(&mut ship, ClientId(2), "a2", "a");
join_lobby(&mut ship, ClientId(1));
join_lobby(&mut ship, ClientId(2));
create_room(&mut ship, ClientId(1), "room", "");
join_room(&mut ship, ClientId(2), 0);
ship.handle(ClientId(2), &RecvShipPacket::Message(Message::new(GameMessage::PlayerDropItem(PlayerDropItem {
client: 0,
target: 0,
unknown1: 0,
area: 0,
item_id: 0x210000,
x: 0.0,
y: 0.0,
z: 0.0,
})))).unwrap().for_each(drop);
ship.handle(ClientId(1), &RecvShipPacket::DirectMessage(DirectMessage::new(0, GameMessage::PickupItem(PickupItem {
client: 0,
target: 0,
item_id: 0x210000,
area: 0,
unknown: [0; 3]
})))).unwrap().for_each(drop);
let p1_inventory = ship.item_manager.get_character_inventory(&char1).unwrap();
assert!(p1_inventory.count() == 1);
let inventory_item = p1_inventory.slot(0).unwrap();
assert!(inventory_item.entity_id == ActiveItemEntityId::Stacked(vec![item::ItemEntityId(1)]));
assert!(inventory_item.item == HeldItemType::Stacked(item::tool::Tool {tool: item::tool::ToolType::Monomate}, 1));
}
#[test]
fn test_pick_up_meseta_when_inventory_full() {
let mut entity_gateway = InMemoryGateway::new();
let (_user1, char1) = new_user_character(&mut entity_gateway, "a1", "a");
let (_user2, mut char2) = new_user_character(&mut entity_gateway, "a2", "a");
for slot in 0..30 {
entity_gateway.create_item(
item::NewItemEntity {
item: item::ItemDetail::Weapon(
item::weapon::Weapon {
weapon: item::weapon::WeaponType::Saber,
grind: 0,
special: None,
attrs: [None, None, None],
tekked: true,
}
),
location: item::ItemLocation::Inventory {
character_id: char1.id,
slot: slot,
equipped: false,
}
});
}
char2.meseta = 300;
entity_gateway.save_character(&char2);
let mut ship = ShipServerState::new(entity_gateway.clone());
log_in_char(&mut ship, ClientId(1), "a1", "a");
log_in_char(&mut ship, ClientId(2), "a2", "a");
join_lobby(&mut ship, ClientId(1));
join_lobby(&mut ship, ClientId(2));
create_room(&mut ship, ClientId(1), "room", "");
join_room(&mut ship, ClientId(2), 0);
ship.handle(ClientId(2), &RecvShipPacket::Message(Message::new(GameMessage::DropCoordinates(DropCoordinates {
client: 0,
target: 0,
item_id: 0xFFFFFFFF,
map_area: 0,
x: 0.0,
z: 0.0,
})))).unwrap().for_each(drop);
ship.handle(ClientId(2), &RecvShipPacket::Message(Message::new(GameMessage::PlayerSplitItemStack(PlayerSplitItemStack {
client: 0,
target: 0,
item_id: 0xFFFFFFFF,
amount: 23,
})))).unwrap().for_each(drop);
ship.handle(ClientId(1), &RecvShipPacket::DirectMessage(DirectMessage::new(0, GameMessage::PickupItem(PickupItem {
client: 0,
target: 0,
item_id: 0xF0000001,
area: 0,
unknown: [0; 3]
})))).unwrap().for_each(drop);
let p1_inventory = ship.item_manager.get_character_inventory(&char1).unwrap();
assert!(p1_inventory.count() == 30);
let c1 = ship.clients.get(&ClientId(1)).unwrap();
let c2 = ship.clients.get(&ClientId(2)).unwrap();
assert!(c1.character.meseta == 23);
assert!(c2.character.meseta == 277);
}
#[test]
fn test_pick_up_partial_stacked_item_when_inventory_is_otherwise_full() {
let mut entity_gateway = InMemoryGateway::new();
let (_user1, char1) = new_user_character(&mut entity_gateway, "a1", "a");
let (_user2, char2) = new_user_character(&mut entity_gateway, "a2", "a");
for slot in 0..29 {
entity_gateway.create_item(
item::NewItemEntity {
item: item::ItemDetail::Weapon(
item::weapon::Weapon {
weapon: item::weapon::WeaponType::Saber,
grind: 0,
special: None,
attrs: [None, None, None],
tekked: true,
}
),
location: item::ItemLocation::Inventory {
character_id: char1.id,
slot: slot,
equipped: false,
}
});
}
entity_gateway.create_item(
item::NewItemEntity {
item: item::ItemDetail::Tool(
item::tool::Tool {
tool: item::tool::ToolType::Monomate,
}
),
location: item::ItemLocation::Inventory {
character_id: char1.id,
slot: 29,
equipped: false,
}
});
entity_gateway.create_item(
item::NewItemEntity {
item: item::ItemDetail::Tool(
item::tool::Tool {
tool: item::tool::ToolType::Monomate,
}
),
location: item::ItemLocation::Inventory {
character_id: char2.id,
slot: 0,
equipped: false,
}
});
let mut ship = ShipServerState::new(entity_gateway.clone());
log_in_char(&mut ship, ClientId(1), "a1", "a");
log_in_char(&mut ship, ClientId(2), "a2", "a");
join_lobby(&mut ship, ClientId(1));
join_lobby(&mut ship, ClientId(2));
create_room(&mut ship, ClientId(1), "room", "");
join_room(&mut ship, ClientId(2), 0);
ship.handle(ClientId(2), &RecvShipPacket::Message(Message::new(GameMessage::PlayerDropItem(PlayerDropItem {
client: 0,
target: 0,
unknown1: 0,
area: 0,
item_id: 0x210000,
x: 0.0,
y: 0.0,
z: 0.0,
})))).unwrap().for_each(drop);
ship.handle(ClientId(1), &RecvShipPacket::DirectMessage(DirectMessage::new(0, GameMessage::PickupItem(PickupItem {
client: 0,
target: 0,
item_id: 0x210000,
area: 0,
unknown: [0; 3]
})))).unwrap().for_each(drop);
let p1_inventory = ship.item_manager.get_character_inventory(&char1).unwrap();
assert!(p1_inventory.count() == 30);
let monomates = p1_inventory.slot(29).unwrap();
assert!(monomates.item == HeldItemType::Stacked(item::tool::Tool {tool: item::tool::ToolType::Monomate}, 2));
}
#[test]
fn test_can_not_pick_up_item_when_inventory_full() {
let mut entity_gateway = InMemoryGateway::new();
let (_user1, char1) = new_user_character(&mut entity_gateway, "a1", "a");
let (_user2, char2) = new_user_character(&mut entity_gateway, "a2", "a");
for slot in 0..30 {
entity_gateway.create_item(
item::NewItemEntity {
item: item::ItemDetail::Weapon(
item::weapon::Weapon {
weapon: item::weapon::WeaponType::Saber,
grind: 0,
special: None,
attrs: [None, None, None],
tekked: true,
}
),
location: item::ItemLocation::Inventory {
character_id: char1.id,
slot: slot,
equipped: false,
}
});
}
entity_gateway.create_item(
item::NewItemEntity {
item: item::ItemDetail::Weapon(
item::weapon::Weapon {
weapon: item::weapon::WeaponType::Handgun,
grind: 0,
special: None,
attrs: [None, None, None],
tekked: true,
}
),
location: item::ItemLocation::Inventory {
character_id: char2.id,
slot: 0,
equipped: false,
}
}
);
let mut ship = ShipServerState::new(entity_gateway.clone());
log_in_char(&mut ship, ClientId(1), "a1", "a");
log_in_char(&mut ship, ClientId(2), "a2", "a");
join_lobby(&mut ship, ClientId(1));
join_lobby(&mut ship, ClientId(2));
create_room(&mut ship, ClientId(1), "room", "");
join_room(&mut ship, ClientId(2), 0);
ship.handle(ClientId(2), &RecvShipPacket::Message(Message::new(GameMessage::PlayerDropItem(PlayerDropItem {
client: 0,
target: 0,
unknown1: 0,
area: 0,
item_id: 0x210000,
x: 0.0,
y: 0.0,
z: 0.0,
})))).unwrap().for_each(drop);
ship.handle(ClientId(1), &RecvShipPacket::DirectMessage(DirectMessage::new(0, GameMessage::PickupItem(PickupItem {
client: 0,
target: 0,
item_id: 0x210000,
area: 0,
unknown: [0; 3]
})))).unwrap().for_each(drop);
let p1_inventory = ship.item_manager.get_character_inventory(&char1).unwrap();
assert!(p1_inventory.count() == 30);
let floor_item = ship.item_manager.get_floor_item_by_id(&char1, ClientItemId(0x210000)).unwrap();
assert!(floor_item.item_id == ClientItemId(0x210000));
}
#[test]
fn test_can_not_drop_more_meseta_than_is_held() {
let mut entity_gateway = InMemoryGateway::new();
let (_user1, mut char1) = new_user_character(&mut entity_gateway, "a1", "a");
char1.meseta = 300;
entity_gateway.save_character(&char1);
let mut ship = ShipServerState::new(entity_gateway.clone());
log_in_char(&mut ship, ClientId(1), "a1", "a");
join_lobby(&mut ship, ClientId(1));
create_room(&mut ship, ClientId(1), "room", "");
ship.handle(ClientId(1), &RecvShipPacket::Message(Message::new(GameMessage::DropCoordinates(DropCoordinates {
client: 0,
target: 0,
item_id: 0xFFFFFFFF,
map_area: 0,
x: 0.0,
z: 0.0,
})))).unwrap().for_each(drop);
let split_attempt = ship.handle(ClientId(1), &RecvShipPacket::Message(Message::new(GameMessage::PlayerSplitItemStack(PlayerSplitItemStack {
client: 0,
target: 0,
item_id: 0xFFFFFFFF,
amount: 301,
}))));
assert!(split_attempt.is_err());
let c1 = ship.clients.get(&ClientId(1)).unwrap();
assert!(c1.character.meseta == 300);
assert!(ship.item_manager.get_floor_item_by_id(&char1, ClientItemId(0xF0000001)).is_err())
}
#[test]
fn test_pick_up_stack_that_would_exceed_stack_limit() {
let mut entity_gateway = InMemoryGateway::new();
let (_user1, char1) = new_user_character(&mut entity_gateway, "a1", "a");
let (_user2, char2) = new_user_character(&mut entity_gateway, "a2", "a");
for _ in 0..6 {
entity_gateway.create_item(
item::NewItemEntity {
item: item::ItemDetail::Tool(
item::tool::Tool {
tool: item::tool::ToolType::Monomate,
}
),
location: item::ItemLocation::Inventory {
character_id: char1.id,
slot: 0,
equipped: false,
}
});
}
for _ in 0..6 {
entity_gateway.create_item(
item::NewItemEntity {
item: item::ItemDetail::Tool(
item::tool::Tool {
tool: item::tool::ToolType::Monomate,
}
),
location: item::ItemLocation::Inventory {
character_id: char2.id,
slot: 0,
equipped: false,
}
});
}
let mut ship = ShipServerState::new(entity_gateway.clone());
log_in_char(&mut ship, ClientId(1), "a1", "a");
log_in_char(&mut ship, ClientId(2), "a2", "a");
join_lobby(&mut ship, ClientId(1));
join_lobby(&mut ship, ClientId(2));
create_room(&mut ship, ClientId(1), "room", "");
join_room(&mut ship, ClientId(2), 0);
ship.handle(ClientId(2), &RecvShipPacket::Message(Message::new(GameMessage::PlayerDropItem(PlayerDropItem {
client: 0,
target: 0,
unknown1: 0,
area: 0,
item_id: 0x210000,
x: 0.0,
y: 0.0,
z: 0.0,
})))).unwrap().for_each(drop);
ship.handle(ClientId(1), &RecvShipPacket::DirectMessage(DirectMessage::new(0, GameMessage::PickupItem(PickupItem {
client: 0,
target: 0,
item_id: 0x210000,
area: 0,
unknown: [0; 3]
})))).unwrap().for_each(drop);
let p1_inventory = ship.item_manager.get_character_inventory(&char1).unwrap();
let monomates = p1_inventory.slot(0).unwrap();
assert!(monomates.item == HeldItemType::Stacked(item::tool::Tool {tool: item::tool::ToolType::Monomate}, 6));
let floor_monomates = ship.item_manager.get_floor_item_by_id(&char1, ClientItemId(0x210000)).unwrap();
assert!(floor_monomates.item == FloorItemType::Stacked(item::tool::Tool {tool: item::tool::ToolType::Monomate}, 6));
}
#[test]
fn test_can_not_pick_up_meseta_when_full() {
let mut entity_gateway = InMemoryGateway::new();
let (_user1, mut char1) = new_user_character(&mut entity_gateway, "a1", "a");
let (_user2, mut char2) = new_user_character(&mut entity_gateway, "a2", "a");
char1.meseta = 999999;
entity_gateway.save_character(&char1);
char2.meseta = 300;
entity_gateway.save_character(&char2);
let mut ship = ShipServerState::new(entity_gateway.clone());
log_in_char(&mut ship, ClientId(1), "a1", "a");
log_in_char(&mut ship, ClientId(2), "a2", "a");
join_lobby(&mut ship, ClientId(1));
join_lobby(&mut ship, ClientId(2));
create_room(&mut ship, ClientId(1), "room", "");
join_room(&mut ship, ClientId(2), 0);
ship.handle(ClientId(2), &RecvShipPacket::Message(Message::new(GameMessage::DropCoordinates(DropCoordinates {
client: 0,
target: 0,
item_id: 0xFFFFFFFF,
map_area: 0,
x: 0.0,
z: 0.0,
})))).unwrap().for_each(drop);
ship.handle(ClientId(2), &RecvShipPacket::Message(Message::new(GameMessage::PlayerSplitItemStack(PlayerSplitItemStack {
client: 0,
target: 0,
item_id: 0xFFFFFFFF,
amount: 23,
})))).unwrap().for_each(drop);
ship.handle(ClientId(1), &RecvShipPacket::DirectMessage(DirectMessage::new(0, GameMessage::PickupItem(PickupItem {
client: 0,
target: 0,
item_id: 0xF0000001,
area: 0,
unknown: [0; 3]
})))).unwrap().for_each(drop);
let c1 = ship.clients.get(&ClientId(1)).unwrap();
let c2 = ship.clients.get(&ClientId(2)).unwrap();
assert!(c1.character.meseta == 999999);
assert!(c2.character.meseta == 277);
let floor_meseta = ship.item_manager.get_floor_item_by_id(&char1, ClientItemId(0xF0000001)).unwrap();
assert!(floor_meseta.item == FloorItemType::Meseta(item::Meseta(23)));
}
#[test]
fn test_meseta_caps_at_999999_when_trying_to_pick_up_more() {
let mut entity_gateway = InMemoryGateway::new();
let (_user1, mut char1) = new_user_character(&mut entity_gateway, "a1", "a");
let (_user2, mut char2) = new_user_character(&mut entity_gateway, "a2", "a");
char1.meseta = 999998;
entity_gateway.save_character(&char1);
char2.meseta = 300;
entity_gateway.save_character(&char2);
let mut ship = ShipServerState::new(entity_gateway.clone());
log_in_char(&mut ship, ClientId(1), "a1", "a");
log_in_char(&mut ship, ClientId(2), "a2", "a");
join_lobby(&mut ship, ClientId(1));
join_lobby(&mut ship, ClientId(2));
create_room(&mut ship, ClientId(1), "room", "");
join_room(&mut ship, ClientId(2), 0);
ship.handle(ClientId(2), &RecvShipPacket::Message(Message::new(GameMessage::DropCoordinates(DropCoordinates {
client: 0,
target: 0,
item_id: 0xFFFFFFFF,
map_area: 0,
x: 0.0,
z: 0.0,
})))).unwrap().for_each(drop);
ship.handle(ClientId(2), &RecvShipPacket::Message(Message::new(GameMessage::PlayerSplitItemStack(PlayerSplitItemStack {
client: 0,
target: 0,
item_id: 0xFFFFFFFF,
amount: 23,
})))).unwrap().for_each(drop);
ship.handle(ClientId(1), &RecvShipPacket::DirectMessage(DirectMessage::new(0, GameMessage::PickupItem(PickupItem {
client: 0,
target: 0,
item_id: 0xF0000001,
area: 0,
unknown: [0; 3]
})))).unwrap().for_each(drop);
let c1 = ship.clients.get(&ClientId(1)).unwrap();
let c2 = ship.clients.get(&ClientId(2)).unwrap();
assert!(c1.character.meseta == 999999);
assert!(c2.character.meseta == 277);
let floor_meseta = ship.item_manager.get_floor_item_by_id(&char1, ClientItemId(0xF0000001));
assert!(floor_meseta.is_err());
}
Loading…
Cancel
Save