ship list logic
This commit is contained in:
parent
c1b40f63e1
commit
fc7b15c129
@ -19,4 +19,9 @@ mio = "0.6"
|
|||||||
mio-extras = "2.0.5"
|
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" }
|
@ -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();
|
if select.reason == 0 {
|
||||||
Ok(if let Some(char) = &chars[select.slot as usize] {
|
let chars = client.characters.as_ref().unwrap();
|
||||||
vec![SendCharacterPacket::CharacterPreview(CharacterPreview {
|
Ok(if let Some(char) = &chars[select.slot as usize] {
|
||||||
flag: 0,
|
vec![SendCharacterPacket::CharacterPreview(CharacterPreview {
|
||||||
slot: select.slot,
|
flag: 0,
|
||||||
character: char.character.as_select_screen(),
|
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 {
|
let user = client.user.as_ref().unwrap();
|
||||||
flag: 0,
|
client.security_data[0..4].clone_from_slice(&[1,3,3,7]);
|
||||||
slot: select.slot,
|
client.security_data[4] = select.slot as u8;
|
||||||
code: 2,
|
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);
|
||||||
|
|
||||||
|
@ -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 {
|
impl LoginStubData {
|
||||||
fn get_user_by_name(&self, username: String) -> Option<UserAccount> {
|
fn new() -> LoginStubData {
|
||||||
if username.as_str() == "hi" {
|
let mut c = pso_character::Character::default();
|
||||||
Some(UserAccount {
|
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(),
|
||||||
})
|
flags: 0,
|
||||||
}
|
});
|
||||||
else {
|
|
||||||
None
|
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] {
|
fn set_user(&mut self, user: &UserAccount) {
|
||||||
let mut c = pso_character::Character::default();
|
self.users.insert(user.username.clone(), user.clone());
|
||||||
c.name = utf8_to_utf16_array!("Test Char", 16);
|
}
|
||||||
[Some(Character {
|
|
||||||
id: 1,
|
fn get_characters_by_user(&self, _user: &UserAccount) -> [Option<Character>; 4] {
|
||||||
user_id: user.id,
|
self.characters
|
||||||
character: c,
|
}
|
||||||
}),
|
|
||||||
None, None, None]
|
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…
x
Reference in New Issue
Block a user