Solstice client.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 

212 lines
6.4 KiB

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<Request>,
proto_rx: mpsc::Receiver<Response>,
control_tx: mpsc::Sender<ControlResponse>,
control_rx: mpsc::Receiver<ControlRequest>,
rooms: collections::HashMap<String, Room>,
privileged_users: collections::HashSet<String>,
}
impl Client {
pub fn new(
proto_tx: mio::Sender<Request>,
proto_rx: mpsc::Receiver<Response>,
control_tx: mpsc::Sender<ControlResponse>,
control_rx: mpsc::Receiver<ControlRequest>)
-> 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);
}
}
}