Compare commits

...

5 Commits

Author SHA1 Message Date
13c6592438 you can use techs now wow
Some checks failed
continuous-integration/drone/push Build is failing
2023-01-29 23:05:42 -07:00
7bd385d580 anyhowing continues 2023-01-29 22:15:49 -07:00
f09c73611f fix some user setting entity stuff 2023-01-29 22:15:29 -07:00
d495ec97f2 fix this keyconfig nonsense 2023-01-29 20:41:12 -07:00
31ebc1af32 actually store meseta bank interaction 2023-01-29 17:06:48 -07:00
18 changed files with 189 additions and 184 deletions

View File

@ -64,12 +64,12 @@ fn main() {
};
let fake_user = entity_gateway.create_user(fake_user).await.unwrap();
entity_gateway.create_user_settings(NewUserSettingsEntity::new(fake_user.id)).await.unwrap();
let mut character = NewCharacterEntity::new(fake_user.id, 1);
let mut character = NewCharacterEntity::new(fake_user.id);
character.name = format!("Test Char {}", i*2);
let character = entity_gateway.create_character(character).await.unwrap();
entity_gateway.set_character_meseta(&character.id, item::Meseta(999999)).await.unwrap();
entity_gateway.set_bank_meseta(&character.id, &item::BankName("".into()), item::Meseta(999999)).await.unwrap();
let mut character = NewCharacterEntity::new(fake_user.id, 1);
let mut character = NewCharacterEntity::new(fake_user.id);
character.slot = 2;
character.name = "ItemRefactor".into();
character.exp = 80000000;

View File

@ -157,7 +157,7 @@ pub struct CharacterAppearance {
}
#[derive(Clone, Debug)]
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct TechLevel(pub u8);
#[derive(Clone, Debug, Default)]
@ -167,16 +167,14 @@ pub struct CharacterTechniques {
impl CharacterTechniques {
pub fn set_tech(&mut self, tech: Technique, level: TechLevel) {
self.techs.insert(tech, TechLevel(level.0 - 1));
self.techs.insert(tech, TechLevel(level.0));
}
// from_bytes
pub fn as_bytes(&self) -> [u8; 20] {
self.techs.iter()
.fold([0xFF; 20], |mut techlist, (tech, level)| {
let index = tech.as_value();
techlist[index as usize] = level.0;
techlist[index as usize] = level.0 - 1;
techlist
})
}
@ -264,82 +262,6 @@ pub struct CharacterMaterials {
pub tp: u32,
}
#[derive(Clone, Debug)]
pub struct CharacterKeyboardConfig {
pub keyboard_config: [u8; 0x16C],
}
impl Default for CharacterKeyboardConfig {
fn default() -> CharacterKeyboardConfig {
CharacterKeyboardConfig {
keyboard_config: DEFAULT_KEYBOARD_CONFIG1,
}
}
}
impl CharacterKeyboardConfig {
fn new(preset: usize) -> CharacterKeyboardConfig {
match preset {
1 => {
CharacterKeyboardConfig {
keyboard_config: DEFAULT_KEYBOARD_CONFIG1,
}
},
2 => {
CharacterKeyboardConfig {
keyboard_config: DEFAULT_KEYBOARD_CONFIG2,
}
},
3 => {
CharacterKeyboardConfig {
keyboard_config: DEFAULT_KEYBOARD_CONFIG3,
}
},
4 => {
CharacterKeyboardConfig {
keyboard_config: DEFAULT_KEYBOARD_CONFIG4,
}
},
_ => {
CharacterKeyboardConfig {
keyboard_config: DEFAULT_KEYBOARD_CONFIG1,
}
},
}
}
pub fn update(&mut self, new_config: &KeyboardConfig) {
self.keyboard_config = new_config.keyboard_config;
}
pub fn as_bytes(&self) -> [u8; 0x16C] {
self.keyboard_config
}
}
#[derive(Clone, Debug)]
pub struct CharacterGamepadConfig {
pub gamepad_config: [u8; 0x38],
}
impl Default for CharacterGamepadConfig {
fn default() -> CharacterGamepadConfig {
CharacterGamepadConfig {
gamepad_config: DEFAULT_GAMEPAD_CONFIG,
}
}
}
impl CharacterGamepadConfig {
pub fn update(&mut self, new_config: &GamepadConfig) {
self.gamepad_config = new_config.gamepad_config;
}
pub fn as_bytes(&self) -> [u8; 0x38] {
self.gamepad_config
}
}
#[derive(Debug, Copy, Clone, Hash, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize, Default, derive_more::Display)]
pub struct CharacterEntityId(pub u32);
@ -363,12 +285,10 @@ pub struct NewCharacterEntity {
pub tech_menu: CharacterTechMenu,
pub option_flags: u32,
pub keyboard_config: CharacterKeyboardConfig,
pub gamepad_config: CharacterGamepadConfig,
}
impl NewCharacterEntity {
pub fn new(user: UserAccountId, keyboard_config_preset: usize,) -> NewCharacterEntity {
pub fn new(user: UserAccountId) -> NewCharacterEntity {
NewCharacterEntity {
user_id: user,
slot: 0,
@ -384,8 +304,6 @@ impl NewCharacterEntity {
materials: CharacterMaterials::default(),
tech_menu: CharacterTechMenu::default(),
option_flags: 0,
keyboard_config: CharacterKeyboardConfig::new(keyboard_config_preset),
gamepad_config: CharacterGamepadConfig::default(),
}
}
}
@ -411,8 +329,6 @@ pub struct CharacterEntity {
pub tech_menu: CharacterTechMenu,
pub option_flags: u32,
pub keyboard_config: CharacterKeyboardConfig,
pub gamepad_config: CharacterGamepadConfig,
pub playtime: u32,
}

View File

@ -72,6 +72,10 @@ impl EntityGateway for InMemoryGatewayTransaction {
}
}
async fn save_user_settings(&mut self, settings: &UserSettingsEntity) -> Result<(), GatewayError> {
self.original_gateway.save_user_settings(settings).await
}
async fn save_character(&mut self, char: &CharacterEntity) -> Result<(), GatewayError> {
copy_if_needed(&mut *self.working_gateway.characters.lock().await,
&*self.original_gateway.characters.lock().await,
@ -418,6 +422,12 @@ impl EntityGateway for InMemoryGateway {
.ok_or(GatewayError::Error)
}
async fn save_user_settings(&mut self, settings: &UserSettingsEntity) -> Result<(), GatewayError> {
let mut user_settings = self.user_settings.lock().await;
user_settings.insert(settings.id, settings.clone());
Ok(())
}
async fn get_characters_by_user(&mut self, user: &UserAccountEntity) -> Result<[Option<CharacterEntity>; 4], GatewayError> {
let characters = self.characters.lock().await;
const NONE: Option<CharacterEntity> = None;
@ -452,8 +462,6 @@ impl EntityGateway for InMemoryGateway {
materials: character.materials,
tech_menu: character.tech_menu,
option_flags: character.option_flags,
keyboard_config: character.keyboard_config,
gamepad_config: character.gamepad_config,
playtime: 0,
};
characters.insert(new_character.id, new_character.clone());

View File

@ -0,0 +1,3 @@
alter table player_character
drop column keyboard_config,
drop column gamepad_config;

View File

@ -217,8 +217,6 @@ pub struct PgCharacter {
tp: i16,
tech_menu: Vec<u8>,
keyboard_config: Vec<u8>,
gamepad_config: Vec<u8>,
playtime: i32,
}
@ -270,12 +268,6 @@ impl From<PgCharacter> for CharacterEntity {
tech_menu: CharacterTechMenu {
tech_menu: vec_to_array(other.tech_menu)
},
keyboard_config: CharacterKeyboardConfig {
keyboard_config: vec_to_array(other.keyboard_config)
},
gamepad_config: CharacterGamepadConfig {
gamepad_config: vec_to_array(other.gamepad_config)
},
playtime: other.playtime as u32,
}
}

View File

@ -217,7 +217,7 @@ async fn save_user_settings(conn: &mut sqlx::PgConnection, settings: &UserSettin
.bind(&settings.settings.symbol_chats.to_vec())
.bind(settings.settings.team_name.iter().copied().flat_map(|i| i.to_le_bytes().to_vec()).collect::<Vec<u8>>())
.bind(settings.id.0)
.fetch_one(conn).await?;
.execute(conn).await?;
Ok(())
}
@ -229,16 +229,14 @@ async fn create_character(conn: &mut sqlx::PgConnection, char: NewCharacterEntit
hair, hair_r, hair_g, hair_b, prop_x,
prop_y, techs, config, infoboard, guildcard,
power, mind, def, evade, luck,
hp, tp, tech_menu, option_flags, keyboard_config,
gamepad_config, playtime)
hp, tp, tech_menu, option_flags, playtime)
values
($1, $2, $3, $4, $5,
$6, $7, $8, $9, $10,
$11, $12, $13, $14, $15,
$16, $17, $18, $19, $20,
$21, $22, $23, $24, $25,
$26, $27, $28, $29, $30,
$31, $32)
$26, $27, $28, $29, $30)
returning *;"#;
let character = sqlx::query_as::<_, PgCharacter>(q)
.bind(char.user_id.0)
@ -270,8 +268,6 @@ async fn create_character(conn: &mut sqlx::PgConnection, char: NewCharacterEntit
.bind(char.materials.tp as i16)
.bind(char.tech_menu.tech_menu.to_vec())
.bind(char.option_flags as i32)
.bind(&char.keyboard_config.keyboard_config.to_vec())
.bind(&char.gamepad_config.gamepad_config.to_vec())
.bind(0)
.fetch_one(conn).await?;
@ -298,8 +294,8 @@ async fn save_character(conn: &mut sqlx::PgConnection, char: &CharacterEntity) -
let q = r#"update player_character set
user_account=$1, slot=$2, name=$3, exp=$4, class=$5, section_id=$6, costume=$7, skin=$8, face=$9, head=$10, hair=$11, hair_r=$12,
hair_g=$13, hair_b=$14, prop_x=$15, prop_y=$16, techs=$17, config=$18, infoboard=$19, guildcard=$20, power=$21, mind=$22, def=$23,
evade=$24, luck=$25, hp=$26, tp=$27, tech_menu=$28, option_flags=$29, keyboard_config=$30, gamepad_config=$31, playtime=$32
where id=$33;"#;
evade=$24, luck=$25, hp=$26, tp=$27, tech_menu=$28, option_flags=$29, playtime=$30
where id=$31;"#;
sqlx::query(q)
.bind(char.user_id.0) // $1
.bind(char.slot as i16) // $2
@ -330,10 +326,8 @@ async fn save_character(conn: &mut sqlx::PgConnection, char: &CharacterEntity) -
.bind(char.materials.tp as i16) // $27
.bind(char.tech_menu.tech_menu.to_vec()) // $28
.bind(char.option_flags as i32) // $29
.bind(&char.keyboard_config.keyboard_config.to_vec()) // $30
.bind(&char.gamepad_config.gamepad_config.to_vec()) // $31
.bind(char.playtime as i32) // $32
.bind(char.id.0 as i32) // $33
.bind(char.playtime as i32) // $30
.bind(char.id.0 as i32) // $31
.execute(conn).await?;
Ok(())
}

View File

@ -747,7 +747,7 @@ impl<EG: EntityGateway + Clone> InterserverActor for CharacterServerState<EG> {
fn new_character_from_preview(user: &UserAccountEntity, preview: &CharacterPreview) -> NewCharacterEntity {
let mut character = NewCharacterEntity::new(user.id, 1); // it should not be possible for the client to specify the kbm config preset from the char create screen
let mut character = NewCharacterEntity::new(user.id);
character.slot = preview.slot;
character.name = String::from_utf16_lossy(&preview.character.name).trim_matches(char::from(0)).into();
character.section_id = preview.character.section_id.into();

View File

@ -113,7 +113,7 @@ where
move |(mut item_state, mut transaction), _| {
Box::pin(async move {
let mut inventory = item_state.inventory(&character_id).await?;
let item = inventory.take_item(&item_id, amount).ok_or_else(|| ItemStateError::NoFloorItem(item_id))?;
let item = inventory.take_item(&item_id, amount).ok_or_else(|| ItemStateError::NoInventoryItem(item_id))?;
transaction.gateway().set_character_inventory(&character_id, &inventory.as_inventory_entity(&character_id)).await?;
item_state.set_inventory(inventory).await;
@ -245,6 +245,7 @@ where
let mut bank = item_state.bank(&character_id).await?;
bank.remove_meseta(amount)?;
transaction.gateway().set_bank_meseta(&character_id, &bank.name, bank.meseta).await?;
item_state.set_bank(bank).await;
Ok(((item_state, transaction), ()))
})
@ -286,6 +287,7 @@ where
let mut bank = item_state.bank(&character_id).await?;
bank.add_meseta(amount)?;
transaction.gateway().set_bank_meseta(&character_id, &bank.name, bank.meseta).await?;
item_state.set_bank(bank).await;
Ok(((item_state, transaction), ()))
})

View File

@ -1,12 +1,14 @@
use std::convert::TryInto;
use futures::future::join_all;
use thiserror::Error;
use anyhow::Context;
use rand::SeedableRng;
use rand::distributions::{WeightedIndex, Distribution};
use crate::entity::gateway::{EntityGateway, GatewayError};
use crate::entity::character::CharacterEntity;
use crate::entity::character::{CharacterEntity, TechLevel};
use crate::entity::item::mag::{MagCell, MagCellError};
use crate::entity::item::tool::{Tool, ToolType};
use crate::entity::item::tech::{TechniqueDisk, Technique};
use crate::entity::item::{ItemDetail, ItemEntityId};
use crate::ship::items::state::{ItemStateProxy, ItemStateError};
use crate::ship::items::inventory::{InventoryItem, InventoryItemDetail};
@ -18,7 +20,7 @@ pub enum ApplyItemError {
NoCharacter,
#[error("item not equipped")]
ItemNotEquipped,
#[error("invalid item")]
#[error("could not use item invalid item")]
InvalidItem,
#[error("gateway error {0}")]
GatewayError(#[from] GatewayError),
@ -307,6 +309,21 @@ where
}
async fn apply_tech<'a, EG>(item_state: &mut ItemStateProxy,
entity_gateway: &mut EG,
character: &mut CharacterEntity,
_entity_id: ItemEntityId,
tech: TechniqueDisk)
-> Result<Vec<ApplyItemAction>, anyhow::Error>
where
EG: EntityGateway + ?Sized,
{
// TODO: make sure the class can learn that specific tech
character.techs.set_tech(tech.tech, TechLevel(tech.level as u8));
entity_gateway.save_character(character).await.unwrap();
Ok(vec![ApplyItemAction::UpdateCharacter(Box::new(character.clone()))])
}
pub async fn apply_item<'a, EG>(item_state: &mut ItemStateProxy,
entity_gateway: &mut EG,
@ -320,7 +337,11 @@ where
InventoryItemDetail::Individual(individual_item) => {
match individual_item.item {
ItemDetail::Tool(tool) => apply_tool(item_state, entity_gateway, character, individual_item.entity_id, tool.tool).await,
_ => Err(ApplyItemError::InvalidItem.into())
ItemDetail::TechniqueDisk(tech) => apply_tech(item_state, entity_gateway, character, individual_item.entity_id, tech).await,
_ => Err(anyhow::Error::from(ApplyItemError::InvalidItem))
.with_context(|| {
format!("item {:?}", individual_item)
})
}
},
InventoryItemDetail::Stacked(stacked_item) => {

View File

@ -64,10 +64,10 @@ pub struct BankItem {
}
impl BankItem {
pub async fn with_entity_id<F, Fut, T>(&self, mut param: T, mut func: F) -> Result<T, ItemStateError>
pub async fn with_entity_id<F, Fut, T>(&self, mut param: T, mut func: F) -> Result<T, anyhow::Error>
where
F: FnMut(T, ItemEntityId) -> Fut,
Fut: Future<Output=Result<T, ItemStateError>>,
Fut: Future<Output=Result<T, anyhow::Error>>,
{
match &self.item {
BankItemDetail::Individual(individual_item) => {
@ -125,27 +125,27 @@ impl BankState {
self.item_id_counter = base_item_id + self.bank.0.len() as u32;
}
pub fn add_meseta(&mut self, amount: u32) -> Result<(), ItemStateError> {
pub fn add_meseta(&mut self, amount: u32) -> Result<(), anyhow::Error> {
if self.meseta.0 + amount > 999999 {
return Err(ItemStateError::FullOfMeseta)
return Err(ItemStateError::FullOfMeseta.into())
}
self.meseta.0 += amount;
Ok(())
}
pub fn remove_meseta(&mut self, amount: u32) -> Result<(), ItemStateError> {
pub fn remove_meseta(&mut self, amount: u32) -> Result<(), anyhow::Error> {
if amount > self.meseta.0 {
return Err(ItemStateError::InvalidMesetaRemoval(amount))
return Err(ItemStateError::InvalidMesetaRemoval(amount).into())
}
self.meseta.0 -= amount;
Ok(())
}
pub fn add_inventory_item(&mut self, item: InventoryItem) -> Result<AddItemResult, BankError> {
pub fn add_inventory_item(&mut self, item: InventoryItem) -> Result<AddItemResult, anyhow::Error> {
match item.item {
InventoryItemDetail::Individual(iitem) => {
if self.bank.0.len() >= 30 {
Err(BankError::BankFull)
Err(BankError::BankFull.into())
}
else {
self.bank.0.push(BankItem {
@ -166,7 +166,7 @@ impl BankState {
match existing_stack {
Some(existing_stack) => {
if existing_stack.entity_ids.len() + sitem.entity_ids.len() > sitem.tool.max_stack() {
Err(BankError::StackFull)
Err(BankError::StackFull.into())
}
else {
existing_stack.entity_ids.append(&mut sitem.entity_ids.clone());
@ -175,7 +175,7 @@ impl BankState {
},
None => {
if self.bank.0.len() >= 30 {
Err(BankError::BankFull)
Err(BankError::BankFull.into())
}
else {
self.bank.0.push(BankItem {

View File

@ -33,7 +33,7 @@ pub struct FloorItem {
}
impl FloorItem {
pub async fn with_entity_id<F, Fut, T>(&self, mut param: T, mut func: F) -> Result<T, ItemStateError>
pub async fn with_entity_id<F, Fut, T>(&self, mut param: T, mut func: F) -> Result<T, anyhow::Error>
where
F: FnMut(T, ItemEntityId) -> Fut,
Fut: Future<Output=Result<T, ItemStateError>>,
@ -53,10 +53,10 @@ impl FloorItem {
Ok(param)
}
pub async fn with_mag<F, Fut, T>(&self, mut param: T, mut func: F) -> Result<T, ItemStateError>
pub async fn with_mag<F, Fut, T>(&self, mut param: T, mut func: F) -> Result<T, anyhow::Error>
where
F: FnMut(T, ItemEntityId, Mag) -> Fut,
Fut: Future<Output=Result<T, ItemStateError>>,
Fut: Future<Output=Result<T, anyhow::Error>>,
{
if let FloorItemDetail::Individual(individual_item) = &self.item {
if let ItemDetail::Mag(mag) = &individual_item.item {

View File

@ -60,7 +60,7 @@ impl InventoryItemDetail {
}
// TODO: this should probably go somewhere a bit more fundamental like ItemDetail
pub fn sell_price(&self) -> Result<u32, ItemStateError> {
pub fn sell_price(&self) -> Result<u32, anyhow::Error> {
match self {
InventoryItemDetail::Individual(individual_item) => {
match &individual_item.item {
@ -102,7 +102,7 @@ impl InventoryItemDetail {
Ok((ToolShopItem::from(d).price() / 8) as u32)
},
ItemDetail::Mag(_m) => {
Err(ItemStateError::ItemNotSellable)
Err(ItemStateError::ItemNotSellable.into())
},
ItemDetail::ESWeapon(_e) => {
Ok(10u32)
@ -126,10 +126,10 @@ pub struct InventoryItem {
}
impl InventoryItem {
pub async fn with_entity_id<F, Fut, T>(&self, mut param: T, mut func: F) -> Result<T, ItemStateError>
pub async fn with_entity_id<F, Fut, T>(&self, mut param: T, mut func: F) -> Result<T, anyhow::Error>
where
F: FnMut(T, ItemEntityId) -> Fut,
Fut: Future<Output=Result<T, ItemStateError>>,
Fut: Future<Output=Result<T, anyhow::Error>>,
{
match &self.item {
InventoryItemDetail::Individual(individual_item) => {
@ -145,10 +145,10 @@ impl InventoryItem {
Ok(param)
}
pub async fn with_mag<F, Fut, T>(&self, mut param: T, mut func: F) -> Result<T, ItemStateError>
pub async fn with_mag<F, Fut, T>(&self, mut param: T, mut func: F) -> Result<T, anyhow::Error>
where
F: FnMut(T, ItemEntityId, Mag) -> Fut,
Fut: Future<Output=Result<T, ItemStateError>>,
Fut: Future<Output=Result<T, anyhow::Error>>,
{
if let InventoryItemDetail::Individual(individual_item) = &self.item {
if let ItemDetail::Mag(mag) = &individual_item.item {
@ -205,11 +205,11 @@ impl InventoryState {
self.inventory.0.len()
}
pub fn add_floor_item(&mut self, item: FloorItem) -> Result<AddItemResult, InventoryError> {
pub fn add_floor_item(&mut self, item: FloorItem) -> Result<AddItemResult, anyhow::Error> {
match item.item {
FloorItemDetail::Individual(iitem) => {
if self.inventory.0.len() >= 30 {
Err(InventoryError::InventoryFull)
Err(InventoryError::InventoryFull.into())
}
else {
self.inventory.0.push(InventoryItem {
@ -229,7 +229,7 @@ impl InventoryState {
match existing_stack {
Some(existing_stack) => {
if existing_stack.entity_ids.len() + sitem.entity_ids.len() > sitem.tool.max_stack() {
Err(InventoryError::StackFull)
Err(InventoryError::StackFull.into())
}
else {
existing_stack.entity_ids.append(&mut sitem.entity_ids.clone());
@ -238,7 +238,7 @@ impl InventoryState {
},
None => {
if self.inventory.0.len() >= 30 {
Err(InventoryError::InventoryFull)
Err(InventoryError::InventoryFull.into())
}
else {
self.inventory.0.push(InventoryItem {
@ -253,7 +253,7 @@ impl InventoryState {
},
FloorItemDetail::Meseta(meseta) => {
if self.meseta == Meseta(999999) {
Err(InventoryError::MesetaFull)
Err(InventoryError::MesetaFull.into())
}
else {
self.meseta.0 = std::cmp::min(self.meseta.0 + meseta.0, 999999);
@ -263,11 +263,11 @@ impl InventoryState {
}
}
pub fn add_item(&mut self, item: InventoryItem) -> Result<(AddItemResult, InventoryItem), InventoryError> {
pub fn add_item(&mut self, item: InventoryItem) -> Result<(AddItemResult, InventoryItem), anyhow::Error> {
match &item.item {
InventoryItemDetail::Individual(_) => {
if self.inventory.0.len() >= 30 {
Err(InventoryError::InventoryFull)
Err(InventoryError::InventoryFull.into())
}
else {
self.inventory.0.push(item);
@ -290,7 +290,7 @@ impl InventoryState {
match existing_stack {
Some(existing_stack) => {
if existing_stack.entity_ids.len() + sitem.entity_ids.len() > sitem.tool.max_stack() {
Err(InventoryError::StackFull)
Err(InventoryError::StackFull.into())
}
else {
existing_stack.entity_ids.append(&mut sitem.entity_ids.clone());
@ -307,7 +307,7 @@ impl InventoryState {
},
None => {
if self.inventory.0.len() >= 30 {
Err(InventoryError::InventoryFull)
Err(InventoryError::InventoryFull.into())
}
else {
self.inventory.0.push(item);
@ -370,25 +370,25 @@ impl InventoryState {
.find(|i| i.item_id == *item_id)
}
pub fn add_meseta(&mut self, amount: u32) -> Result<(), ItemStateError> {
pub fn add_meseta(&mut self, amount: u32) -> Result<(), anyhow::Error> {
if self.meseta.0 == 999999 {
return Err(ItemStateError::FullOfMeseta)
return Err(ItemStateError::FullOfMeseta.into())
}
self.meseta.0 = std::cmp::min(self.meseta.0 + amount, 999999);
Ok(())
}
pub fn add_meseta_no_overflow(&mut self, amount: u32) -> Result<(), ItemStateError> {
pub fn add_meseta_no_overflow(&mut self, amount: u32) -> Result<(), anyhow::Error> {
if self.meseta.0 + amount > 999999 {
return Err(ItemStateError::FullOfMeseta)
return Err(ItemStateError::FullOfMeseta.into())
}
self.meseta.0 += amount;
Ok(())
}
pub fn remove_meseta(&mut self, amount: u32) -> Result<(), ItemStateError> {
pub fn remove_meseta(&mut self, amount: u32) -> Result<(), anyhow::Error> {
if amount > self.meseta.0 {
return Err(ItemStateError::InvalidMesetaRemoval(amount))
return Err(ItemStateError::InvalidMesetaRemoval(amount).into())
}
self.meseta.0 -= amount;
Ok(())

View File

@ -22,6 +22,8 @@ pub enum ItemStateError {
NoCharacter(CharacterEntityId),
#[error("room {0} not found")]
NoRoom(RoomId),
#[error("inventory item {0} not found")]
NoInventoryItem(ClientItemId),
#[error("floor item {0} not found")]
NoFloorItem(ClientItemId),
#[error("expected {0} to be a tool")]

View File

@ -34,8 +34,8 @@ pub async fn block_selected(id: ClientId,
.meseta(inventory.meseta)
.inventory(&inventory)
.bank(&bank)
.keyboard_config(&client.character.keyboard_config.as_bytes())
.gamepad_config(&client.character.gamepad_config.as_bytes())
.keyboard_config(&client.settings.settings.keyboard_config)
.gamepad_config(&client.settings.settings.gamepad_config)
.symbol_chat(&client.settings.settings.symbol_chats)
.tech_menu(&client.character.tech_menu.as_bytes())
.option_flags(client.character.option_flags)

View File

@ -48,8 +48,8 @@ where
clients.with_mut(id, |client| {
let mut entity_gateway = entity_gateway.clone();
Box::pin(async move {
client.character.keyboard_config.update(&keyboard_config);
entity_gateway.save_character(&client.character).await
client.settings.settings.keyboard_config = keyboard_config.keyboard_config;
entity_gateway.save_user_settings(&client.settings).await
})}).await??;
Ok(Vec::new())
}
@ -65,8 +65,8 @@ where
clients.with_mut(id, |client| {
let mut entity_gateway = entity_gateway.clone();
Box::pin(async move {
client.character.gamepad_config.update(&gamepad_config);
entity_gateway.save_character(&client.character).await
client.settings.settings.gamepad_config = gamepad_config.gamepad_config;
entity_gateway.save_user_settings(&client.settings).await
})}).await??;
Ok(Vec::new())
}

View File

@ -13,6 +13,7 @@ use libpso::packet::login::{Login, Session};
use libpso::{utf8_to_array, utf8_to_utf16_array};
//TODO: remove kb_conf_preset
pub async fn new_user_character<EG: EntityGateway + Clone>(entity_gateway: &mut EG, username: &str, password: &str, kb_conf_preset: usize) -> (UserAccountEntity, CharacterEntity) {
let new_user = NewUserAccountEntity {
email: format!("{}@pso.com", username),
@ -26,7 +27,7 @@ pub async fn new_user_character<EG: EntityGateway + Clone>(entity_gateway: &mut
let user = entity_gateway.create_user(new_user).await.unwrap();
let new_settings = NewUserSettingsEntity::new(user.id);
let _settings = entity_gateway.create_user_settings(new_settings).await.unwrap();
let new_character = NewCharacterEntity::new(user.id, kb_conf_preset);
let new_character = NewCharacterEntity::new(user.id);
let character = entity_gateway.create_character(new_character).await.unwrap();
entity_gateway.set_character_meseta(&character.id, Meseta(0)).await.unwrap();
entity_gateway.set_bank_meseta(&character.id, &BankName("".into()), Meseta(0)).await.unwrap();

View File

@ -31,26 +31,6 @@ async fn test_save_options() {
assert!(char.option_flags == 12345);
}
#[async_std::test]
async fn test_default3_keyboard_mappings() {
/*
check if keyboard is set to default3 when specified. this will only occur for things like creating characters from the web page.
normal client behaviour will simply use default1 when creating a character.
gamepad only has 1 default config
*/
let mut entity_gateway = InMemoryGateway::default();
let (_user1, char1) = new_user_character(&mut entity_gateway, "a1", "a", 3).await;
assert!(char1.keyboard_config.as_bytes() == DEFAULT_KEYBOARD_CONFIG3);
}
#[async_std::test]
async fn test_invalid_keyboard_preset_value() {
// check if keyboard_config self-corrects to DEFAULT1 if an invalid value (>4) is given
let mut entity_gateway = InMemoryGateway::default();
let (_user1, char1) = new_user_character(&mut entity_gateway, "a1", "a", 10).await;
assert!(char1.keyboard_config.as_bytes() == DEFAULT_KEYBOARD_CONFIG1);
}
#[async_std::test]
async fn test_change_keyboard_mappings() {
let mut entity_gateway = InMemoryGateway::default();
@ -63,7 +43,8 @@ async fn test_change_keyboard_mappings() {
log_in_char(&mut ship, ClientId(1), "a1", "a").await;
join_lobby(&mut ship, ClientId(1)).await;
assert!(char1.keyboard_config.as_bytes() == DEFAULT_KEYBOARD_CONFIG2);
let settings = entity_gateway.get_user_settings_by_user(&user1).await.unwrap();
assert!(settings.settings.keyboard_config == DEFAULT_KEYBOARD_CONFIG1);
// update from default2 to default4
// the client simply sends the full 364 bytes...
@ -95,8 +76,6 @@ async fn test_change_keyboard_mappings() {
],
})).await.unwrap();
let characters = entity_gateway.get_characters_by_user(&user1).await.unwrap();
let char = characters[0].as_ref().unwrap();
assert!(char.keyboard_config.as_bytes() == DEFAULT_KEYBOARD_CONFIG4);
let settings = entity_gateway.get_user_settings_by_user(&user1).await.unwrap();
assert!(settings.settings.keyboard_config == DEFAULT_KEYBOARD_CONFIG4);
}

View File

@ -2,6 +2,7 @@ use elseware::common::serverstate::{ClientId, ServerState};
use elseware::entity::gateway::{EntityGateway, InMemoryGateway};
use elseware::entity::item;
use elseware::ship::ship::{ShipServerState, RecvShipPacket};
use elseware::entity::character::TechLevel;
//use elseware::ship::items::{ClientItemId, ActiveItemEntityId, HeldItemType, FloorItemType};
use libpso::packet::ship::*;
@ -304,6 +305,92 @@ async fn test_jackolantern() {
}
}
#[async_std::test]
async fn test_use_barta_1() {
let mut entity_gateway = InMemoryGateway::default();
let (user1, char1) = new_user_character(&mut entity_gateway, "a1", "a", 1).await;
/*
let mut p1_inv = Vec::new();
for tool in vec![item::tool::ToolType::PowerMaterial, item::tool::ToolType::].into_iter() {
let mut item = Vec::new();
for _ in 0..5usize {
item.push(entity_gateway.create_item(
item::NewItemEntity {
item: item::ItemDetail::Tool(
item::tool::Tool {
tool: tool
}
),
}).await.unwrap());
}
p1_inv.push(item::InventoryItemEntity::Stacked(item));
}*/
let inv = vec![
entity_gateway.create_item(
item::NewItemEntity {
item: item::ItemDetail::TechniqueDisk(
item::tech::TechniqueDisk {
tech: item::tech::Technique::Foie,
level: 3,
}
)
}
).await.unwrap(),
entity_gateway.create_item(
item::NewItemEntity {
item: item::ItemDetail::TechniqueDisk(
item::tech::TechniqueDisk {
tech: item::tech::Technique::Barta,
level: 4,
}
)
}
).await.unwrap(),
entity_gateway.create_item(
item::NewItemEntity {
item: item::ItemDetail::TechniqueDisk(
item::tech::TechniqueDisk {
tech: item::tech::Technique::Zonde,
level: 5,
}
)
}
).await.unwrap()
];
entity_gateway.set_character_inventory(&char1.id, &item::InventoryEntity::new(inv)).await.unwrap();
let mut ship = Box::new(ShipServerState::builder()
.gateway(entity_gateway.clone())
.build());
log_in_char(&mut ship, ClientId(1), "a1", "a").await;
join_lobby(&mut ship, ClientId(1)).await;
create_room(&mut ship, ClientId(1), "room", "").await;
ship.handle(ClientId(1), RecvShipPacket::Message(Message::new(GameMessage::PlayerUseItem(PlayerUseItem {
client: 0,
target: 0,
item_id: 0x10000,
})))).await.unwrap();
ship.handle(ClientId(1), RecvShipPacket::Message(Message::new(GameMessage::PlayerUseItem(PlayerUseItem {
client: 0,
target: 0,
item_id: 0x10002,
})))).await.unwrap();
let characters = entity_gateway.get_characters_by_user(&user1).await.unwrap();
let char = characters[0].as_ref().unwrap();
assert_eq!(char.techs.techs.len(), 2);
assert_eq!(char.techs.techs.get(&item::tech::Technique::Foie).unwrap(), &TechLevel(3));
assert_eq!(char.techs.techs.get(&item::tech::Technique::Zonde).unwrap(), &TechLevel(5));
assert!(char.techs.techs.get(&item::tech::Technique::Barta).is_none());
}
// TODO: tests for ALL ITEMS WOW
/*