use proper data structs that convert to what pso wants
This commit is contained in:
parent
81043891a1
commit
a74941441b
@ -1,7 +1,7 @@
|
|||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use std::fs::File;
|
use std::fs::File;
|
||||||
use serde_json::Value;
|
use serde_json::Value;
|
||||||
use libpso::character::character::Class;
|
use crate::entity::character::CharacterClass;
|
||||||
|
|
||||||
#[derive(Default, Copy, Clone, Debug, PartialEq)]
|
#[derive(Default, Copy, Clone, Debug, PartialEq)]
|
||||||
pub struct CharacterStats {
|
pub struct CharacterStats {
|
||||||
@ -27,7 +27,7 @@ struct CharacterLevelEntry {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub struct CharacterLevelTable {
|
pub struct CharacterLevelTable {
|
||||||
table: HashMap<Class, [CharacterLevelEntry; 200]>,
|
table: HashMap<CharacterClass, [CharacterLevelEntry; 200]>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -39,18 +39,18 @@ impl CharacterLevelTable {
|
|||||||
|
|
||||||
for it in json.as_object().unwrap().iter(){
|
for it in json.as_object().unwrap().iter(){
|
||||||
let cl = match it.0.as_str() {
|
let cl = match it.0.as_str() {
|
||||||
"HUmar" => Class::HUmar,
|
"HUmar" => CharacterClass::HUmar,
|
||||||
"HUnewearl" => Class::HUnewearl,
|
"HUnewearl" => CharacterClass::HUnewearl,
|
||||||
"HUcast" => Class::HUcast,
|
"HUcast" => CharacterClass::HUcast,
|
||||||
"HUcaseal" => Class::HUcaseal,
|
"HUcaseal" => CharacterClass::HUcaseal,
|
||||||
"RAmar" => Class::RAmar,
|
"RAmar" => CharacterClass::RAmar,
|
||||||
"RAmarl" => Class::RAmarl,
|
"RAmarl" => CharacterClass::RAmarl,
|
||||||
"RAcast" => Class::RAcast,
|
"RAcast" => CharacterClass::RAcast,
|
||||||
"RAcaseal" => Class::RAcaseal,
|
"RAcaseal" => CharacterClass::RAcaseal,
|
||||||
"FOmar" => Class::FOmar,
|
"FOmar" => CharacterClass::FOmar,
|
||||||
"FOmarl" => Class::FOmarl,
|
"FOmarl" => CharacterClass::FOmarl,
|
||||||
"FOnewm" => Class::FOnewm,
|
"FOnewm" => CharacterClass::FOnewm,
|
||||||
"FOnewearl" => Class::FOnewearl,
|
"FOnewearl" => CharacterClass::FOnewearl,
|
||||||
_ => panic!("unexpected class in char stats"),
|
_ => panic!("unexpected class in char stats"),
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -76,7 +76,7 @@ impl CharacterLevelTable {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_stats_from_exp(&self, ch_class: Class, exp: u32) -> (u32, CharacterStats) {
|
pub fn get_stats_from_exp(&self, ch_class: CharacterClass, exp: u32) -> (u32, CharacterStats) {
|
||||||
if let Some(statlist) = self.table.get(&ch_class) {
|
if let Some(statlist) = self.table.get(&ch_class) {
|
||||||
statlist
|
statlist
|
||||||
.iter()
|
.iter()
|
||||||
@ -107,7 +107,7 @@ mod test {
|
|||||||
#[test]
|
#[test]
|
||||||
fn test_stat_levels() {
|
fn test_stat_levels() {
|
||||||
let table = CharacterLevelTable::new();
|
let table = CharacterLevelTable::new();
|
||||||
assert!(table.get_stats_from_exp(Class::FOmarl, 0) == (1, CharacterStats { hp: 20, atp: 13, mst: 53, evp: 35, dfp: 10, ata: 15, lck: 10 }));
|
assert!(table.get_stats_from_exp(CharacterClass::FOmarl, 0) == (1, CharacterStats { hp: 20, atp: 13, mst: 53, evp: 35, dfp: 10, ata: 15, lck: 10 }));
|
||||||
assert!(table.get_stats_from_exp(Class::FOmarl, 1 << 17) == (36, CharacterStats { hp: 125, atp: 114, mst: 219, evp: 182, dfp: 42, ata: 213, lck: 10 }));
|
assert!(table.get_stats_from_exp(CharacterClass::FOmarl, 1 << 17) == (36, CharacterStats { hp: 125, atp: 114, mst: 219, evp: 182, dfp: 42, ata: 213, lck: 10 }));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,9 +1,251 @@
|
|||||||
|
use std::convert::{From, Into, TryFrom, TryInto};
|
||||||
|
use std::collections::HashMap;
|
||||||
|
|
||||||
use libpso::character::character;
|
use libpso::character::character;
|
||||||
|
|
||||||
#[derive(Copy, Clone, Debug, Default)]
|
#[derive(Copy, Clone, Hash, PartialEq, Eq)]
|
||||||
|
pub enum CharacterClass {
|
||||||
|
HUmar,
|
||||||
|
HUnewearl,
|
||||||
|
HUcast,
|
||||||
|
HUcaseal,
|
||||||
|
RAmar,
|
||||||
|
RAmarl,
|
||||||
|
RAcast,
|
||||||
|
RAcaseal,
|
||||||
|
FOmar,
|
||||||
|
FOmarl,
|
||||||
|
FOnewm,
|
||||||
|
FOnewearl,
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: TryFrom
|
||||||
|
impl From<u8> for CharacterClass {
|
||||||
|
fn from(f: u8) -> CharacterClass {
|
||||||
|
match f {
|
||||||
|
0 => CharacterClass::HUmar,
|
||||||
|
1 => CharacterClass::HUnewearl,
|
||||||
|
2 => CharacterClass::HUcast,
|
||||||
|
3 => CharacterClass::RAmar,
|
||||||
|
4 => CharacterClass::RAcast,
|
||||||
|
5 => CharacterClass::RAcaseal,
|
||||||
|
6 => CharacterClass::FOmarl,
|
||||||
|
7 => CharacterClass::FOnewm,
|
||||||
|
8 => CharacterClass::FOnewearl,
|
||||||
|
9 => CharacterClass::HUcaseal,
|
||||||
|
10 => CharacterClass::RAmarl,
|
||||||
|
11 => CharacterClass::FOmar,
|
||||||
|
_ => panic!("unknown class")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Into<u8> for CharacterClass {
|
||||||
|
fn into(self) -> u8 {
|
||||||
|
match self {
|
||||||
|
CharacterClass::HUmar => 0,
|
||||||
|
CharacterClass::HUnewearl => 1,
|
||||||
|
CharacterClass::HUcast => 2,
|
||||||
|
CharacterClass::RAmar => 3,
|
||||||
|
CharacterClass::RAcast => 4,
|
||||||
|
CharacterClass::RAcaseal => 5,
|
||||||
|
CharacterClass::FOmarl => 6,
|
||||||
|
CharacterClass::FOnewm => 7,
|
||||||
|
CharacterClass::FOnewearl => 8,
|
||||||
|
CharacterClass::HUcaseal => 9,
|
||||||
|
CharacterClass::RAmarl => 10,
|
||||||
|
CharacterClass::FOmar => 11,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#[derive(Copy, Clone, Hash, PartialEq, Eq)]
|
||||||
|
pub enum SectionID {
|
||||||
|
Viridia,
|
||||||
|
Greenill,
|
||||||
|
Skyly,
|
||||||
|
Bluefull,
|
||||||
|
Purplenum,
|
||||||
|
Pinkal,
|
||||||
|
Redria,
|
||||||
|
Oran,
|
||||||
|
Yellowboze,
|
||||||
|
Whitill,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<u8> for SectionID {
|
||||||
|
fn from(id: u8) -> SectionID {
|
||||||
|
match id {
|
||||||
|
0 => SectionID::Viridia,
|
||||||
|
1 => SectionID::Greenill,
|
||||||
|
2 => SectionID::Skyly,
|
||||||
|
3 => SectionID::Bluefull,
|
||||||
|
4 => SectionID::Purplenum,
|
||||||
|
5 => SectionID::Pinkal,
|
||||||
|
6 => SectionID::Redria,
|
||||||
|
7 => SectionID::Oran,
|
||||||
|
8 => SectionID::Yellowboze,
|
||||||
|
9 => SectionID::Whitill,
|
||||||
|
_ => panic!(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Into<u8> for SectionID {
|
||||||
|
fn into(self) -> u8 {
|
||||||
|
match self {
|
||||||
|
SectionID::Viridia => 0,
|
||||||
|
SectionID::Greenill => 1,
|
||||||
|
SectionID::Skyly => 2,
|
||||||
|
SectionID::Bluefull => 3,
|
||||||
|
SectionID::Purplenum => 4,
|
||||||
|
SectionID::Pinkal => 5,
|
||||||
|
SectionID::Redria => 6,
|
||||||
|
SectionID::Oran => 7,
|
||||||
|
SectionID::Yellowboze => 8,
|
||||||
|
SectionID::Whitill => 9,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, Default)]
|
||||||
|
pub struct CharacterAppearance {
|
||||||
|
pub costume: u16,
|
||||||
|
pub skin: u16,
|
||||||
|
pub face: u16,
|
||||||
|
pub head: u16,
|
||||||
|
pub hair: u16,
|
||||||
|
pub hair_r: u16,
|
||||||
|
pub hair_g: u16,
|
||||||
|
pub hair_b: u16,
|
||||||
|
pub prop_x: f32,
|
||||||
|
pub prop_y: f32,
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
|
||||||
|
pub enum Techniques {
|
||||||
|
Foie,
|
||||||
|
Gifoie,
|
||||||
|
Rafoie,
|
||||||
|
Zonde,
|
||||||
|
Gizonde,
|
||||||
|
Razonde,
|
||||||
|
Barta,
|
||||||
|
Gibarta,
|
||||||
|
Rabarta,
|
||||||
|
Grants,
|
||||||
|
Megid,
|
||||||
|
Shifta,
|
||||||
|
Deband,
|
||||||
|
Jellen,
|
||||||
|
Zalure,
|
||||||
|
Resta,
|
||||||
|
Anti,
|
||||||
|
Reverser,
|
||||||
|
Ryuker,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug)]
|
||||||
|
pub struct TechLevel(u8);
|
||||||
|
|
||||||
|
#[derive(Clone, Debug)]
|
||||||
|
pub struct CharacterTechniques {
|
||||||
|
techs: HashMap<Techniques, TechLevel>
|
||||||
|
}
|
||||||
|
|
||||||
|
impl CharacterTechniques {
|
||||||
|
fn new() -> CharacterTechniques {
|
||||||
|
CharacterTechniques {
|
||||||
|
techs: HashMap::new(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn set_tech(&mut self, tech: Techniques, level: TechLevel) {
|
||||||
|
self.techs.insert(tech, level);
|
||||||
|
}
|
||||||
|
|
||||||
|
// from_bytes
|
||||||
|
|
||||||
|
fn as_bytes(&self) -> [u8; 20] {
|
||||||
|
self.techs.iter()
|
||||||
|
.fold([0xFF; 20], |mut techlist, (tech, level)| {
|
||||||
|
let index = match tech {
|
||||||
|
Techniques::Foie => 0,
|
||||||
|
Techniques::Gifoie => 1,
|
||||||
|
Techniques::Rafoie => 2,
|
||||||
|
Techniques::Zonde => 3,
|
||||||
|
Techniques::Gizonde => 4,
|
||||||
|
Techniques::Razonde => 5,
|
||||||
|
Techniques::Barta => 6,
|
||||||
|
Techniques::Gibarta => 7,
|
||||||
|
Techniques::Rabarta => 8,
|
||||||
|
Techniques::Grants => 9,
|
||||||
|
Techniques::Megid => 10,
|
||||||
|
Techniques::Shifta => 11,
|
||||||
|
Techniques::Deband => 12,
|
||||||
|
Techniques::Jellen => 13,
|
||||||
|
Techniques::Zalure => 14,
|
||||||
|
Techniques::Resta => 15,
|
||||||
|
Techniques::Anti => 16,
|
||||||
|
Techniques::Reverser => 17,
|
||||||
|
Techniques::Ryuker => 18,
|
||||||
|
};
|
||||||
|
|
||||||
|
techlist[index] = level.0;
|
||||||
|
techlist
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub struct CharacterConfig {
|
||||||
|
raw_data: [u8; 0xE8],
|
||||||
|
}
|
||||||
|
|
||||||
|
impl CharacterConfig {
|
||||||
|
fn new() -> CharacterConfig {
|
||||||
|
CharacterConfig {
|
||||||
|
raw_data: [0; 0xE8],
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
pub struct Character {
|
pub struct Character {
|
||||||
pub id: u32,
|
pub id: u32,
|
||||||
pub user_id: u32,
|
pub user_id: u32,
|
||||||
pub slot: u32,
|
pub slot: u32,
|
||||||
pub character: character::Character,
|
|
||||||
|
pub name: String,
|
||||||
|
pub exp: u32,
|
||||||
|
|
||||||
|
pub char_class: CharacterClass,
|
||||||
|
pub section_id: SectionID,
|
||||||
|
|
||||||
|
pub appearance: CharacterAppearance,
|
||||||
|
pub techs: CharacterTechniques,
|
||||||
|
pub config: CharacterConfig,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl std::default::Default for Character {
|
||||||
|
fn default() -> Character {
|
||||||
|
Character {
|
||||||
|
id: 0,
|
||||||
|
user_id: 0,
|
||||||
|
slot: 0,
|
||||||
|
name: "".into(),
|
||||||
|
exp: 0,
|
||||||
|
char_class: CharacterClass::HUmar,
|
||||||
|
section_id: SectionID::Viridia,
|
||||||
|
appearance: CharacterAppearance::default(),
|
||||||
|
techs: CharacterTechniques::new(),
|
||||||
|
config: CharacterConfig::new(),
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -8,6 +8,7 @@ use libpso::packet::login::*;
|
|||||||
use libpso::{PacketParseError, PSOPacket};
|
use libpso::{PacketParseError, PSOPacket};
|
||||||
use libpso::crypto::bb::PSOBBCipher;
|
use libpso::crypto::bb::PSOBBCipher;
|
||||||
use libpso::item;
|
use libpso::item;
|
||||||
|
use libpso::character::character;
|
||||||
|
|
||||||
use crate::common::cipherkeys::{ELSEWHERE_PRIVATE_KEY, ELSEWHERE_PARRAY};
|
use crate::common::cipherkeys::{ELSEWHERE_PRIVATE_KEY, ELSEWHERE_PARRAY};
|
||||||
use crate::common::serverstate::{SendServerPacket, RecvServerPacket, ServerState, OnConnect, ClientId};
|
use crate::common::serverstate::{SendServerPacket, RecvServerPacket, ServerState, OnConnect, ClientId};
|
||||||
@ -17,7 +18,7 @@ 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::account::{UserAccount, USERFLAG_NEWCHAR, USERFLAG_DRESSINGROOM};
|
||||||
use crate::entity::item::{ItemDetail, ItemLocation, Weapon, Armor, Shield, Tool};
|
use crate::entity::item::{ItemDetail, ItemLocation, Weapon, Armor, Shield, Tool};
|
||||||
use crate::entity::character::Character;
|
use crate::entity::character::{Character, CharacterClass};
|
||||||
|
|
||||||
use crate::login::login::get_login_status;
|
use crate::login::login::get_login_status;
|
||||||
|
|
||||||
@ -183,19 +184,15 @@ pub struct CharacterServerState<EG: EntityGateway> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
fn new_character<EG: EntityGateway>(entity_gateway: &mut EG, user: &UserAccount, preview: &CharacterPreview) {
|
fn new_character<EG: EntityGateway>(entity_gateway: &mut EG, user: &UserAccount, preview: &CharacterPreview) {
|
||||||
let mut char = entity_gateway.new_character_by_user(&user);
|
let mut char = entity_gateway.new_character_by_user(&user);
|
||||||
char.slot = preview.slot;
|
new_character_from_preview(&mut char, preview);
|
||||||
char.character = preview.character.as_character();
|
|
||||||
entity_gateway.set_character(&char);
|
entity_gateway.set_character(&char);
|
||||||
|
|
||||||
let new_weapon = match char.character.ch_class {
|
let new_weapon = match char.char_class {
|
||||||
0 | 1 | 2 | 9 => item::weapon::WeaponType::Saber,
|
CharacterClass::HUmar | CharacterClass::HUnewearl | CharacterClass::HUcast | CharacterClass::HUcaseal => item::weapon::WeaponType::Saber,
|
||||||
3 | 4 | 5 | 11 => item::weapon::WeaponType::Handgun,
|
CharacterClass::RAmar | CharacterClass::RAmarl | CharacterClass::RAcast | CharacterClass::RAcaseal => item::weapon::WeaponType::Handgun,
|
||||||
6 | 7 | 8 | 10 => item::weapon::WeaponType::Cane,
|
CharacterClass::FOmar| CharacterClass::FOmarl| CharacterClass::FOnewm | CharacterClass::FOnewearl => item::weapon::WeaponType::Cane,
|
||||||
_ => panic!()
|
|
||||||
};
|
};
|
||||||
|
|
||||||
entity_gateway.new_item(
|
entity_gateway.new_item(
|
||||||
@ -330,9 +327,13 @@ impl<EG: EntityGateway> CharacterServerState<EG> {
|
|||||||
if select.reason == 0 {
|
if select.reason == 0 {
|
||||||
let chars = client.characters.as_ref().unwrap();
|
let chars = client.characters.as_ref().unwrap();
|
||||||
Ok(if let Some(char) = &chars[select.slot as usize] {
|
Ok(if let Some(char) = &chars[select.slot as usize] {
|
||||||
|
let (level, stats) = self.level_table.get_stats_from_exp(char.char_class, char.exp);
|
||||||
vec![SendCharacterPacket::CharacterPreview(CharacterPreview {
|
vec![SendCharacterPacket::CharacterPreview(CharacterPreview {
|
||||||
slot: select.slot,
|
slot: select.slot,
|
||||||
character: char.character.as_select_screen(),
|
character: SelectScreenCharacterBuilder::new()
|
||||||
|
.character(&char)
|
||||||
|
.level(level)
|
||||||
|
.build()
|
||||||
})]
|
})]
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
@ -525,6 +526,88 @@ impl<EG: EntityGateway> ServerState for CharacterServerState<EG> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
fn new_character_from_preview(character: &mut Character, preview: &CharacterPreview) {
|
||||||
|
character.slot = preview.slot;
|
||||||
|
character.name = String::from_utf16_lossy(&preview.character.name);
|
||||||
|
character.section_id = preview.character.section_id.into();
|
||||||
|
character.char_class = preview.character.ch_class.into();
|
||||||
|
character.appearance.costume = preview.character.costume;
|
||||||
|
character.appearance.skin = preview.character.skin;
|
||||||
|
character.appearance.face = preview.character.face;
|
||||||
|
character.appearance.head = preview.character.head;
|
||||||
|
character.appearance.hair = preview.character.hair;
|
||||||
|
character.appearance.hair_r = preview.character.hair_r;
|
||||||
|
character.appearance.hair_g = preview.character.hair_g;
|
||||||
|
character.appearance.hair_b = preview.character.hair_b;
|
||||||
|
character.appearance.prop_x = preview.character.prop_x;
|
||||||
|
character.appearance.prop_y = preview.character.prop_y;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
struct SelectScreenCharacterBuilder<'a> {
|
||||||
|
character: Option<&'a Character>,
|
||||||
|
level: Option<u32>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> SelectScreenCharacterBuilder<'a> {
|
||||||
|
fn new() -> SelectScreenCharacterBuilder<'a> {
|
||||||
|
SelectScreenCharacterBuilder {
|
||||||
|
character: None,
|
||||||
|
level: None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn character(self, character: &'a Character) -> SelectScreenCharacterBuilder<'a> {
|
||||||
|
SelectScreenCharacterBuilder {
|
||||||
|
character: Some(character),
|
||||||
|
..self
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn level(self, level: u32) -> SelectScreenCharacterBuilder<'a> {
|
||||||
|
SelectScreenCharacterBuilder {
|
||||||
|
level: Some(level),
|
||||||
|
..self
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn build(self) -> character::SelectScreenCharacter {
|
||||||
|
let character = self.character.unwrap();
|
||||||
|
let level = self.level.unwrap();
|
||||||
|
|
||||||
|
character::SelectScreenCharacter {
|
||||||
|
//exp: character.exp,
|
||||||
|
level: level - 1,
|
||||||
|
//guildcard: character.guildcard,
|
||||||
|
//_unknown: character._unknown3,
|
||||||
|
//name_color: character.name_color,
|
||||||
|
//model: character.model,
|
||||||
|
//_unused: [0; 15],
|
||||||
|
//name_color_checksum: character.name_color_checksum,
|
||||||
|
section_id: character.section_id.into(),
|
||||||
|
ch_class: character.char_class.into(),
|
||||||
|
//v2flags: character.v2flags,
|
||||||
|
//version: character.version,
|
||||||
|
//v1flags: character.v1flags,
|
||||||
|
costume: character.appearance.costume,
|
||||||
|
skin: character.appearance.skin,
|
||||||
|
face: character.appearance.face,
|
||||||
|
head: character.appearance.head,
|
||||||
|
hair: character.appearance.hair,
|
||||||
|
hair_r: character.appearance.hair_r,
|
||||||
|
hair_g: character.appearance.hair_g,
|
||||||
|
hair_b: character.appearance.hair_b,
|
||||||
|
prop_x: character.appearance.prop_x,
|
||||||
|
prop_y: character.appearance.prop_y,
|
||||||
|
name: utf8_to_utf16_array!(character.name, 16),
|
||||||
|
//play_time: character.play_time,
|
||||||
|
..character::SelectScreenCharacter::default()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod test {
|
mod test {
|
||||||
use super::*;
|
use super::*;
|
||||||
|
17
src/main.rs
17
src/main.rs
@ -75,11 +75,12 @@ fn main() {
|
|||||||
entity_gateway.set_user(&fake_user);
|
entity_gateway.set_user(&fake_user);
|
||||||
entity_gateway.create_user_settings_by_user(&fake_user);
|
entity_gateway.create_user_settings_by_user(&fake_user);
|
||||||
let mut character = entity_gateway.new_character_by_user(&fake_user);
|
let mut character = entity_gateway.new_character_by_user(&fake_user);
|
||||||
character.character.name = utf8_to_utf16_array!("Test Char 1", 0x10);
|
character.name = "Test Char 1".into();
|
||||||
entity_gateway.set_character(&character);
|
entity_gateway.set_character(&character);
|
||||||
let mut character = entity_gateway.new_character_by_user(&fake_user);
|
let mut character = entity_gateway.new_character_by_user(&fake_user);
|
||||||
character.slot = 2;
|
character.slot = 2;
|
||||||
character.character.name = utf8_to_utf16_array!("Test Char 2", 0x10);
|
character.name = "\tE12345678".into();
|
||||||
|
character.exp = 80000000;
|
||||||
entity_gateway.set_character(&character);
|
entity_gateway.set_character(&character);
|
||||||
|
|
||||||
let fake_user2 = UserAccount {
|
let fake_user2 = UserAccount {
|
||||||
@ -96,11 +97,11 @@ fn main() {
|
|||||||
entity_gateway.set_user(&fake_user2);
|
entity_gateway.set_user(&fake_user2);
|
||||||
entity_gateway.create_user_settings_by_user(&fake_user2);
|
entity_gateway.create_user_settings_by_user(&fake_user2);
|
||||||
let mut character = entity_gateway.new_character_by_user(&fake_user2);
|
let mut character = entity_gateway.new_character_by_user(&fake_user2);
|
||||||
character.character.name = utf8_to_utf16_array!("Test Char 3", 0x10);
|
character.name = "Test Char 3".into();
|
||||||
entity_gateway.set_character(&character);
|
entity_gateway.set_character(&character);
|
||||||
let mut character = entity_gateway.new_character_by_user(&fake_user2);
|
let mut character = entity_gateway.new_character_by_user(&fake_user2);
|
||||||
character.slot = 2;
|
character.slot = 2;
|
||||||
character.character.name = utf8_to_utf16_array!("Test Char 4", 0x10);
|
character.name = "Test Char 4".into();
|
||||||
entity_gateway.set_character(&character);
|
entity_gateway.set_character(&character);
|
||||||
|
|
||||||
let fake_user3 = UserAccount {
|
let fake_user3 = UserAccount {
|
||||||
@ -117,11 +118,11 @@ fn main() {
|
|||||||
entity_gateway.set_user(&fake_user3);
|
entity_gateway.set_user(&fake_user3);
|
||||||
entity_gateway.create_user_settings_by_user(&fake_user3);
|
entity_gateway.create_user_settings_by_user(&fake_user3);
|
||||||
let mut character = entity_gateway.new_character_by_user(&fake_user3);
|
let mut character = entity_gateway.new_character_by_user(&fake_user3);
|
||||||
character.character.name = utf8_to_utf16_array!("Test Char 5", 0x10);
|
character.name = "Test Char 5".into();
|
||||||
entity_gateway.set_character(&character);
|
entity_gateway.set_character(&character);
|
||||||
let mut character = entity_gateway.new_character_by_user(&fake_user3);
|
let mut character = entity_gateway.new_character_by_user(&fake_user3);
|
||||||
character.slot = 2;
|
character.slot = 2;
|
||||||
character.character.name = utf8_to_utf16_array!("Test Char 6", 0x10);
|
character.name = "Test Char 6".into();
|
||||||
entity_gateway.set_character(&character);
|
entity_gateway.set_character(&character);
|
||||||
|
|
||||||
let fake_user4 = UserAccount {
|
let fake_user4 = UserAccount {
|
||||||
@ -138,11 +139,11 @@ fn main() {
|
|||||||
entity_gateway.set_user(&fake_user4);
|
entity_gateway.set_user(&fake_user4);
|
||||||
entity_gateway.create_user_settings_by_user(&fake_user4);
|
entity_gateway.create_user_settings_by_user(&fake_user4);
|
||||||
let mut character = entity_gateway.new_character_by_user(&fake_user4);
|
let mut character = entity_gateway.new_character_by_user(&fake_user4);
|
||||||
character.character.name = utf8_to_utf16_array!("Test Char 7", 0x10);
|
character.name = "Test Char 7".into();
|
||||||
entity_gateway.set_character(&character);
|
entity_gateway.set_character(&character);
|
||||||
let mut character = entity_gateway.new_character_by_user(&fake_user4);
|
let mut character = entity_gateway.new_character_by_user(&fake_user4);
|
||||||
character.slot = 2;
|
character.slot = 2;
|
||||||
character.character.name = utf8_to_utf16_array!("Test Char 8", 0x10);
|
character.name = "Test Char 8".into();
|
||||||
entity_gateway.set_character(&character);
|
entity_gateway.set_character(&character);
|
||||||
|
|
||||||
async_std::task::block_on(async move {
|
async_std::task::block_on(async move {
|
||||||
|
@ -1,40 +1,41 @@
|
|||||||
use crate::common::leveltable::CharacterStats;
|
|
||||||
use libpso::character::character;
|
use libpso::character::character;
|
||||||
|
use crate::common::leveltable::CharacterStats;
|
||||||
|
use crate::entity::character::Character;
|
||||||
use crate::ship::items::Inventory;
|
use crate::ship::items::Inventory;
|
||||||
|
|
||||||
|
// TODO: exp
|
||||||
pub struct CharacterBuilder<'a> {
|
pub struct CharacterBytesBuilder<'a> {
|
||||||
character: Option<&'a character::Character>,
|
character: Option<&'a Character>,
|
||||||
stats: Option<&'a CharacterStats>,
|
stats: Option<&'a CharacterStats>,
|
||||||
level: Option<u32>,
|
level: Option<u32>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
impl<'a> CharacterBuilder<'a> {
|
impl<'a> CharacterBytesBuilder<'a> {
|
||||||
pub fn new() -> CharacterBuilder<'a> {
|
pub fn new() -> CharacterBytesBuilder<'a> {
|
||||||
CharacterBuilder {
|
CharacterBytesBuilder {
|
||||||
character: None,
|
character: None,
|
||||||
stats: None,
|
stats: None,
|
||||||
level: None,
|
level: None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn character(self, character: &'a character::Character) -> CharacterBuilder<'a> {
|
pub fn character(self, character: &'a Character) -> CharacterBytesBuilder<'a> {
|
||||||
CharacterBuilder {
|
CharacterBytesBuilder {
|
||||||
character: Some(character),
|
character: Some(character),
|
||||||
..self
|
..self
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn stats(self, stats: &'a CharacterStats) -> CharacterBuilder<'a> {
|
pub fn stats(self, stats: &'a CharacterStats) -> CharacterBytesBuilder<'a> {
|
||||||
CharacterBuilder {
|
CharacterBytesBuilder {
|
||||||
stats: Some(stats),
|
stats: Some(stats),
|
||||||
..self
|
..self
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn level(self, level: u32) -> CharacterBuilder<'a> {
|
pub fn level(self, level: u32) -> CharacterBytesBuilder<'a> {
|
||||||
CharacterBuilder {
|
CharacterBytesBuilder {
|
||||||
level: Some(level),
|
level: Some(level),
|
||||||
..self
|
..self
|
||||||
}
|
}
|
||||||
@ -45,6 +46,7 @@ impl<'a> CharacterBuilder<'a> {
|
|||||||
let stats = self.stats.unwrap();
|
let stats = self.stats.unwrap();
|
||||||
let level = self.level.unwrap();
|
let level = self.level.unwrap();
|
||||||
character::Character {
|
character::Character {
|
||||||
|
name: libpso::utf8_to_utf16_array!(character.name, 16),
|
||||||
hp: stats.hp,
|
hp: stats.hp,
|
||||||
atp: stats.atp,
|
atp: stats.atp,
|
||||||
mst: stats.mst,
|
mst: stats.mst,
|
||||||
@ -53,53 +55,83 @@ impl<'a> CharacterBuilder<'a> {
|
|||||||
ata: stats.ata,
|
ata: stats.ata,
|
||||||
lck: stats.lck,
|
lck: stats.lck,
|
||||||
level: level,
|
level: level,
|
||||||
..*character
|
section_id: character.section_id.into(),
|
||||||
|
ch_class: character.char_class.into(),
|
||||||
|
costume: character.appearance.costume,
|
||||||
|
skin: character.appearance.skin,
|
||||||
|
face: character.appearance.face,
|
||||||
|
head: character.appearance.head,
|
||||||
|
hair: character.appearance.hair,
|
||||||
|
hair_r: character.appearance.hair_r,
|
||||||
|
hair_g: character.appearance.hair_g,
|
||||||
|
hair_b: character.appearance.hair_b,
|
||||||
|
prop_x: character.appearance.prop_x,
|
||||||
|
prop_y: character.appearance.prop_y,
|
||||||
|
..character::Character::default()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
pub struct FullCharacterBuilder<'a> {
|
pub struct FullCharacterBytesBuilder<'a> {
|
||||||
character: Option<&'a character::Character>,
|
character: Option<&'a Character>,
|
||||||
|
stats: Option<&'a CharacterStats>,
|
||||||
|
level: Option<u32>,
|
||||||
inventory: Option<&'a Inventory>,
|
inventory: Option<&'a Inventory>,
|
||||||
key_config: Option<&'a [u8; 0x16C]>,
|
key_config: Option<&'a [u8; 0x16C]>,
|
||||||
joystick_config: Option<&'a [u8; 0x38]>,
|
joystick_config: Option<&'a [u8; 0x38]>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
impl<'a> FullCharacterBuilder<'a> {
|
impl<'a> FullCharacterBytesBuilder<'a> {
|
||||||
pub fn new() -> FullCharacterBuilder<'a> {
|
pub fn new() -> FullCharacterBytesBuilder<'a> {
|
||||||
FullCharacterBuilder {
|
FullCharacterBytesBuilder {
|
||||||
character: None,
|
character: None,
|
||||||
|
stats: None,
|
||||||
|
level: None,
|
||||||
inventory: None,
|
inventory: None,
|
||||||
key_config: None,
|
key_config: None,
|
||||||
joystick_config: None,
|
joystick_config: None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn character(self, character: &'a character::Character) -> FullCharacterBuilder<'a> {
|
pub fn character(self, character: &'a Character) -> FullCharacterBytesBuilder<'a> {
|
||||||
FullCharacterBuilder {
|
FullCharacterBytesBuilder {
|
||||||
character: Some(character),
|
character: Some(character),
|
||||||
..self
|
..self
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn inventory(self, inventory: &'a Inventory) -> FullCharacterBuilder<'a> {
|
pub fn stats(self, stats: &'a CharacterStats) -> FullCharacterBytesBuilder<'a> {
|
||||||
FullCharacterBuilder {
|
FullCharacterBytesBuilder {
|
||||||
|
stats: Some(stats),
|
||||||
|
..self
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn level(self, level: u32) -> FullCharacterBytesBuilder<'a> {
|
||||||
|
FullCharacterBytesBuilder {
|
||||||
|
level: Some(level),
|
||||||
|
..self
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn inventory(self, inventory: &'a Inventory) -> FullCharacterBytesBuilder<'a> {
|
||||||
|
FullCharacterBytesBuilder {
|
||||||
inventory: Some(inventory),
|
inventory: Some(inventory),
|
||||||
..self
|
..self
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn key_config(self, key_config: &'a [u8; 0x16C]) -> FullCharacterBuilder<'a> {
|
pub fn key_config(self, key_config: &'a [u8; 0x16C]) -> FullCharacterBytesBuilder<'a> {
|
||||||
FullCharacterBuilder {
|
FullCharacterBytesBuilder {
|
||||||
key_config: Some(key_config),
|
key_config: Some(key_config),
|
||||||
..self
|
..self
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn joystick_config(self, joystick_config: &'a [u8; 0x38]) -> FullCharacterBuilder<'a> {
|
pub fn joystick_config(self, joystick_config: &'a [u8; 0x38]) -> FullCharacterBytesBuilder<'a> {
|
||||||
FullCharacterBuilder {
|
FullCharacterBytesBuilder {
|
||||||
joystick_config: Some(joystick_config),
|
joystick_config: Some(joystick_config),
|
||||||
..self
|
..self
|
||||||
}
|
}
|
||||||
@ -107,12 +139,18 @@ impl<'a> FullCharacterBuilder<'a> {
|
|||||||
|
|
||||||
pub fn build(self) -> character::FullCharacter {
|
pub fn build(self) -> character::FullCharacter {
|
||||||
let character = self.character.unwrap();
|
let character = self.character.unwrap();
|
||||||
|
let stats = self.stats.unwrap();
|
||||||
|
let level = self.level.unwrap();
|
||||||
let inventory = self.inventory.unwrap();
|
let inventory = self.inventory.unwrap();
|
||||||
let key_config = self.key_config.unwrap();
|
let key_config = self.key_config.unwrap();
|
||||||
let joystick_config = self.joystick_config.unwrap();
|
let joystick_config = self.joystick_config.unwrap();
|
||||||
|
|
||||||
character::FullCharacter {
|
character::FullCharacter {
|
||||||
character: *character,
|
character: CharacterBytesBuilder::new()
|
||||||
|
.character(&character)
|
||||||
|
.stats(&stats)
|
||||||
|
.level(level - 1)
|
||||||
|
.build(),
|
||||||
inventory: character::Inventory {
|
inventory: character::Inventory {
|
||||||
item_count: inventory.count() as u8,
|
item_count: inventory.count() as u8,
|
||||||
items: inventory.as_client_inventory_items(),
|
items: inventory.as_client_inventory_items(),
|
||||||
|
@ -20,7 +20,7 @@ use crate::entity::character::Character;
|
|||||||
use crate::entity::item::{ItemLocation, Item};
|
use crate::entity::item::{ItemLocation, Item};
|
||||||
use crate::login::login::get_login_status;
|
use crate::login::login::get_login_status;
|
||||||
use crate::ship::location::{ClientLocation, LobbyId, RoomId, AreaType, MAX_ROOMS};
|
use crate::ship::location::{ClientLocation, LobbyId, RoomId, AreaType, MAX_ROOMS};
|
||||||
use crate::ship::character::{CharacterBuilder, FullCharacterBuilder};
|
use crate::ship::character::{CharacterBytesBuilder, FullCharacterBytesBuilder};
|
||||||
use crate::ship::items;
|
use crate::ship::items;
|
||||||
use crate::ship::room;
|
use crate::ship::room;
|
||||||
|
|
||||||
@ -162,13 +162,14 @@ impl<EG: EntityGateway> ShipServerState<EG> {
|
|||||||
Ok(match get_login_status(&self.entity_gateway, pkt) {
|
Ok(match get_login_status(&self.entity_gateway, pkt) {
|
||||||
Ok(user) => {
|
Ok(user) => {
|
||||||
let mut response = LoginResponse::by_status(AccountStatus::Ok, Session::new());
|
let mut response = LoginResponse::by_status(AccountStatus::Ok, Session::new());
|
||||||
response.guildcard = user.guildcard.map_or(24, |gc| gc) as u32;
|
response.guildcard = user.id as u32;
|
||||||
response.team_id = user.team_id.map_or(31, |ti| ti) as u32;
|
response.team_id = user.team_id.map_or(31, |ti| ti) as u32;
|
||||||
let characters = self.entity_gateway.get_characters_by_user(&user);
|
let characters = self.entity_gateway.get_characters_by_user(&user);
|
||||||
let character = characters
|
let character = characters
|
||||||
.get(pkt.session.character_slot as usize)
|
.get(pkt.session.character_slot as usize)
|
||||||
.ok_or(ShipError::InvalidSlot(id, pkt.session.character_slot as u32))?
|
.ok_or(ShipError::InvalidSlot(id, pkt.session.character_slot as u32))?.as_ref()
|
||||||
.ok_or(ShipError::NoCharacterInSlot(id, pkt.session.character_slot as u32))?;
|
.ok_or(ShipError::NoCharacterInSlot(id, pkt.session.character_slot as u32))?
|
||||||
|
.clone();
|
||||||
let settings = self.entity_gateway.get_user_settings_by_user(&user)
|
let settings = self.entity_gateway.get_user_settings_by_user(&user)
|
||||||
.ok_or(ShipError::ClientNotFound(id))?;
|
.ok_or(ShipError::ClientNotFound(id))?;
|
||||||
|
|
||||||
@ -191,14 +192,12 @@ impl<EG: EntityGateway> ShipServerState<EG> {
|
|||||||
let client = self.clients.get_mut(&id).ok_or(ShipError::ClientNotFound(id))?;
|
let client = self.clients.get_mut(&id).ok_or(ShipError::ClientNotFound(id))?;
|
||||||
client.block = pkt.item as u32;
|
client.block = pkt.item as u32;
|
||||||
|
|
||||||
let (level, stats) = self.level_table.get_stats_from_exp(character::Class::from(client.character.character.ch_class), client.character.character.exp);
|
let (level, stats) = self.level_table.get_stats_from_exp(client.character.char_class, client.character.exp);
|
||||||
|
|
||||||
let fc = FullCharacterBuilder::new()
|
let fc = FullCharacterBytesBuilder::new()
|
||||||
.character(&CharacterBuilder::new()
|
.character(&client.character)
|
||||||
.character(&client.character.character)
|
.stats(&stats)
|
||||||
.stats(&stats)
|
.level(level)
|
||||||
.level(level - 1)
|
|
||||||
.build())
|
|
||||||
.inventory(&client.inventory)
|
.inventory(&client.inventory)
|
||||||
.key_config(&client.settings.settings.key_config)
|
.key_config(&client.settings.settings.key_config)
|
||||||
.joystick_config(&client.settings.settings.joystick_config)
|
.joystick_config(&client.settings.settings.joystick_config)
|
||||||
@ -221,9 +220,9 @@ impl<EG: EntityGateway> ShipServerState<EG> {
|
|||||||
let playerinfo = clients.iter()
|
let playerinfo = clients.iter()
|
||||||
.map(|room_client| {
|
.map(|room_client| {
|
||||||
let client = self.clients.get(&room_client.client_id).ok_or(ShipError::ClientNotFound(id)).unwrap();
|
let client = self.clients.get(&room_client.client_id).ok_or(ShipError::ClientNotFound(id)).unwrap();
|
||||||
let (level, stats) = self.level_table.get_stats_from_exp(character::Class::from(client.character.character.ch_class), client.character.character.exp);
|
let (level, stats) = self.level_table.get_stats_from_exp(client.character.char_class, client.character.exp);
|
||||||
let c = CharacterBuilder::new()
|
let c = CharacterBytesBuilder::new()
|
||||||
.character(&client.character.character)
|
.character(&client.character)
|
||||||
.stats(&stats)
|
.stats(&stats)
|
||||||
.level(level - 1)
|
.level(level - 1)
|
||||||
.build();
|
.build();
|
||||||
@ -268,9 +267,9 @@ impl<EG: EntityGateway> ShipServerState<EG> {
|
|||||||
};
|
};
|
||||||
|
|
||||||
let client = self.clients.get_mut(&id).ok_or(ShipError::ClientNotFound(id)).unwrap();
|
let client = self.clients.get_mut(&id).ok_or(ShipError::ClientNotFound(id)).unwrap();
|
||||||
let (level, stats) = self.level_table.get_stats_from_exp(character::Class::from(client.character.character.ch_class), client.character.character.exp);
|
let (level, stats) = self.level_table.get_stats_from_exp(client.character.char_class, client.character.exp);
|
||||||
let c = CharacterBuilder::new()
|
let c = CharacterBytesBuilder::new()
|
||||||
.character(&client.character.character)
|
.character(&client.character)
|
||||||
.stats(&stats)
|
.stats(&stats)
|
||||||
.level(level - 1)
|
.level(level - 1)
|
||||||
.build();
|
.build();
|
||||||
@ -334,7 +333,7 @@ impl<EG: EntityGateway> ShipServerState<EG> {
|
|||||||
|
|
||||||
fn player_chat(&mut self, id: ClientId, msg: &PlayerChat) -> Result<Box<dyn Iterator<Item = (ClientId, SendShipPacket)> + Send>, ShipError> {
|
fn player_chat(&mut self, id: ClientId, msg: &PlayerChat) -> Result<Box<dyn Iterator<Item = (ClientId, SendShipPacket)> + Send>, ShipError> {
|
||||||
let client = self.clients.get_mut(&id).ok_or(ShipError::ClientNotFound(id))?;
|
let client = self.clients.get_mut(&id).ok_or(ShipError::ClientNotFound(id))?;
|
||||||
let cmsg = PlayerChat::new(client.user.guildcard.unwrap(), msg.message.clone());
|
let cmsg = PlayerChat::new(client.user.id, msg.message.clone());
|
||||||
|
|
||||||
Ok(Box::new(self.client_location.get_area_by_user(id).clients().iter()
|
Ok(Box::new(self.client_location.get_area_by_user(id).clients().iter()
|
||||||
.map(move |client| {
|
.map(move |client| {
|
||||||
@ -364,7 +363,7 @@ impl<EG: EntityGateway> ShipServerState<EG> {
|
|||||||
guildcard: client.user.id,
|
guildcard: client.user.id,
|
||||||
_unknown1: [0; 5],
|
_unknown1: [0; 5],
|
||||||
client_id: 0,
|
client_id: 0,
|
||||||
name: client.character.character.name,
|
name: libpso::utf8_to_utf16_array!(client.character.name, 16),
|
||||||
_unknown2: 2,
|
_unknown2: 2,
|
||||||
}, PlayerHeader::default(), PlayerHeader::default(), PlayerHeader::default()];
|
}, PlayerHeader::default(), PlayerHeader::default(), PlayerHeader::default()];
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user