Merge pull request 'properly pick up stacks and a ton of tests' (#160) from properly_pick_up_stacks into master
This commit is contained in:
commit
ed51aa379c
13
Cargo.toml
13
Cargo.toml
@ -4,19 +4,6 @@ version = "0.1.0"
|
|||||||
authors = ["Jake Probst <jake.probst@gmail.com>"]
|
authors = ["Jake Probst <jake.probst@gmail.com>"]
|
||||||
edition = "2018"
|
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]
|
[dependencies]
|
||||||
libpso = { git = "http://git.sharnoth.com/jake/libpso" }
|
libpso = { git = "http://git.sharnoth.com/jake/libpso" }
|
||||||
async-std = { version = "1.5.0", features = ["unstable"] }
|
async-std = { version = "1.5.0", features = ["unstable"] }
|
||||||
|
@ -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 std::time::SystemTime;
|
||||||
use log::{info};
|
use log::{info};
|
||||||
|
|
||||||
use patch::patch::{PatchServerState, generate_patch_tree, load_config, load_motd};
|
use elseware::patch::patch::{PatchServerState, generate_patch_tree, load_config, load_motd};
|
||||||
use login::login::LoginServerState;
|
use elseware::login::login::LoginServerState;
|
||||||
use login::character::CharacterServerState;
|
use elseware::login::character::CharacterServerState;
|
||||||
use ship::ship::ShipServerState;
|
use elseware::ship::ship::ShipServerState;
|
||||||
use entity::account::{NewUserAccountEntity, NewUserSettingsEntity};
|
use elseware::entity::account::{NewUserAccountEntity, NewUserSettingsEntity};
|
||||||
use entity::gateway::{EntityGateway, InMemoryGateway};
|
use elseware::entity::gateway::{EntityGateway, InMemoryGateway};
|
||||||
use entity::character::NewCharacterEntity;
|
use elseware::entity::character::NewCharacterEntity;
|
||||||
use entity::item::{NewItemEntity, ItemDetail, ItemLocation};
|
use elseware::entity::item::{NewItemEntity, ItemDetail, ItemLocation};
|
||||||
|
|
||||||
use crate::entity::item;
|
use elseware::entity::item;
|
||||||
|
|
||||||
fn setup_logger() {
|
fn setup_logger() {
|
||||||
let colors = fern::colors::ColoredLevelConfig::new()
|
let colors = fern::colors::ColoredLevelConfig::new()
|
||||||
@ -48,7 +34,7 @@ fn setup_logger() {
|
|||||||
.chain(std::io::stdout());
|
.chain(std::io::stdout());
|
||||||
let fileout = fern::Dispatch::new()
|
let fileout = fern::Dispatch::new()
|
||||||
.level(log::LevelFilter::Trace)
|
.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()
|
fern::Dispatch::new()
|
||||||
.chain(stdio)
|
.chain(stdio)
|
||||||
.chain(fileout)
|
.chain(fileout)
|
||||||
@ -86,11 +72,11 @@ fn main() {
|
|||||||
NewItemEntity {
|
NewItemEntity {
|
||||||
item: ItemDetail::Weapon(
|
item: ItemDetail::Weapon(
|
||||||
item::weapon::Weapon {
|
item::weapon::Weapon {
|
||||||
weapon: item::weapon::WeaponType::Handgun,
|
weapon: item::weapon::WeaponType::Raygun,
|
||||||
grind: 5,
|
grind: 5,
|
||||||
special: Some(item::weapon::WeaponSpecial::Hell),
|
special: Some(item::weapon::WeaponSpecial::Hell),
|
||||||
attrs: [Some(item::weapon::WeaponAttribute{attr: item::weapon::Attribute::Hit, value: 100}),
|
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,],
|
None,],
|
||||||
tekked: true,
|
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_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_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();
|
let thread_entity_gateway = entity_gateway.clone();
|
||||||
@ -119,7 +105,7 @@ fn main() {
|
|||||||
info!("[auth] starting server");
|
info!("[auth] starting server");
|
||||||
let auth_state = LoginServerState::new(thread_entity_gateway);
|
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();
|
let thread_entity_gateway = entity_gateway.clone();
|
||||||
@ -127,14 +113,14 @@ fn main() {
|
|||||||
info!("[character] starting server");
|
info!("[character] starting server");
|
||||||
let char_state = CharacterServerState::new(thread_entity_gateway);
|
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 thread_entity_gateway = entity_gateway.clone();
|
||||||
let ship = async_std::task::spawn(async {
|
let ship = async_std::task::spawn(async {
|
||||||
info!("[ship] starting server");
|
info!("[ship] starting server");
|
||||||
let ship_state = ShipServerState::new(thread_entity_gateway);
|
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);
|
futures::join!(patch, auth, character, ship);
|
@ -7,11 +7,11 @@ use libpso::character::guildcard;
|
|||||||
pub const USERFLAG_NEWCHAR: u32 = 0x00000001;
|
pub const USERFLAG_NEWCHAR: u32 = 0x00000001;
|
||||||
pub const USERFLAG_DRESSINGROOM: u32 = 0x00000002;
|
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);
|
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);
|
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);
|
pub struct GuildCardDataId(pub u32);
|
||||||
|
|
||||||
|
|
||||||
|
@ -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);
|
pub struct CharacterEntityId(pub u32);
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
use std::collections::HashMap;
|
use std::collections::BTreeMap;
|
||||||
|
|
||||||
use crate::entity::account::*;
|
use crate::entity::account::*;
|
||||||
use crate::entity::character::*;
|
use crate::entity::character::*;
|
||||||
@ -9,19 +9,19 @@ use std::sync::{Arc, Mutex};
|
|||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct InMemoryGateway {
|
pub struct InMemoryGateway {
|
||||||
users: Arc<Mutex<HashMap<UserAccountId, UserAccountEntity>>>,
|
users: Arc<Mutex<BTreeMap<UserAccountId, UserAccountEntity>>>,
|
||||||
user_settings: Arc<Mutex<HashMap<UserSettingsId, UserSettingsEntity>>>,
|
user_settings: Arc<Mutex<BTreeMap<UserSettingsId, UserSettingsEntity>>>,
|
||||||
characters: Arc<Mutex<HashMap<CharacterEntityId, CharacterEntity>>>,
|
characters: Arc<Mutex<BTreeMap<CharacterEntityId, CharacterEntity>>>,
|
||||||
items: Arc<Mutex<HashMap<ItemEntityId, ItemEntity>>>,
|
items: Arc<Mutex<BTreeMap<ItemEntityId, ItemEntity>>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl InMemoryGateway {
|
impl InMemoryGateway {
|
||||||
pub fn new() -> InMemoryGateway {
|
pub fn new() -> InMemoryGateway {
|
||||||
InMemoryGateway {
|
InMemoryGateway {
|
||||||
users: Arc::new(Mutex::new(HashMap::new())),
|
users: Arc::new(Mutex::new(BTreeMap::new())),
|
||||||
user_settings: Arc::new(Mutex::new(HashMap::new())),
|
user_settings: Arc::new(Mutex::new(BTreeMap::new())),
|
||||||
characters: Arc::new(Mutex::new(HashMap::new())),
|
characters: Arc::new(Mutex::new(BTreeMap::new())),
|
||||||
items: Arc::new(Mutex::new(HashMap::new())),
|
items: Arc::new(Mutex::new(BTreeMap::new())),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -11,7 +11,7 @@ use crate::entity::character::CharacterEntityId;
|
|||||||
use crate::ship::map::MapArea;
|
use crate::ship::map::MapArea;
|
||||||
use crate::ship::drops::ItemDropType;
|
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);
|
pub struct ItemEntityId(pub u32);
|
||||||
#[derive(Hash, PartialEq, Eq, Debug, Clone)]
|
#[derive(Hash, PartialEq, Eq, Debug, Clone)]
|
||||||
pub struct ItemId(u32);
|
pub struct ItemId(u32);
|
||||||
|
@ -218,7 +218,7 @@ impl ToolType {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn max_stack(&self) -> u8 {
|
pub fn max_stack(&self) -> usize {
|
||||||
match self {
|
match self {
|
||||||
ToolType::Monomate => 10,
|
ToolType::Monomate => 10,
|
||||||
ToolType::Dimate => 10,
|
ToolType::Dimate => 10,
|
||||||
|
13
src/lib.rs
Normal file
13
src/lib.rs
Normal file
@ -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;
|
@ -21,16 +21,16 @@ struct RoomItemId(RoomId, u32);
|
|||||||
#[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)]
|
#[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)]
|
||||||
pub struct ClientItemId(pub u32);
|
pub struct ClientItemId(pub u32);
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone, PartialEq)]
|
||||||
enum ActiveItemEntityId {
|
pub enum ActiveItemEntityId {
|
||||||
Individual(ItemEntityId),
|
Individual(ItemEntityId),
|
||||||
Stacked(Vec<ItemEntityId>),
|
Stacked(Vec<ItemEntityId>),
|
||||||
Meseta(Meseta),
|
Meseta(Meseta),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone, PartialEq)]
|
||||||
enum HeldItemType {
|
pub enum HeldItemType {
|
||||||
Individual(ItemDetail),
|
Individual(ItemDetail),
|
||||||
Stacked(Tool, usize),
|
Stacked(Tool, usize),
|
||||||
}
|
}
|
||||||
@ -56,7 +56,7 @@ impl HeldItemType {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone, PartialEq)]
|
||||||
pub enum FloorItemType {
|
pub enum FloorItemType {
|
||||||
Individual(ItemDetail),
|
Individual(ItemDetail),
|
||||||
Stacked(Tool, usize),
|
Stacked(Tool, usize),
|
||||||
@ -89,10 +89,10 @@ impl FloorItemType {
|
|||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct InventoryItem {
|
pub struct InventoryItem {
|
||||||
entity_id: ActiveItemEntityId,
|
pub entity_id: ActiveItemEntityId,
|
||||||
item_id: ClientItemId,
|
pub item_id: ClientItemId,
|
||||||
item: HeldItemType,
|
pub item: HeldItemType,
|
||||||
equipped: bool,
|
pub equipped: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
@ -113,6 +113,7 @@ pub struct BankItem {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
pub struct CharacterInventory<'a>(&'a Vec<InventoryItem>);
|
pub struct CharacterInventory<'a>(&'a Vec<InventoryItem>);
|
||||||
|
|
||||||
impl<'a> CharacterInventory<'a> {
|
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 {
|
pub fn count(&self) -> usize {
|
||||||
self.0.len()
|
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 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))?;
|
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) {
|
if let Some(_) = local_floor.iter().find(|i| i.item_id == floor_item.item_id) {
|
||||||
local_floor.retain(|item| {
|
local_floor.retain(|item| {
|
||||||
item.item_id != floor_item.item_id
|
item.item_id != floor_item.item_id
|
||||||
@ -299,10 +333,6 @@ impl ItemManager {
|
|||||||
return Err(ItemManagerError::NoSuchItemId(floor_item.item_id))
|
return Err(ItemManagerError::NoSuchItemId(floor_item.item_id))
|
||||||
}
|
}
|
||||||
|
|
||||||
if inventory.len() >= 30 {
|
|
||||||
return Err(ItemManagerError::CouldNotAddToInventory(floor_item));
|
|
||||||
}
|
|
||||||
|
|
||||||
match floor_item.item {
|
match floor_item.item {
|
||||||
FloorItemType::Individual(item) => {
|
FloorItemType::Individual(item) => {
|
||||||
let inventory_item = InventoryItem {
|
let inventory_item = InventoryItem {
|
||||||
@ -325,13 +355,45 @@ impl ItemManager {
|
|||||||
inventory.push(inventory_item);
|
inventory.push(inventory_item);
|
||||||
} // else something went very wrong TODO: log it
|
} // else something went very wrong TODO: log it
|
||||||
},
|
},
|
||||||
FloorItemType::Stacked(tool, usize) => {
|
FloorItemType::Stacked(tool, amount) => {
|
||||||
let inventory_item = InventoryItem {
|
let inventory_item = inventory.iter_mut()
|
||||||
entity_id: floor_item.entity_id,
|
.filter(|i| {
|
||||||
item_id: floor_item.item_id,
|
if let HeldItemType::Stacked(tooltype, _amount) = i.item {
|
||||||
item: HeldItemType::Stacked(tool, usize),
|
tooltype == tool
|
||||||
equipped: false,
|
}
|
||||||
};
|
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 {
|
if let ActiveItemEntityId::Stacked(item_ids) = &inventory_item.entity_id {
|
||||||
for item_id in item_ids {
|
for item_id in item_ids {
|
||||||
@ -345,11 +407,10 @@ impl ItemManager {
|
|||||||
},
|
},
|
||||||
}); // TODO: error check
|
}); // TODO: error check
|
||||||
};
|
};
|
||||||
inventory.push(inventory_item);
|
|
||||||
} // else something went very wrong TODO: log it
|
} // else something went very wrong TODO: log it
|
||||||
},
|
},
|
||||||
FloorItemType::Meseta(meseta) => {
|
FloorItemType::Meseta(meseta) => {
|
||||||
character.meseta += meseta.0;
|
character.meseta = std::cmp::min(character.meseta + meseta.0, 999999);
|
||||||
entity_gateway.save_character(&character);
|
entity_gateway.save_character(&character);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -238,12 +238,12 @@ impl ClientState {
|
|||||||
|
|
||||||
pub struct ShipServerState<EG: EntityGateway> {
|
pub struct ShipServerState<EG: EntityGateway> {
|
||||||
entity_gateway: EG,
|
entity_gateway: EG,
|
||||||
clients: Clients,
|
pub clients: Clients,
|
||||||
client_location: ClientLocation,
|
client_location: ClientLocation,
|
||||||
level_table: CharacterLevelTable,
|
level_table: CharacterLevelTable,
|
||||||
name: String,
|
name: String,
|
||||||
rooms: Rooms,
|
rooms: Rooms,
|
||||||
item_manager: items::ItemManager,
|
pub item_manager: items::ItemManager,
|
||||||
quests: quests::QuestList,
|
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 {
|
match &msg.msg {
|
||||||
GameMessage::RequestExp(request_exp) => {
|
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) => {
|
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) => {
|
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) => {
|
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();
|
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| {
|
.map(move |client| {
|
||||||
(client.client, SendShipPacket::Message(cmsg.clone()))
|
(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;
|
let target = msg.flag;
|
||||||
match &msg.msg {
|
match &msg.msg {
|
||||||
GameMessage::GuildcardSend(guildcard_send) => {
|
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) => {
|
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) => {
|
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) => {
|
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();
|
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)
|
.filter(move |client| client.local_client.id() == target as u8)
|
||||||
.map(move |client| {
|
.map(move |client| {
|
||||||
(client.client, SendShipPacket::DirectMessage(cmsg.clone()))
|
(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())
|
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) => {
|
RecvShipPacket::Message(msg) => {
|
||||||
self.message(id, msg)
|
self.message(id, msg)?
|
||||||
},
|
},
|
||||||
RecvShipPacket::DirectMessage(msg) => {
|
RecvShipPacket::DirectMessage(msg) => {
|
||||||
self.direct_message(id, msg)
|
self.direct_message(id, msg)?
|
||||||
},
|
},
|
||||||
RecvShipPacket::PlayerChat(msg) => {
|
RecvShipPacket::PlayerChat(msg) => {
|
||||||
Box::new(handler::communication::player_chat(id, msg, &self.client_location, &self.clients)?.into_iter())
|
Box::new(handler::communication::player_chat(id, msg, &self.client_location, &self.clients)?.into_iter())
|
||||||
|
683
tests/test_item_pickup.rs
Normal file
683
tests/test_item_pickup.rs
Normal file
@ -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…
x
Reference in New Issue
Block a user