diff --git a/src/common/client.rs b/src/common/client.rs
new file mode 100644
index 0000000..ad3b378
--- /dev/null
+++ b/src/common/client.rs
@@ -0,0 +1,91 @@
+use libpso::crypto::{PSOCipher, NullCipher};
+use libpso::{PSOPacket, PacketParseError};
+
+use crate::common::serverstate::{ServerState, ServerPacket, OnConnect};
+use crate::common::network::{send_packet, recv_packet};
+
+use std::net;
+use mio::tcp::TcpStream;
+use mio::{Poll, Events, Token, Ready, PollOpt};
+
+pub struct Client
{
+ running: bool,
+ socket: mio::tcp::TcpStream,
+ cipher_in: Box,
+ cipher_out: Box,
+ state: Box>,
+}
+
+impl Client {
+ pub fn new(socket: net::TcpStream, state: Box>) -> Client {
+ let mut client = Client {
+ running: true,
+ socket: mio::tcp::TcpStream::from_stream(socket).expect("could not convert socket to nonblocking"),
+ cipher_in: Box::new(NullCipher {}),
+ cipher_out: Box::new(NullCipher {}),
+ state: state,
+ };
+
+ for task in client.state.on_connect() {
+ match task {
+ OnConnect::Packet(pkt) => client.send(&*pkt),
+ OnConnect::Cipher((cipher_in, cipher_out)) => {
+ client.cipher_in = cipher_in;
+ client.cipher_out = cipher_out;
+ },
+ }
+ }
+
+ client
+ }
+
+ fn send(&mut self, pkt: &dyn PSOPacket) {
+ match send_packet(&mut self.socket, &mut *self.cipher_out, pkt) {
+ Ok(_) => {
+ println!("[patch] send ({:?}): {:?}", self.socket, pkt);
+ },
+ Err(err) => {
+ println!("[patch] error sending packet to {:?}: {:?}", self.socket, err);
+ self.running = false;
+ }
+ }
+ }
+
+ pub fn io_loop(mut self) {
+ let poll = mio::Poll::new().unwrap();
+ poll.register(&self.socket, Token(0), Ready::readable(), PollOpt::edge()).unwrap();
+
+
+ let mut events = Events::with_capacity(1024);
+ loop {
+ poll.poll(&mut events, None).unwrap();
+
+ for event in &events{
+ println!("event! {:?}", event);
+ if event.token() == Token(0) {
+ loop {
+ let pkt = recv_packet(&mut self.socket, &mut *self.cipher_in)
+ .and_then(|pkt| {
+ P::from_bytes(&pkt)
+ .map_err(|err| err.into())
+ });
+
+ match pkt {
+ Ok(pkt) => {
+ let response = self.state.handle(&pkt);
+ for r in response {
+ self.send(&*r);
+ }
+ },
+ Err(err) => {
+ println!("error recv-ing packet with {:?}: {:?}", self.socket, err);
+ break;
+ }
+ }
+ }
+ }
+ }
+ }
+
+ }
+}
diff --git a/src/common/mod.rs b/src/common/mod.rs
index a6bb89e..29ec690 100644
--- a/src/common/mod.rs
+++ b/src/common/mod.rs
@@ -1,4 +1,5 @@
pub mod cipherkeys;
pub mod network;
-
+pub mod serverstate;
+pub mod client;
diff --git a/src/common/network.rs b/src/common/network.rs
index c1976f0..f110462 100644
--- a/src/common/network.rs
+++ b/src/common/network.rs
@@ -36,7 +36,6 @@ pub fn recv_packet(socket: &mut T, cipher: &mut dyn PSOCipher) -> Resul
let mut offset = 0;
while offset < cipher.header_size() {
let diff = socket.read(&mut size_buf[offset..])?;
- println!("! {} {:?}", diff, size_buf);
if diff == 0 {
return Err(PacketNetworkError::ClientDisconnected);
}
@@ -76,6 +75,8 @@ pub fn recv_packet(socket: &mut T, cipher: &mut dyn PSOCipher) -> Resul
Ok(full_buf)
}
+// TODO: this fails horribly when the send buffer is full
+// if it didnt send it all make a mio poll
pub fn send_packet(socket: &mut T, cipher: &mut dyn PSOCipher, pkt: &dyn PSOPacket) -> Result<(), PacketNetworkError> {
let buf = pkt.as_bytes();
//println!("[send]: {:X?}", buf);
diff --git a/src/common/serverstate.rs b/src/common/serverstate.rs
new file mode 100644
index 0000000..affb31f
--- /dev/null
+++ b/src/common/serverstate.rs
@@ -0,0 +1,23 @@
+use libpso::{PSOPacket, PacketParseError};
+use libpso::crypto::PSOCipher;
+
+
+pub enum OnConnect {
+ Packet(Box),
+ Cipher((Box, Box)),
+}
+
+pub trait ServerPacket: Sized {
+ fn from_bytes(data: &Vec) -> Result;
+}
+
+pub trait ServerState {
+ type Packet: ServerPacket;
+ type PacketError;
+
+ //fn handle(&mut self, pkt: &Self::Packet) -> Result>, Self::PacketError>;
+ fn on_connect(&mut self) -> Vec;
+ //fn handle(&mut self, pkt: &Self::Packet) -> Result>>, Self::PacketError>;
+ fn handle(&mut self, pkt: &Self::Packet) -> Box>>;
+}
+
diff --git a/src/patch/main.rs b/src/patch/main.rs
index 8760a98..91de59f 100644
--- a/src/patch/main.rs
+++ b/src/patch/main.rs
@@ -12,9 +12,10 @@ use rand::{Rng, RngCore};
use crc::{crc32, Hasher32};
use libpso::{PacketParseError, PSOPacket};
use libpso::packet::patch::*;
-use libpso::crypto::{CipherError, PSOCipher, NullCipher};
use libpso::crypto::pc::PSOPCCipher;
use elseware::common::network::{send_packet, recv_packet, PacketNetworkError};
+use elseware::common::client::Client;
+use elseware::common::serverstate::{ServerPacket, ServerState, OnConnect};
const PATCH_PORT: u16 = 11000;
@@ -43,25 +44,6 @@ impl From for PatchError {
}
}
-#[derive(Debug)]
-pub enum PatchPacket {
- PatchWelcomeReply(PatchWelcomeReply),
- LoginReply(LoginReply),
- FileInfoReply(FileInfoReply),
- FileInfoListEnd(FileInfoListEnd),
-}
-
-impl PatchPacket {
- pub fn from_bytes(data: &Vec) -> Result {
- match data[2] {
- 0x02 => Ok(PatchPacket::PatchWelcomeReply(PatchWelcomeReply::from_bytes(data)?)),
- 0x04 => Ok(PatchPacket::LoginReply(LoginReply::from_bytes(data)?)),
- 0x0F => Ok(PatchPacket::FileInfoReply(FileInfoReply::from_bytes(data)?)),
- 0x10 => Ok(PatchPacket::FileInfoListEnd(FileInfoListEnd::from_bytes(data)?)),
- _ => Err(PacketParseError::WrongPacketForServerType)
- }
- }
-}
#[derive(Debug, Clone)]
struct PatchFile {
@@ -70,7 +52,6 @@ struct PatchFile {
size: u32,
}
-
enum PatchTreeIterItem {
Directory(PathBuf),
File(PathBuf, u32),
@@ -109,50 +90,87 @@ impl PatchFileTree {
}
+#[derive(Debug)]
+pub enum PatchPacket {
+ PatchWelcomeReply(PatchWelcomeReply),
+ LoginReply(LoginReply),
+ FileInfoReply(FileInfoReply),
+ FileInfoListEnd(FileInfoListEnd),
+}
+
+impl ServerPacket for PatchPacket {
+ fn from_bytes(data: &Vec) -> Result {
+ match data[2] {
+ 0x02 => Ok(PatchPacket::PatchWelcomeReply(PatchWelcomeReply::from_bytes(data)?)),
+ 0x04 => Ok(PatchPacket::LoginReply(LoginReply::from_bytes(data)?)),
+ 0x0F => Ok(PatchPacket::FileInfoReply(FileInfoReply::from_bytes(data)?)),
+ 0x10 => Ok(PatchPacket::FileInfoListEnd(FileInfoListEnd::from_bytes(data)?)),
+ _ => Err(PacketParseError::WrongPacketForServerType)
+ }
+ }
+}
-struct Client {
- running: bool,
- key_in: u32,
- key_out: u32,
- socket: mio::tcp::TcpStream,
- cipher_in: Box,
- cipher_out: Box,
+struct PatchServerState {
patch_file_tree: PatchFileTree,
patch_file_lookup: HashMap,
patch_file_info: Vec,
}
-impl Client {
- fn new(socket: net::TcpStream, patch_file_tree: PatchFileTree, patch_file_lookup: HashMap) -> Client {
+impl PatchServerState {
+ fn new(patch_file_tree: PatchFileTree, patch_file_lookup: HashMap) -> PatchServerState {
+ PatchServerState {
+ patch_file_tree: patch_file_tree,
+ patch_file_lookup: patch_file_lookup,
+ patch_file_info: Vec::new(),
+ }
+ }
+}
+
+impl ServerState for PatchServerState {
+ type Packet = PatchPacket;
+ type PacketError = PatchError;
+
+ fn on_connect(&mut self) -> Vec {
let mut rng = rand::thread_rng();
let key_in: u32 = rng.gen();
let key_out: u32 = rng.gen();
- Client {
- running: true,
- key_in: key_in,
- key_out: key_out,
- socket: mio::tcp::TcpStream::from_stream(socket).expect("could not convert socket to nonblocking"),
- cipher_in: Box::new(NullCipher {}),
- cipher_out: Box::new(NullCipher {}),
- patch_file_tree: patch_file_tree,
- patch_file_lookup: patch_file_lookup,
- patch_file_info: Vec::new()
- }
+ vec![OnConnect::Packet(Box::new(PatchWelcome::new(key_out, key_in))),
+ OnConnect::Cipher((Box::new(PSOPCCipher::new(key_in)), Box::new(PSOPCCipher::new(key_out))))
+ ]
}
- fn send(&mut self, pkt: &dyn PSOPacket) {
- match send_packet(&mut self.socket, &mut *self.cipher_out, pkt) {
- Ok(_) => {
- println!("[patch] send ({:?}): {:?}", self.socket, pkt);
+ fn handle(&mut self, pkt: &PatchPacket) -> Box>> {
+ match pkt {
+ PatchPacket::PatchWelcomeReply(_pkt) => {
+ let p: Vec> = vec![Box::new(RequestLogin {})];
+ Box::new(p.into_iter())
+ },
+ PatchPacket::LoginReply(_pkt) => {
+ let mut p: Vec> = vec![Box::new(Message::new("hello player".to_string()))];
+ p.append(&mut get_file_list_packets(&self.patch_file_tree));
+ p.push(Box::new(PatchEndList {}));
+ Box::new(p.into_iter())
+ },
+ PatchPacket::FileInfoReply(pkt) => {
+ self.patch_file_info.push(pkt.clone());
+ Box::new(None.into_iter())
},
- Err(err) => {
- println!("[patch] error sending packet to {:?}: {:?}", self.socket, err);
- self.running = false;
+ PatchPacket::FileInfoListEnd(_pkt) => {
+ let need_update = self.patch_file_info.iter()
+ .filter(|file_info| does_file_need_updating(file_info, &self.patch_file_lookup))
+ .collect::>();
+
+ let total_size = need_update.iter().fold(0, |a, file_info| a + file_info.size);
+ let total_files = need_update.len() as u32;
+
+ let p: Vec> = vec![Box::new(FilesToPatchMetadata::new(total_size, total_files)),
+ Box::new(PatchStartList {}),
+ ];
+ Box::new(p.into_iter().chain(SendFileIterator::new(&self)))
}
}
}
-
}
fn load_patch_dir(basedir: &str, patchbase: &str, file_ids: &mut HashMap) -> PatchFileTree {
@@ -192,7 +210,6 @@ fn generate_patch_tree(basedir: &str) -> (PatchFileTree, HashMap
}
-// TODO: this should be a function that takes client and sends packets instead of returning a vec of packets
fn get_file_list_packets(patch_file_tree: &PatchFileTree) -> Vec> {
let mut pkts: Vec> = Vec::new();
@@ -213,20 +230,6 @@ fn get_file_list_packets(patch_file_tree: &PatchFileTree) -> Vec Result<(), PatchError> {
- client.send(&PatchStartList {});
-
- let pkts = get_file_list_packets(&client.patch_file_tree);
- for pkt in pkts {
- client.send(&*pkt);
- }
-
- client.send(&PatchEndList {});
-
- Ok(())
-}
-
fn get_checksum_and_size(path: &PathBuf) -> Result<(u32, u32), PatchError> {
let file = fs::File::open(path)?;
let size = file.metadata().unwrap().len();
@@ -248,124 +251,92 @@ fn does_file_need_updating(file_info: &FileInfoReply, patch_file_lookup: &HashMa
patch_file.checksum != file_info.checksum || patch_file.size != file_info.size
}
-fn send_file(client: &mut Client, actual_path: &PathBuf, patch_path: &PathBuf, id: u32) -> Result<(), PatchError>{
- let file = fs::File::open(actual_path)?;
- let size = file.metadata().unwrap().len();
-
- client.send(&StartFileSend::new(patch_path.to_str().unwrap(), size as u32, id));
- let mut buf = [0u8; PATCH_FILE_CHUNK_SIZE as usize];
- let mut reader = io::BufReader::new(file);
- let mut chunk_num = 0;
- while let Ok(len) = reader.read(&mut buf) {
- if len == 0 {
- break;
- }
-
- let mut crc = crc32::Digest::new(crc32::IEEE);
- crc.write(&buf[0..len]);
-
- let pkt = FileSend {
- chunk_num: chunk_num,
- checksum: crc.sum32(),
- chunk_size: len as u32,
- buffer: buf
- };
-
- client.send(&pkt);
-
- chunk_num += 1;
- }
-
- client.send(&EndFileSend::new());
- Ok(())
+struct SendFileIterator {
+ done: bool,
+ file_iter: Box>,
+ file_ids_to_update: HashSet,
+ patch_file_lookup: HashMap,
+ current_file: Option>,
+ chunk_num: u32,
}
-
-fn send_file_data(client: &mut Client) -> Result<(), PatchError> {
- let need_update = client.patch_file_info.iter()
- .filter(|file_info| does_file_need_updating(file_info, &client.patch_file_lookup))
- .collect::>();
-
- let total_size = need_update.iter().fold(0, |a, file_info| a + file_info.size);
- let total_files = need_update.len() as u32;
-
- let file_ids_to_update = need_update.iter().map(|k| k.id).collect::>();
-
- client.send(&FilesToPatchMetadata::new(total_size, total_files));
- client.send(&PatchStartList {});
-
- for file in client.patch_file_tree.flatten() {
- match file {
- PatchTreeIterItem::Directory(path) => {
- client.send(&ChangeDirectory::new(path.to_str().unwrap()));
- },
- PatchTreeIterItem::File(path, id) => {
- if file_ids_to_update.contains(&id) {
- let patch_file = client.patch_file_lookup.get(&id).unwrap().clone();
- send_file(client, &patch_file.path, &path, id)?;
- }
- },
- PatchTreeIterItem::UpDirectory => {
- client.send(&UpOneDirectory {});
- }
+impl SendFileIterator {
+ fn new(state: &PatchServerState) -> SendFileIterator {
+ let need_update = state.patch_file_info.iter()
+ .filter(|file_info| does_file_need_updating(file_info, &state.patch_file_lookup))
+ .collect::>();
+ let file_ids_to_update = need_update.iter().map(|k| k.id).collect::>();
+
+ SendFileIterator {
+ done: false,
+ file_ids_to_update: file_ids_to_update,
+ patch_file_lookup: state.patch_file_lookup.clone(),
+ file_iter: Box::new(state.patch_file_tree.flatten().into_iter()),
+ current_file: None,
+ chunk_num: 0,
}
-
}
- client.send(&FinalizePatching {});
-
- Ok(())
}
+impl Iterator for SendFileIterator {
+ type Item = Box;
-fn handle_packet(client: &mut Client, pkt: PatchPacket) -> Result<(), PatchError> {
- println!("[patch] recv({:?}): {:?}", client.socket, pkt);
-
- match pkt {
- PatchPacket::PatchWelcomeReply(_pkt) => {
- client.send(&RequestLogin {});
- },
- PatchPacket::LoginReply(_pkt) => {
- client.send(&Message::new("hello player".to_string()));
- send_file_list(client)?;
- },
- PatchPacket::FileInfoReply(pkt) => {
- client.patch_file_info.push(pkt);
- },
- PatchPacket::FileInfoListEnd(_pkt) => {
- send_file_data(client)?;
+ fn next(&mut self) -> Option {
+ if self.done {
+ return None;
}
- }
- Ok(())
-}
-
-fn client_loop(mut client: Client) {
- let poll = mio::Poll::new().unwrap();
- poll.register(&client.socket, Token(0), Ready::readable(), PollOpt::edge()).unwrap();
-
- let mut events = Events::with_capacity(1024);
- loop {
- poll.poll(&mut events, None).unwrap();
-
- for event in &events{
- println!("event! {:?}", event);
- if event.token() == Token(0) {
- loop {
- let pkt = recv_packet(&mut client.socket, &mut *client.cipher_in)
- .and_then(|pkt| {
- PatchPacket::from_bytes(&pkt)
- .map_err(|err| err.into())
- });
-
- match pkt {
- Ok(pkt) => {
- handle_packet(&mut client, pkt).expect("could not handle packet");
- },
- Err(err) => {
- println!("[patch] error recv-ing packet with {:?}: {:?}", client.socket, err);
- break;
+ match self.current_file {
+ Some(ref mut file) => {
+ let mut buf = [0u8; PATCH_FILE_CHUNK_SIZE as usize];
+ let len = file.read(&mut buf).unwrap();
+ if len == 0 {
+ self.current_file = None;
+ self.chunk_num = 0;
+ Some(Box::new(EndFileSend::new()))
+ }
+ else {
+ let mut crc = crc32::Digest::new(crc32::IEEE);
+ crc.write(&buf[0..len]);
+ let pkt = FileSend {
+ chunk_num: self.chunk_num,
+ checksum: crc.sum32(),
+ chunk_size: len as u32,
+ buffer: buf,
+ };
+ self.chunk_num += 1;
+ Some(Box::new(pkt))
+ }
+ },
+ None => {
+ match self.file_iter.next() {
+ Some(next_file) => {
+ match next_file {
+ PatchTreeIterItem::Directory(path) => {
+ Some(Box::new(ChangeDirectory::new(path.to_str().unwrap())))
+ },
+ PatchTreeIterItem::File(path, id) => {
+ if self.file_ids_to_update.contains(&id) {
+ let patch_file = self.patch_file_lookup.get(&id).unwrap();
+ let file = fs::File::open(&patch_file.path).unwrap();
+ let size = file.metadata().unwrap().len();
+ self.current_file = Some(io::BufReader::new(file));
+ Some(Box::new(StartFileSend::new(path.to_str().unwrap(), size as u32, id)))
+ }
+ else {
+ self.next()
+ }
+ },
+ PatchTreeIterItem::UpDirectory => {
+ Some(Box::new(UpOneDirectory {}))
+ },
}
+ },
+ None => {
+ self.current_file = None;
+ self.done = true;
+ Some(Box::new(FinalizePatching {}))
}
}
}
@@ -373,16 +344,16 @@ fn client_loop(mut client: Client) {
}
}
+
+
fn new_client(socket: net::TcpStream, patch_file_tree: PatchFileTree, patch_file_lookup: HashMap) {
- let mut client = Client::new(socket, patch_file_tree, patch_file_lookup);
- let welcome_pkt = PatchWelcome::new(client.key_out, client.key_in);
- client.send(&welcome_pkt);
- client.cipher_in = Box::new(PSOPCCipher::new(client.key_in));
- client.cipher_out = Box::new(PSOPCCipher::new(client.key_out));
- client_loop(client);
+ let state = PatchServerState::new(patch_file_tree, patch_file_lookup);
+ let client = Client::new(socket, Box::new(state));
+ client.io_loop();
}
+
fn main() {
println!("[patch] starting server");