|
@ -16,14 +16,14 @@ use crate::common::leveltable::CharacterLevelTable; |
|
|
use libpso::{utf8_to_array, utf8_to_utf16_array};
|
|
|
use libpso::{utf8_to_array, utf8_to_utf16_array};
|
|
|
|
|
|
|
|
|
use crate::entity::gateway::EntityGateway;
|
|
|
use crate::entity::gateway::EntityGateway;
|
|
|
use crate::entity::account::{UserAccount, USERFLAG_NEWCHAR, USERFLAG_DRESSINGROOM};
|
|
|
|
|
|
use crate::entity::item::{ItemDetail, ItemLocation};
|
|
|
|
|
|
|
|
|
use crate::entity::account::{UserAccountEntity, UserSettingsEntity, USERFLAG_NEWCHAR, USERFLAG_DRESSINGROOM};
|
|
|
|
|
|
use crate::entity::item::{ItemEntity, ItemDetail, ItemLocation};
|
|
|
use crate::entity::item::weapon::Weapon;
|
|
|
use crate::entity::item::weapon::Weapon;
|
|
|
use crate::entity::item::armor::Armor;
|
|
|
use crate::entity::item::armor::Armor;
|
|
|
use crate::entity::item::tech::Technique;
|
|
|
use crate::entity::item::tech::Technique;
|
|
|
use crate::entity::item::tool::Tool;
|
|
|
use crate::entity::item::tool::Tool;
|
|
|
use crate::entity::item::mag::{Mag, MagType};
|
|
|
use crate::entity::item::mag::{Mag, MagType};
|
|
|
use crate::entity::character::{Character, CharacterClass, TechLevel};
|
|
|
|
|
|
|
|
|
use crate::entity::character::{CharacterEntity, CharacterClass, TechLevel};
|
|
|
|
|
|
|
|
|
use crate::login::login::get_login_status;
|
|
|
use crate::login::login::get_login_status;
|
|
|
|
|
|
|
|
@ -142,8 +142,8 @@ fn generate_param_data(path: &str) -> (ParamDataHeader, Vec<u8>) { |
|
|
#[derive(Clone)]
|
|
|
#[derive(Clone)]
|
|
|
struct ClientState {
|
|
|
struct ClientState {
|
|
|
param_index: usize,
|
|
|
param_index: usize,
|
|
|
user: Option<UserAccount>,
|
|
|
|
|
|
characters: Option<[Option<Character>; 4]>,
|
|
|
|
|
|
|
|
|
user: Option<UserAccountEntity>,
|
|
|
|
|
|
characters: Option<[Option<CharacterEntity>; 4]>,
|
|
|
guildcard_data_buffer: Option<Vec<u8>>,
|
|
|
guildcard_data_buffer: Option<Vec<u8>>,
|
|
|
session: Session,
|
|
|
session: Session,
|
|
|
}
|
|
|
}
|
|
@ -189,24 +189,28 @@ pub struct CharacterServerState<EG: EntityGateway> { |
|
|
}
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
fn new_character<EG: EntityGateway>(entity_gateway: &mut EG, user: &UserAccount, preview: &CharacterPreview) {
|
|
|
|
|
|
let mut char = entity_gateway.new_character_by_user(&user);
|
|
|
|
|
|
new_character_from_preview(&mut char, preview);
|
|
|
|
|
|
match char.char_class {
|
|
|
|
|
|
CharacterClass::FOmar | CharacterClass::FOmarl| CharacterClass::FOnewm | CharacterClass::FOnewearl => char.techs.set_tech(Technique::Foie, TechLevel(1)),
|
|
|
|
|
|
|
|
|
fn new_character<EG: EntityGateway>(entity_gateway: &mut EG, user: &UserAccountEntity, preview: &CharacterPreview) {
|
|
|
|
|
|
//let mut character = entity_gateway.new_character_by_user(&user);
|
|
|
|
|
|
//new_character_from_preview(&mut char, preview);
|
|
|
|
|
|
let mut character = new_character_from_preview(user, preview);
|
|
|
|
|
|
match character.char_class {
|
|
|
|
|
|
CharacterClass::FOmar | CharacterClass::FOmarl| CharacterClass::FOnewm | CharacterClass::FOnewearl => character.techs.set_tech(Technique::Foie, TechLevel(1)),
|
|
|
_ => {}
|
|
|
_ => {}
|
|
|
}
|
|
|
}
|
|
|
|
|
|
|
|
|
entity_gateway.set_character(&char);
|
|
|
|
|
|
|
|
|
entity_gateway.set_character(&mut character);
|
|
|
|
|
|
|
|
|
let new_weapon = match char.char_class {
|
|
|
|
|
|
|
|
|
let new_weapon = match character.char_class {
|
|
|
CharacterClass::HUmar | CharacterClass::HUnewearl | CharacterClass::HUcast | CharacterClass::HUcaseal => item::weapon::WeaponType::Saber,
|
|
|
CharacterClass::HUmar | CharacterClass::HUnewearl | CharacterClass::HUcast | CharacterClass::HUcaseal => item::weapon::WeaponType::Saber,
|
|
|
CharacterClass::RAmar | CharacterClass::RAmarl | CharacterClass::RAcast | CharacterClass::RAcaseal => item::weapon::WeaponType::Handgun,
|
|
|
CharacterClass::RAmar | CharacterClass::RAmarl | CharacterClass::RAcast | CharacterClass::RAcaseal => item::weapon::WeaponType::Handgun,
|
|
|
CharacterClass::FOmar | CharacterClass::FOmarl | CharacterClass::FOnewm | CharacterClass::FOnewearl => item::weapon::WeaponType::Cane,
|
|
|
CharacterClass::FOmar | CharacterClass::FOmarl | CharacterClass::FOnewm | CharacterClass::FOnewearl => item::weapon::WeaponType::Cane,
|
|
|
};
|
|
|
};
|
|
|
|
|
|
|
|
|
entity_gateway.new_item(
|
|
|
|
|
|
ItemDetail::Weapon(
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
entity_gateway.set_item(
|
|
|
|
|
|
&mut ItemEntity {
|
|
|
|
|
|
id: None,
|
|
|
|
|
|
item : ItemDetail::Weapon(
|
|
|
Weapon {
|
|
|
Weapon {
|
|
|
weapon: new_weapon,
|
|
|
weapon: new_weapon,
|
|
|
grind: 0,
|
|
|
grind: 0,
|
|
@ -214,28 +218,32 @@ fn new_character<EG: EntityGateway>(entity_gateway: &mut EG, user: &UserAccount, |
|
|
attrs: [None; 3],
|
|
|
attrs: [None; 3],
|
|
|
tekked: true,
|
|
|
tekked: true,
|
|
|
}),
|
|
|
}),
|
|
|
ItemLocation::Inventory {
|
|
|
|
|
|
character_id: char.id,
|
|
|
|
|
|
|
|
|
location: ItemLocation::Inventory {
|
|
|
|
|
|
character_id: character.id.unwrap(),
|
|
|
index: 0,
|
|
|
index: 0,
|
|
|
equipped: true,
|
|
|
equipped: true,
|
|
|
});
|
|
|
|
|
|
|
|
|
}});
|
|
|
|
|
|
|
|
|
entity_gateway.new_item(
|
|
|
|
|
|
ItemDetail::Armor (
|
|
|
|
|
|
|
|
|
entity_gateway.set_item(
|
|
|
|
|
|
&mut ItemEntity {
|
|
|
|
|
|
id: None,
|
|
|
|
|
|
item: ItemDetail::Armor (
|
|
|
Armor {
|
|
|
Armor {
|
|
|
armor: item::armor::ArmorType::Frame,
|
|
|
armor: item::armor::ArmorType::Frame,
|
|
|
dfp: 0,
|
|
|
dfp: 0,
|
|
|
evp: 0,
|
|
|
evp: 0,
|
|
|
slots: 0,
|
|
|
slots: 0,
|
|
|
}),
|
|
|
}),
|
|
|
ItemLocation::Inventory {
|
|
|
|
|
|
character_id: char.id,
|
|
|
|
|
|
|
|
|
location: ItemLocation::Inventory {
|
|
|
|
|
|
character_id: character.id.unwrap(),
|
|
|
index: 1,
|
|
|
index: 1,
|
|
|
equipped: true,
|
|
|
equipped: true,
|
|
|
});
|
|
|
|
|
|
|
|
|
}});
|
|
|
|
|
|
|
|
|
entity_gateway.new_item(
|
|
|
|
|
|
ItemDetail::Mag(
|
|
|
|
|
|
|
|
|
entity_gateway.set_item(
|
|
|
|
|
|
&mut ItemEntity {
|
|
|
|
|
|
id: None,
|
|
|
|
|
|
item: ItemDetail::Mag(
|
|
|
Mag {
|
|
|
Mag {
|
|
|
mag: MagType::Mag,
|
|
|
mag: MagType::Mag,
|
|
|
def: 500,
|
|
|
def: 500,
|
|
@ -246,33 +254,37 @@ fn new_character<EG: EntityGateway>(entity_gateway: &mut EG, user: &UserAccount, |
|
|
iq: 0,
|
|
|
iq: 0,
|
|
|
photon_blast: [None; 3],
|
|
|
photon_blast: [None; 3],
|
|
|
}),
|
|
|
}),
|
|
|
ItemLocation::Inventory {
|
|
|
|
|
|
character_id: char.id,
|
|
|
|
|
|
|
|
|
location: ItemLocation::Inventory {
|
|
|
|
|
|
character_id: character.id.unwrap(),
|
|
|
index: 2,
|
|
|
index: 2,
|
|
|
equipped: true,
|
|
|
equipped: true,
|
|
|
});
|
|
|
|
|
|
|
|
|
}});
|
|
|
|
|
|
|
|
|
for _ in 0..4 {
|
|
|
for _ in 0..4 {
|
|
|
entity_gateway.new_item(
|
|
|
|
|
|
ItemDetail::Tool (
|
|
|
|
|
|
|
|
|
entity_gateway.set_item(
|
|
|
|
|
|
&mut ItemEntity {
|
|
|
|
|
|
id: None,
|
|
|
|
|
|
item: ItemDetail::Tool (
|
|
|
Tool {
|
|
|
Tool {
|
|
|
tool: item::tool::ToolType::Monomate,
|
|
|
tool: item::tool::ToolType::Monomate,
|
|
|
}),
|
|
|
}),
|
|
|
ItemLocation::Inventory {
|
|
|
|
|
|
character_id: char.id,
|
|
|
|
|
|
|
|
|
location: ItemLocation::Inventory {
|
|
|
|
|
|
character_id: character.id.unwrap(),
|
|
|
index: 3,
|
|
|
index: 3,
|
|
|
equipped: false,
|
|
|
equipped: false,
|
|
|
});
|
|
|
|
|
|
entity_gateway.new_item(
|
|
|
|
|
|
ItemDetail::Tool (
|
|
|
|
|
|
|
|
|
}});
|
|
|
|
|
|
entity_gateway.set_item(
|
|
|
|
|
|
&mut ItemEntity {
|
|
|
|
|
|
id: None,
|
|
|
|
|
|
item: ItemDetail::Tool (
|
|
|
Tool {
|
|
|
Tool {
|
|
|
tool: item::tool::ToolType::Monofluid,
|
|
|
tool: item::tool::ToolType::Monofluid,
|
|
|
}),
|
|
|
}),
|
|
|
ItemLocation::Inventory {
|
|
|
|
|
|
character_id: char.id,
|
|
|
|
|
|
|
|
|
location: ItemLocation::Inventory {
|
|
|
|
|
|
character_id: character.id.unwrap(),
|
|
|
index: 4,
|
|
|
index: 4,
|
|
|
equipped: false,
|
|
|
equipped: false,
|
|
|
});
|
|
|
|
|
|
|
|
|
}});
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
|
|
|
|
|
@ -333,7 +345,11 @@ impl<EG: EntityGateway> CharacterServerState<EG> { |
|
|
// TODO: this should error (data should be added on account creation, why did I copy this silly sylv logic?)
|
|
|
// TODO: this should error (data should be added on account creation, why did I copy this silly sylv logic?)
|
|
|
let settings = match self.entity_gateway.get_user_settings_by_user(&user) {
|
|
|
let settings = match self.entity_gateway.get_user_settings_by_user(&user) {
|
|
|
Some(settings) => settings,
|
|
|
Some(settings) => settings,
|
|
|
None => self.entity_gateway.create_user_settings_by_user(&user),
|
|
|
|
|
|
|
|
|
None => {
|
|
|
|
|
|
let mut user_settings = UserSettingsEntity::new(user.id.unwrap());
|
|
|
|
|
|
self.entity_gateway.set_user_settings(&mut user_settings);
|
|
|
|
|
|
user_settings
|
|
|
|
|
|
}
|
|
|
};
|
|
|
};
|
|
|
|
|
|
|
|
|
let pkt = SendKeyAndTeamSettings::new(settings.settings.key_config,
|
|
|
let pkt = SendKeyAndTeamSettings::new(settings.settings.key_config,
|
|
@ -421,7 +437,7 @@ impl<EG: EntityGateway> CharacterServerState<EG> { |
|
|
let client = self.clients.get_mut(&id).ok_or(CharacterError::ClientNotFound(id))?;
|
|
|
let client = self.clients.get_mut(&id).ok_or(CharacterError::ClientNotFound(id))?;
|
|
|
let mut user = client.user.as_mut().unwrap();
|
|
|
let mut user = client.user.as_mut().unwrap();
|
|
|
user.flags = setflag.flags;
|
|
|
user.flags = setflag.flags;
|
|
|
self.entity_gateway.set_user(&user);
|
|
|
|
|
|
|
|
|
self.entity_gateway.set_user(&mut user);
|
|
|
Ok(None.into_iter())
|
|
|
Ok(None.into_iter())
|
|
|
}
|
|
|
}
|
|
|
|
|
|
|
|
@ -459,7 +475,7 @@ impl<EG: EntityGateway> CharacterServerState<EG> { |
|
|
client.session.action = SessionAction::SelectCharacter;
|
|
|
client.session.action = SessionAction::SelectCharacter;
|
|
|
client.session.character_slot = preview.slot as u8;
|
|
|
client.session.character_slot = preview.slot as u8;
|
|
|
user.flags = 0;
|
|
|
user.flags = 0;
|
|
|
self.entity_gateway.set_user(&user);
|
|
|
|
|
|
|
|
|
self.entity_gateway.set_user(&mut user);
|
|
|
Ok(vec![SendCharacterPacket::LoginResponse(LoginResponse::by_char_select(user.guildcard,
|
|
|
Ok(vec![SendCharacterPacket::LoginResponse(LoginResponse::by_char_select(user.guildcard,
|
|
|
user.team_id.unwrap_or(1),
|
|
|
user.team_id.unwrap_or(1),
|
|
|
client.session)),
|
|
|
client.session)),
|
|
@ -552,7 +568,8 @@ impl<EG: EntityGateway> ServerState for CharacterServerState<EG> { |
|
|
}
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
fn new_character_from_preview(character: &mut Character, preview: &CharacterPreview) {
|
|
|
|
|
|
|
|
|
fn new_character_from_preview(user: &UserAccountEntity, preview: &CharacterPreview) -> CharacterEntity {
|
|
|
|
|
|
let mut character = CharacterEntity::new(user.id.unwrap());
|
|
|
character.slot = preview.slot;
|
|
|
character.slot = preview.slot;
|
|
|
character.name = String::from_utf16_lossy(&preview.character.name).trim_matches(char::from(0)).into();
|
|
|
character.name = String::from_utf16_lossy(&preview.character.name).trim_matches(char::from(0)).into();
|
|
|
character.section_id = preview.character.section_id.into();
|
|
|
character.section_id = preview.character.section_id.into();
|
|
@ -567,11 +584,12 @@ fn new_character_from_preview(character: &mut Character, preview: &CharacterPrev |
|
|
character.appearance.hair_b = preview.character.hair_b;
|
|
|
character.appearance.hair_b = preview.character.hair_b;
|
|
|
character.appearance.prop_x = preview.character.prop_x;
|
|
|
character.appearance.prop_x = preview.character.prop_x;
|
|
|
character.appearance.prop_y = preview.character.prop_y;
|
|
|
character.appearance.prop_y = preview.character.prop_y;
|
|
|
|
|
|
character
|
|
|
}
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
struct SelectScreenCharacterBuilder<'a> {
|
|
|
struct SelectScreenCharacterBuilder<'a> {
|
|
|
character: Option<&'a Character>,
|
|
|
|
|
|
|
|
|
character: Option<&'a CharacterEntity>,
|
|
|
level: Option<u32>,
|
|
|
level: Option<u32>,
|
|
|
}
|
|
|
}
|
|
|
|
|
|
|
|
@ -583,7 +601,7 @@ impl<'a> SelectScreenCharacterBuilder<'a> { |
|
|
}
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
|
|
|
|
|
|
fn character(self, character: &'a Character) -> SelectScreenCharacterBuilder<'a> {
|
|
|
|
|
|
|
|
|
fn character(self, character: &'a CharacterEntity) -> SelectScreenCharacterBuilder<'a> {
|
|
|
SelectScreenCharacterBuilder {
|
|
|
SelectScreenCharacterBuilder {
|
|
|
character: Some(character),
|
|
|
character: Some(character),
|
|
|
..self
|
|
|
..self
|
|
@ -636,7 +654,7 @@ impl<'a> SelectScreenCharacterBuilder<'a> { |
|
|
#[cfg(test)]
|
|
|
#[cfg(test)]
|
|
|
mod test {
|
|
|
mod test {
|
|
|
use super::*;
|
|
|
use super::*;
|
|
|
use crate::entity::account::UserSettings;
|
|
|
|
|
|
|
|
|
use crate::entity::account::*;
|
|
|
use libpso::character::{settings, character};
|
|
|
use libpso::character::{settings, character};
|
|
|
use std::time::SystemTime;
|
|
|
use std::time::SystemTime;
|
|
|
use crate::entity::gateway::{InMemoryGateway};
|
|
|
use crate::entity::gateway::{InMemoryGateway};
|
|
@ -647,10 +665,10 @@ mod test { |
|
|
}
|
|
|
}
|
|
|
|
|
|
|
|
|
impl EntityGateway for TestData {
|
|
|
impl EntityGateway for TestData {
|
|
|
fn get_user_settings_by_user(&self, _user: &UserAccount) -> Option<UserSettings> {
|
|
|
|
|
|
Some(UserSettings {
|
|
|
|
|
|
id: 0,
|
|
|
|
|
|
user_id: 0,
|
|
|
|
|
|
|
|
|
fn get_user_settings_by_user(&self, user: &UserAccountEntity) -> Option<UserSettingsEntity> {
|
|
|
|
|
|
Some(UserSettingsEntity {
|
|
|
|
|
|
id: Some(UserSettingsId(0)),
|
|
|
|
|
|
user_id: user.id.unwrap(),
|
|
|
settings: settings::UserSettings::default()
|
|
|
settings: settings::UserSettings::default()
|
|
|
})
|
|
|
})
|
|
|
}
|
|
|
}
|
|
@ -658,8 +676,8 @@ mod test { |
|
|
|
|
|
|
|
|
let mut server = CharacterServerState::new(TestData {});
|
|
|
let mut server = CharacterServerState::new(TestData {});
|
|
|
let mut clientstate = ClientState::new();
|
|
|
let mut clientstate = ClientState::new();
|
|
|
clientstate.user = Some(UserAccount {
|
|
|
|
|
|
id: 1,
|
|
|
|
|
|
|
|
|
clientstate.user = Some(UserAccountEntity {
|
|
|
|
|
|
id: Some(UserAccountId(1)),
|
|
|
username: "testuser".to_owned(),
|
|
|
username: "testuser".to_owned(),
|
|
|
password: bcrypt::hash("mypassword", 5).unwrap(),
|
|
|
password: bcrypt::hash("mypassword", 5).unwrap(),
|
|
|
guildcard: 0,
|
|
|
guildcard: 0,
|
|
@ -700,8 +718,8 @@ mod test { |
|
|
fn test_character_create() {
|
|
|
fn test_character_create() {
|
|
|
let TestData = InMemoryGateway::new();
|
|
|
let TestData = InMemoryGateway::new();
|
|
|
let mut fake_user = ClientState::new();
|
|
|
let mut fake_user = ClientState::new();
|
|
|
fake_user.user = Some(UserAccount {
|
|
|
|
|
|
id: 3,
|
|
|
|
|
|
|
|
|
fake_user.user = Some(UserAccountEntity {
|
|
|
|
|
|
id: Some(UserAccountId(3)),
|
|
|
username: "hi3".to_string(),
|
|
|
username: "hi3".to_string(),
|
|
|
password: bcrypt::hash("qwer", 5).unwrap(),
|
|
|
password: bcrypt::hash("qwer", 5).unwrap(),
|
|
|
guildcard: 3,
|
|
|
guildcard: 3,
|
|
@ -715,7 +733,7 @@ mod test { |
|
|
let mut server = CharacterServerState::new(TestData.clone());
|
|
|
let mut server = CharacterServerState::new(TestData.clone());
|
|
|
server.clients.insert(ClientId(1), fake_user.clone());
|
|
|
server.clients.insert(ClientId(1), fake_user.clone());
|
|
|
let mut send = server.handle(ClientId(1), &RecvCharacterPacket::SetFlag(SetFlag {flags: 1})).unwrap().collect::<Vec<_>>();
|
|
|
let mut send = server.handle(ClientId(1), &RecvCharacterPacket::SetFlag(SetFlag {flags: 1})).unwrap().collect::<Vec<_>>();
|
|
|
assert!(TestData.get_user_by_id(3).unwrap().flags == 1);
|
|
|
|
|
|
|
|
|
assert!(TestData.get_user_by_id(UserAccountId(3)).unwrap().flags == 1);
|
|
|
send = server.handle(ClientId(1), &RecvCharacterPacket::CharacterPreview(CharacterPreview {slot: 1, character: character::SelectScreenCharacter {
|
|
|
send = server.handle(ClientId(1), &RecvCharacterPacket::CharacterPreview(CharacterPreview {slot: 1, character: character::SelectScreenCharacter {
|
|
|
exp: 0,
|
|
|
exp: 0,
|
|
|
level: 0,
|
|
|
level: 0,
|
|
|