use std::collections; use std::sync::mpsc; use mio; use config; use control::{ControlRequest, ControlResponse}; use proto::{Response, Request}; use proto::server::*; enum RoomKind { Public, PrivateOwned, PrivateOther, } struct Room { kind: RoomKind, user_count: usize, operated: bool, } #[derive(Debug)] enum IncomingMessage { ServerResponse(ServerResponse), ControlRequest(ControlRequest), } #[derive(Debug, Clone, Copy)] enum State { NotLoggedIn, LoggingIn, LoggedIn, } pub struct Client { state: State, proto_tx: mio::Sender, proto_rx: mpsc::Receiver, control_tx: mpsc::Sender, control_rx: mpsc::Receiver, rooms: collections::HashMap, privileged_users: collections::HashSet, } impl Client { pub fn new( proto_tx: mio::Sender, proto_rx: mpsc::Receiver, control_tx: mpsc::Sender, control_rx: mpsc::Receiver) -> Self { Client { state: State::NotLoggedIn, proto_tx: proto_tx, proto_rx: proto_rx, control_tx: control_tx, control_rx: control_rx, rooms: collections::HashMap::new(), privileged_users: collections::HashSet::new(), } } pub fn run(&mut self) { info!("Logging in..."); self.state = State::LoggingIn; let server_request = ServerRequest::LoginRequest(LoginRequest::new( config::USERNAME, config::PASSWORD, config::VER_MAJOR, config::VER_MINOR, ).unwrap()); self.proto_tx.send(Request::ServerRequest(server_request)).unwrap(); loop { match self.recv() { IncomingMessage::ServerResponse(response) => self.handle_server_response(response), IncomingMessage::ControlRequest(request) => self.handle_control_request(request), } } } fn recv(&mut self) -> IncomingMessage { let proto_rx = &self.proto_rx; let control_rx = &self.control_rx; select! { result = proto_rx.recv() => match result.unwrap() { Response::ServerResponse(server_response) => IncomingMessage::ServerResponse(server_response), }, result = control_rx.recv() => IncomingMessage::ControlRequest(result.unwrap()) } } fn handle_control_request(&mut self, request: ControlRequest) { match request { _ => { error!("Unhandled control request: {:?}", request); }, } } fn handle_server_response(&mut self, response: ServerResponse) { match response { ServerResponse::LoginResponse(response) => self.handle_login_response(response), ServerResponse::PrivilegedUsersResponse(response) => self.handle_privileged_users_response(response), ServerResponse::RoomListResponse(response) => self.handle_room_list_response(response), ServerResponse::UnknownResponse(code, packet) => warn!("Unknown response: code {}, size {}", code, packet.bytes_remaining()), response => warn!("Unhandled response: {:?}", response), } } fn handle_login_response(&mut self, login: LoginResponse) { if let State::LoggingIn = self.state { match login { LoginResponse::LoginOk { motd, ip, password_md5_opt } => { self.state = State::LoggedIn; info!("Login successful!"); info!("MOTD: \"{}\"", motd); info!("External IP address: {}", ip); match password_md5_opt { Some(_) => { info!(concat!( "Connected to official server ", "as official client")); }, None => info!(concat!( "Connected to official server ", "as unofficial client")), } }, LoginResponse::LoginFail { reason } => { self.state = State::NotLoggedIn; error!("Login failed: \"{}\"", reason); } } } else { error!("Received unexpected login response, state = {:?}", self.state); } } fn handle_room_list_response( &mut self, mut response: RoomListResponse) { self.rooms.clear(); for (name, user_count) in response.rooms.drain(..) { self.rooms.insert(name, Room{ kind: RoomKind::Public, operated: false, user_count: user_count as usize, }); } for (name, user_count) in response.owned_private_rooms.drain(..) { let room = Room { kind: RoomKind::PrivateOwned, operated: false, user_count: user_count as usize, }; if let Some(_) = self.rooms.insert(name, room) { error!("Room is both normal and owned_private"); } } for (name, user_count) in response.other_private_rooms.drain(..) { let room = Room { kind: RoomKind::PrivateOther, operated: false, user_count: user_count as usize, }; if let Some(_) = self.rooms.insert(name, room) { error!("Room is both normal and other_private"); } } for name in response.operated_private_room_names.drain(..) { match self.rooms.get_mut(&name) { None => error!("Room {} is operated but does not exist", name), Some(room) => room.operated = true, } } } fn handle_privileged_users_response( &mut self, mut response: PrivilegedUsersResponse) { self.privileged_users.clear(); for username in response.users.drain(..) { self.privileged_users.insert(username); } } }