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