|
|
@ -10,11 +10,11 @@ use libpso::crypto::bb::PSOBBCipher; |
|
|
|
|
|
|
|
use elseware::common::cipherkeys::{ELSEWHERE_PRIVATE_KEY, ELSEWHERE_PARRAY};
|
|
|
|
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::login::get_login_status;
|
|
|
|
use crate::entities::{UserAccount, Character};
|
|
|
|
use crate::entities::{UserAccount, Character, USERFLAG_NEWCHAR, USERFLAG_DRESSINGROOM};
|
|
|
|
|
|
|
|
pub const CHARACTER_PORT: u16 = 12001;
|
|
|
|
|
|
|
@ -33,6 +33,8 @@ pub enum RecvCharacterPacket { |
|
|
|
GuildcardDataChunkRequest(GuildcardDataChunkRequest),
|
|
|
|
ParamDataRequest(ParamDataRequest),
|
|
|
|
ParamDataChunkRequest(ParamDataChunkRequest),
|
|
|
|
CharacterPreview(CharacterPreview),
|
|
|
|
SetFlag(SetFlag)
|
|
|
|
}
|
|
|
|
|
|
|
|
impl RecvServerPacket for RecvCharacterPacket {
|
|
|
@ -46,6 +48,8 @@ impl RecvServerPacket for RecvCharacterPacket { |
|
|
|
0x3DC => Ok(RecvCharacterPacket::GuildcardDataChunkRequest(GuildcardDataChunkRequest::from_bytes(data)?)),
|
|
|
|
0x4EB => Ok(RecvCharacterPacket::ParamDataRequest(ParamDataRequest::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)
|
|
|
|
}
|
|
|
|
}
|
|
|
@ -64,6 +68,8 @@ pub enum SendCharacterPacket { |
|
|
|
GuildcardDataChunk(GuildcardDataChunk),
|
|
|
|
ParamDataHeader(ParamDataHeader),
|
|
|
|
ParamDataChunk(ParamDataChunk),
|
|
|
|
Timestamp(Timestamp),
|
|
|
|
ShipList(ShipList)
|
|
|
|
}
|
|
|
|
|
|
|
|
impl SendServerPacket for SendCharacterPacket {
|
|
|
@ -79,6 +85,8 @@ impl SendServerPacket for SendCharacterPacket { |
|
|
|
SendCharacterPacket::GuildcardDataChunk(pkt) => pkt.as_bytes(),
|
|
|
|
SendCharacterPacket::ParamDataHeader(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(),
|
|
|
|
}
|
|
|
|
}
|
|
|
@ -120,6 +128,7 @@ struct ClientState { |
|
|
|
user: Option<UserAccount>,
|
|
|
|
characters: Option<[Option<Character>; 4]>,
|
|
|
|
guildcard_data_buffer: Option<Vec<u8>>,
|
|
|
|
security_data: [u8; 40],
|
|
|
|
}
|
|
|
|
|
|
|
|
impl ClientState {
|
|
|
@ -129,6 +138,7 @@ impl ClientState { |
|
|
|
user: None,
|
|
|
|
characters: 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))?;
|
|
|
|
Ok(match get_login_status(&self.data_access, pkt) {
|
|
|
|
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.team_id = user.team_id.map_or(0, |ti| ti) as u32;
|
|
|
|
client.user = Some(user);
|
|
|
|
client.security_data = pkt.security_data;
|
|
|
|
vec![SendCharacterPacket::LoginResponse(response)]
|
|
|
|
},
|
|
|
|
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> {
|
|
|
|
let client = self.clients.get_mut(&id).ok_or(CharacterError::ClientNotFound(id))?;
|
|
|
|
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()));
|
|
|
|
}
|
|
|
|
|
|
|
|
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 {
|
|
|
|
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> {
|
|
|
@ -271,7 +326,12 @@ impl<DA: DataAccess> ServerState for CharacterServerState<DA> { |
|
|
|
-> Result<Box<dyn Iterator<Item = (ClientId, SendCharacterPacket)>>, CharacterError> {
|
|
|
|
Ok(match pkt {
|
|
|
|
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) => {
|
|
|
|
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) => {
|
|
|
|
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) => {
|
|
|
|
let client = self.clients.get_mut(&id).ok_or(CharacterError::ClientNotFound(id))?;
|
|
|
|
let chunk = client.param_index;
|
|
|
@ -309,6 +376,36 @@ impl<DA: DataAccess> ServerState for CharacterServerState<DA> { |
|
|
|
data: data,
|
|
|
|
}
|
|
|
|
)].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,
|
|
|
|
muted_until: SystemTime::now(),
|
|
|
|
created_at: SystemTime::now(),
|
|
|
|
flags: 0,
|
|
|
|
});
|
|
|
|
server.clients.insert(ClientId(5), clientstate);
|
|
|
|
|
|
|
|