Browse Source

ship list logic

pbs
jake 5 years ago
parent
commit
fc7b15c129
  1. 5
      Cargo.toml
  2. 134
      src/login/character.rs
  3. 60
      src/login/main.rs

5
Cargo.toml

@ -20,3 +20,8 @@ mio-extras = "2.0.5"
crc = "^1.0.0" crc = "^1.0.0"
bcrypt = "0.4" bcrypt = "0.4"
threadpool = "1.0" threadpool = "1.0"
chrono = "*"
[patch."http://git.sharnoth.com/jake/libpso"]
libpso = { path = "../libpso" }

134
src/login/character.rs

@ -10,11 +10,11 @@ use libpso::crypto::bb::PSOBBCipher;
use elseware::common::cipherkeys::{ELSEWHERE_PRIVATE_KEY, ELSEWHERE_PARRAY}; use elseware::common::cipherkeys::{ELSEWHERE_PRIVATE_KEY, ELSEWHERE_PARRAY};
use elseware::common::serverstate::{SendServerPacket, RecvServerPacket, ServerState, OnConnect, ClientId}; use elseware::common::serverstate::{SendServerPacket, RecvServerPacket, ServerState, OnConnect, ClientId};
use elseware::utf8_to_array;
use elseware::{utf8_to_array, utf8_to_utf16_array};
use crate::dataaccess::DataAccess; use crate::dataaccess::DataAccess;
use crate::login::get_login_status; use crate::login::get_login_status;
use crate::entities::{UserAccount, Character};
use crate::entities::{UserAccount, Character, USERFLAG_NEWCHAR, USERFLAG_DRESSINGROOM};
pub const CHARACTER_PORT: u16 = 12001; pub const CHARACTER_PORT: u16 = 12001;
@ -33,6 +33,8 @@ pub enum RecvCharacterPacket {
GuildcardDataChunkRequest(GuildcardDataChunkRequest), GuildcardDataChunkRequest(GuildcardDataChunkRequest),
ParamDataRequest(ParamDataRequest), ParamDataRequest(ParamDataRequest),
ParamDataChunkRequest(ParamDataChunkRequest), ParamDataChunkRequest(ParamDataChunkRequest),
CharacterPreview(CharacterPreview),
SetFlag(SetFlag)
} }
impl RecvServerPacket for RecvCharacterPacket { impl RecvServerPacket for RecvCharacterPacket {
@ -46,6 +48,8 @@ impl RecvServerPacket for RecvCharacterPacket {
0x3DC => Ok(RecvCharacterPacket::GuildcardDataChunkRequest(GuildcardDataChunkRequest::from_bytes(data)?)), 0x3DC => Ok(RecvCharacterPacket::GuildcardDataChunkRequest(GuildcardDataChunkRequest::from_bytes(data)?)),
0x4EB => Ok(RecvCharacterPacket::ParamDataRequest(ParamDataRequest::from_bytes(data)?)), 0x4EB => Ok(RecvCharacterPacket::ParamDataRequest(ParamDataRequest::from_bytes(data)?)),
0x3EB => Ok(RecvCharacterPacket::ParamDataChunkRequest(ParamDataChunkRequest::from_bytes(data)?)), 0x3EB => Ok(RecvCharacterPacket::ParamDataChunkRequest(ParamDataChunkRequest::from_bytes(data)?)),
0xE5 => Ok(RecvCharacterPacket::CharacterPreview(CharacterPreview::from_bytes(data)?)),
0xEC => Ok(RecvCharacterPacket::SetFlag(SetFlag::from_bytes(data)?)),
_ => Err(PacketParseError::WrongPacketForServerType) _ => Err(PacketParseError::WrongPacketForServerType)
} }
} }
@ -64,6 +68,8 @@ pub enum SendCharacterPacket {
GuildcardDataChunk(GuildcardDataChunk), GuildcardDataChunk(GuildcardDataChunk),
ParamDataHeader(ParamDataHeader), ParamDataHeader(ParamDataHeader),
ParamDataChunk(ParamDataChunk), ParamDataChunk(ParamDataChunk),
Timestamp(Timestamp),
ShipList(ShipList)
} }
impl SendServerPacket for SendCharacterPacket { impl SendServerPacket for SendCharacterPacket {
@ -79,6 +85,8 @@ impl SendServerPacket for SendCharacterPacket {
SendCharacterPacket::GuildcardDataChunk(pkt) => pkt.as_bytes(), SendCharacterPacket::GuildcardDataChunk(pkt) => pkt.as_bytes(),
SendCharacterPacket::ParamDataHeader(pkt) => pkt.as_bytes(), SendCharacterPacket::ParamDataHeader(pkt) => pkt.as_bytes(),
SendCharacterPacket::ParamDataChunk(pkt) => pkt.as_bytes(), SendCharacterPacket::ParamDataChunk(pkt) => pkt.as_bytes(),
SendCharacterPacket::Timestamp(pkt) => pkt.as_bytes(),
SendCharacterPacket::ShipList(pkt) => pkt.as_bytes(),
//SendLoginPacket::RedirectClient(pkt) => pkt.as_bytes(), //SendLoginPacket::RedirectClient(pkt) => pkt.as_bytes(),
} }
} }
@ -120,6 +128,7 @@ struct ClientState {
user: Option<UserAccount>, user: Option<UserAccount>,
characters: Option<[Option<Character>; 4]>, characters: Option<[Option<Character>; 4]>,
guildcard_data_buffer: Option<Vec<u8>>, guildcard_data_buffer: Option<Vec<u8>>,
security_data: [u8; 40],
} }
impl ClientState { impl ClientState {
@ -129,6 +138,7 @@ impl ClientState {
user: None, user: None,
characters: None, characters: None,
guildcard_data_buffer: None, guildcard_data_buffer: None,
security_data: [0; 40],
} }
} }
} }
@ -157,18 +167,46 @@ impl<DA: DataAccess> CharacterServerState<DA> {
let client = self.clients.get_mut(&id).ok_or(CharacterError::ClientNotFound(id))?; let client = self.clients.get_mut(&id).ok_or(CharacterError::ClientNotFound(id))?;
Ok(match get_login_status(&self.data_access, pkt) { Ok(match get_login_status(&self.data_access, pkt) {
Ok(user) => { Ok(user) => {
let mut response = LoginResponse::by_status(AccountStatus::Ok, pkt.security_data);
let mut response = LoginResponse::by_status(AccountStatus::Ok, [0; 40]);
response.guildcard = user.guildcard.map_or(0, |gc| gc) as u32; response.guildcard = user.guildcard.map_or(0, |gc| gc) as u32;
response.team_id = user.team_id.map_or(0, |ti| ti) as u32; response.team_id = user.team_id.map_or(0, |ti| ti) as u32;
client.user = Some(user); client.user = Some(user);
client.security_data = pkt.security_data;
vec![SendCharacterPacket::LoginResponse(response)] vec![SendCharacterPacket::LoginResponse(response)]
}, },
Err(err) => { Err(err) => {
vec![SendCharacterPacket::LoginResponse(LoginResponse::by_status(err, pkt.security_data))]
vec![SendCharacterPacket::LoginResponse(LoginResponse::by_status(err, [0; 40]))]
} }
}) })
} }
fn send_ship_list(&mut self, id: ClientId, pkt: &Login) -> Result<Vec<SendCharacterPacket>, CharacterError> {
Ok(vec![SendCharacterPacket::Timestamp(Timestamp::new(chrono::Utc::now())),
SendCharacterPacket::ShipList(ShipList {
flag: 0,
ships: vec![
ShipListEntry {
menu: 0,
item: 1,
flags: 0,
name: utf8_to_utf16_array!("Sona-Nyl", 0x11),
},
ShipListEntry {
menu: 0,
item: 2,
flags: 0,
name: utf8_to_utf16_array!("Dylath-Leen", 0x11),
},
ShipListEntry {
menu: 0,
item: 2,
flags: 0,
name: utf8_to_utf16_array!("Innsmouth", 0x11),
}
]
})])
}
fn get_settings(&mut self, id: ClientId) -> Result<Vec<SendCharacterPacket>, CharacterError> { fn get_settings(&mut self, id: ClientId) -> Result<Vec<SendCharacterPacket>, CharacterError> {
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 user = client.user.as_ref().unwrap(); let user = client.user.as_ref().unwrap();
@ -192,21 +230,38 @@ impl<DA: DataAccess> CharacterServerState<DA> {
client.characters = Some(self.data_access.get_characters_by_user(client.user.as_ref().unwrap())); client.characters = Some(self.data_access.get_characters_by_user(client.user.as_ref().unwrap()));
} }
let chars = client.characters.as_ref().unwrap();
Ok(if let Some(char) = &chars[select.slot as usize] {
vec![SendCharacterPacket::CharacterPreview(CharacterPreview {
flag: 0,
slot: select.slot,
character: char.character.as_select_screen(),
})]
if select.reason == 0 {
let chars = client.characters.as_ref().unwrap();
Ok(if let Some(char) = &chars[select.slot as usize] {
vec![SendCharacterPacket::CharacterPreview(CharacterPreview {
flag: 0,
slot: select.slot,
character: char.character.as_select_screen(),
})]
}
else {
vec![SendCharacterPacket::CharAck(CharAck {
flag: 0,
slot: select.slot,
code: 2,
})]
})
} }
else { else {
vec![SendCharacterPacket::CharAck(CharAck {
flag: 0,
slot: select.slot,
code: 2,
})]
})
let user = client.user.as_ref().unwrap();
client.security_data[0..4].clone_from_slice(&[1,3,3,7]);
client.security_data[4] = select.slot as u8;
client.security_data[5] = 1;
Ok(vec![SendCharacterPacket::LoginResponse(LoginResponse::by_char_select(user.guildcard.unwrap_or(1),
user.team_id.unwrap_or(1),
client.security_data)),
SendCharacterPacket::CharAck(CharAck {
flag: 0,
slot: select.slot,
code: 1,
})
])
}
} }
fn validate_checksum(&mut self) -> Vec<SendCharacterPacket> { fn validate_checksum(&mut self) -> Vec<SendCharacterPacket> {
@ -271,7 +326,12 @@ impl<DA: DataAccess> ServerState for CharacterServerState<DA> {
-> Result<Box<dyn Iterator<Item = (ClientId, SendCharacterPacket)>>, CharacterError> { -> Result<Box<dyn Iterator<Item = (ClientId, SendCharacterPacket)>>, CharacterError> {
Ok(match pkt { Ok(match pkt {
RecvCharacterPacket::Login(login) => { RecvCharacterPacket::Login(login) => {
Box::new(self.validate_login(id, login)?.into_iter().map(move |pkt| (id, pkt)))
if login.security_data[0..4] == [1,3,3,7] {
Box::new(self.send_ship_list(id, login)?.into_iter().map(move |pkt| (id, pkt)))
}
else {
Box::new(self.validate_login(id, login)?.into_iter().map(move |pkt| (id, pkt)))
}
}, },
RecvCharacterPacket::RequestSettings(_req) => { RecvCharacterPacket::RequestSettings(_req) => {
Box::new(self.get_settings(id)?.into_iter().map(move |pkt| (id, pkt))) Box::new(self.get_settings(id)?.into_iter().map(move |pkt| (id, pkt)))
@ -291,6 +351,13 @@ impl<DA: DataAccess> ServerState for CharacterServerState<DA> {
RecvCharacterPacket::ParamDataRequest(_request) => { RecvCharacterPacket::ParamDataRequest(_request) => {
Box::new(vec![SendCharacterPacket::ParamDataHeader(self.param_header.clone())].into_iter().map(move |pkt| (id, pkt))) Box::new(vec![SendCharacterPacket::ParamDataHeader(self.param_header.clone())].into_iter().map(move |pkt| (id, pkt)))
}, },
RecvCharacterPacket::SetFlag(flags) => {
let client = self.clients.get_mut(&id).ok_or(CharacterError::ClientNotFound(id))?;
let mut user = client.user.as_mut().unwrap();
user.flags = flags.flags;
self.data_access.set_user(&user);
Box::new(None.into_iter())
},
RecvCharacterPacket::ParamDataChunkRequest(_request) => { RecvCharacterPacket::ParamDataChunkRequest(_request) => {
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 chunk = client.param_index; let chunk = client.param_index;
@ -309,6 +376,36 @@ impl<DA: DataAccess> ServerState for CharacterServerState<DA> {
data: data, data: data,
} }
)].into_iter().map(move |pkt| (id, pkt))) )].into_iter().map(move |pkt| (id, pkt)))
},
RecvCharacterPacket::CharacterPreview(preview) => {
let client = self.clients.get_mut(&id).ok_or(CharacterError::ClientNotFound(id))?;
let mut user = client.user.as_mut().unwrap();
if user.flags == USERFLAG_NEWCHAR {
let char = Character {
id: 9,
user_id: user.id,
character: preview.character.as_character()
};
self.data_access.set_character_by_user(&user, preview.slot, char);
}
if user.flags == USERFLAG_DRESSINGROOM {
// TODO: dressing room stuff
}
client.security_data[0..4].clone_from_slice(&[1,3,3,7]);
client.security_data[4] = preview.slot as u8;
client.security_data[5] = 1;
user.flags = 0;
self.data_access.set_user(&user);
Box::new(vec![SendCharacterPacket::LoginResponse(LoginResponse::by_char_select(user.guildcard.unwrap_or(0),
user.team_id.unwrap_or(0),
client.security_data)),
SendCharacterPacket::CharAck(CharAck {
flag: 0,
slot: preview.slot,
code: 0
})
].into_iter().map(move |pkt| (id, pkt)))
} }
}) })
} }
@ -347,6 +444,7 @@ mod test {
banned: false, banned: false,
muted_until: SystemTime::now(), muted_until: SystemTime::now(),
created_at: SystemTime::now(), created_at: SystemTime::now(),
flags: 0,
}); });
server.clients.insert(ClientId(5), clientstate); server.clients.insert(ClientId(5), clientstate);

60
src/login/main.rs

@ -4,6 +4,7 @@ mod dataaccess;
mod entities; mod entities;
use std::thread; use std::thread;
use std::collections::HashMap;
use bcrypt; use bcrypt;
@ -22,12 +23,17 @@ use std::time::SystemTime;
#[derive(Clone)] #[derive(Clone)]
struct LoginStubData { struct LoginStubData {
users: HashMap<String, UserAccount>,
characters: [Option<Character> ;4],
} }
impl DataAccess for LoginStubData {
fn get_user_by_name(&self, username: String) -> Option<UserAccount> {
if username.as_str() == "hi" {
Some(UserAccount {
impl LoginStubData {
fn new() -> LoginStubData {
let mut c = pso_character::Character::default();
c.name = utf8_to_utf16_array!("Test Char", 16);
let mut users = HashMap::new();
users.insert("hi".to_string(), UserAccount {
id: 1, id: 1,
username: "hi".to_owned(), username: "hi".to_owned(),
password: bcrypt::hash("qwer", 5).unwrap(), password: bcrypt::hash("qwer", 5).unwrap(),
@ -36,12 +42,26 @@ impl DataAccess for LoginStubData {
banned: false, banned: false,
muted_until: SystemTime::now(), muted_until: SystemTime::now(),
created_at: SystemTime::now(), created_at: SystemTime::now(),
})
}
else {
None
flags: 0,
});
LoginStubData {
users: users,
characters: [Some(Character {
id: 1,
user_id: 1,
character: c,
}),
None, None, None]
} }
} }
}
impl DataAccess for LoginStubData {
fn get_user_by_name(&self, username: String) -> Option<UserAccount> {
self.users.get(&username).map(|user| user.clone())
}
fn get_user_settings_by_user(&self, user: &UserAccount) -> Option<UserSettings> { fn get_user_settings_by_user(&self, user: &UserAccount) -> Option<UserSettings> {
Some(UserSettings { Some(UserSettings {
@ -51,15 +71,16 @@ impl DataAccess for LoginStubData {
}) })
} }
fn get_characters_by_user(&self, user: &UserAccount) -> [Option<Character>; 4] {
let mut c = pso_character::Character::default();
c.name = utf8_to_utf16_array!("Test Char", 16);
[Some(Character {
id: 1,
user_id: user.id,
character: c,
}),
None, None, None]
fn set_user(&mut self, user: &UserAccount) {
self.users.insert(user.username.clone(), user.clone());
}
fn get_characters_by_user(&self, _user: &UserAccount) -> [Option<Character>; 4] {
self.characters
}
fn set_character_by_user(&mut self, _user: &UserAccount, slot: u32, char: Character) {
self.characters[slot as usize] = Some(char);
} }
fn get_guild_card_data_by_user(&self, user: &UserAccount) -> GuildCardData { fn get_guild_card_data_by_user(&self, user: &UserAccount) -> GuildCardData {
@ -74,13 +95,12 @@ impl DataAccess for LoginStubData {
fn main() { fn main() {
println!("[login+character] starting server"); println!("[login+character] starting server");
// TODO: character mainloop
let auth_thread = thread::spawn(|| { let auth_thread = thread::spawn(|| {
let auth_state = LoginServerState::new(LoginStubData {});
let auth_state = LoginServerState::new(LoginStubData::new());
elseware::common::mainloop::mainloop(auth_state, login::LOGIN_PORT); elseware::common::mainloop::mainloop(auth_state, login::LOGIN_PORT);
}); });
let char_thread = thread::spawn(|| { let char_thread = thread::spawn(|| {
let char_state = CharacterServerState::new(LoginStubData {});
let char_state = CharacterServerState::new(LoginStubData::new());
elseware::common::mainloop::mainloop(char_state, character::CHARACTER_PORT); elseware::common::mainloop::mainloop(char_state, character::CHARACTER_PORT);
}); });

Loading…
Cancel
Save