Browse Source

Add preliminary support for joining rooms.

wip
Titouan Rigoudy 9 years ago
parent
commit
2d2cc9851d
6 changed files with 152 additions and 33 deletions
  1. +58
    -2
      src/client.rs
  2. +9
    -3
      src/control/controller.rs
  3. +3
    -0
      src/handler.rs
  4. +1
    -0
      src/main.rs
  5. +26
    -13
      src/proto/server/response.rs
  6. +55
    -15
      src/room.rs

+ 58
- 2
src/client.rs View File

@ -144,8 +144,42 @@ impl Client {
}
fn handle_join_room_request(&mut self, room_name: String) {
let request = JoinRoomRequest { room_name: room_name };
self.server_send(ServerRequest::JoinRoomRequest(request));
// First check that we are not already a member.
// Enclosed in a block otherwise the mutable borrow of self.rooms
// prevents from calling self.server_send.
{
let room = match self.rooms.get_mut(&room_name) {
Some(room) => room,
None => {
error!("Cannot join inexistent room \"{}\"", room_name);
return;
}
};
match room.membership {
room::Membership::NonMember => {
// As expected.
room.membership = room::Membership::Joining;
},
room::Membership::Member => {
warn!(
"Controller requested to re-join room \"{}\"",
room_name
);
// TODO Send control response
},
membership => {
error!(
"Cannot join room \"{}\": current membership: {:?}",
room_name, membership
);
}
}
}
self.server_send(ServerRequest::JoinRoomRequest(JoinRoomRequest {
room_name: room_name
}));
}
fn handle_login_status_request(&mut self) {
@ -185,6 +219,9 @@ impl Client {
fn handle_server_response(&mut self, response: ServerResponse) {
match response {
ServerResponse::JoinRoomResponse(response) =>
self.handle_join_room_response(response),
ServerResponse::LoginResponse(response) =>
self.handle_login_response(response),
@ -201,6 +238,21 @@ impl Client {
}
}
fn handle_join_room_response(&mut self, response: JoinRoomResponse) {
let room = match self.rooms.get_mut(&response.room_name) {
Some(room) => room,
None => {
error!(
"Received JoinRoomResponse for inexistent room \"{}\"",
&response.room_name
);
return;
}
};
room.membership = room::Membership::Member;
room.user_count = response.user_names.len();
}
fn handle_login_response(&mut self, login: LoginResponse) {
if let LoginStatus::Pending = self.login_status {
match login {
@ -234,7 +286,11 @@ impl Client {
}
fn handle_room_list_response(&mut self, response: RoomListResponse) {
// Update the room map in memory.
self.rooms.update(response);
// Send the updated version to the controller.
let response = self.rooms.get_room_list_response();
self.control_send(control::Response::RoomListResponse(response));
}
fn handle_privileged_users_response(


+ 9
- 3
src/control/controller.rs View File

@ -182,10 +182,14 @@ impl Controller {
}
},
websocket::message::Type::Close => break,
websocket::message::Type::Close => {
info!("Received close request from controller.");
break;
},
code => warn!("Unhandled websocket message with code {:?}",
code),
code => warn!(
"Unhandled websocket message with code {:?}", code
),
}
}
info!("Shutting down websocket receiver");
@ -201,6 +205,7 @@ impl Controller {
{
let payload = try!(str::from_utf8(payload_bytes));
let control_request = try!(json::decode(payload));
debug!("Received control request: {:?}", control_request);
try!(client_tx.send(control_request));
Ok(())
}
@ -237,6 +242,7 @@ impl Controller {
fn send_response(sender: &mut WebSocketSender, response: Response)
-> Result<(), Error>
{
debug!("Sending control response: {:?}", response);
let encoded = try!(json::encode(&response));
let message = websocket::Message::text(encoded);
try!(sender.send_message(&message));


+ 3
- 0
src/handler.rs View File

@ -111,6 +111,8 @@ impl ConnectionHandler {
};
let server_response = try!(ServerResponse::from_packet(&mut packet));
debug!("Received server response: {:?}", server_response);
match self.client_tx.send(Response::ServerResponse(server_response)) {
Ok(()) => Ok(true),
Err(e) => Err(io::Error::new(
@ -136,6 +138,7 @@ impl ConnectionHandler {
}
fn notify_server(&mut self, request: ServerRequest) -> io::Result<()> {
debug!("Sending server request: {:?}", request);
let packet = try!(request.to_packet());
self.server_queue.push_back(packet);
Ok(())


+ 1
- 0
src/main.rs View File

@ -6,6 +6,7 @@ mod control;
mod handler;
mod proto;
mod room;
mod user;
extern crate byteorder;
extern crate core;


+ 26
- 13
src/proto/server/response.rs View File

@ -4,6 +4,8 @@ use std::net;
use super::constants::*;
use super::super::packet::Packet;
use user;
/*=============*
* FROM PACKET *
*=============*/
@ -135,26 +137,37 @@ impl FromPacket for ConnectToPeerResponse {
#[derive(Debug)]
pub struct JoinRoomResponse {
room_name: String,
user_names: Vec<String>,
user_statuses: Vec<u32>,
pub room_name: String,
pub user_names: Vec<String>,
pub user_infos: Vec<user::User>,
pub user_countries: Vec<String>,
pub owner_and_operators: Option<(String, Vec<String>)>,
}
impl FromPacket for JoinRoomResponse {
fn from_packet(packet: &mut Packet) -> io::Result<Self> {
let room_name = try!(packet.read_str());
let mut response = JoinRoomResponse {
room_name: try!(packet.read_str()),
user_names: Vec::new(),
user_infos: Vec::new(),
user_countries: Vec::new(),
owner_and_operators: None,
};
let mut user_names = Vec::new();
try!(packet.read_array(&mut user_names, Packet::read_str));
try!(packet.read_array(&mut response.user_names, Packet::read_str));
let mut user_statuses = Vec::new();
try!(packet.read_array(&mut user_statuses, Packet::read_uint));
try!(response.read_user_infos(packet));
Ok(JoinRoomResponse {
room_name: room_name,
user_names: user_names,
user_statuses: user_statuses,
})
try!(packet.read_array(&mut response.user_countries, Packet::read_str));
Ok(response)
}
}
impl JoinRoomResponse {
fn read_user_infos(&mut self, packet: &mut Packet) -> io::Result<()>
{
Ok(())
}
}


+ 55
- 15
src/room.rs View File

@ -1,8 +1,24 @@
use std::collections;
use std::mem;
use control;
use proto::server;
/// This enumeration is the list of possible membership states for a chat room.
#[derive(Clone, Copy, Debug, RustcDecodable, RustcEncodable)]
pub enum Membership {
/// The user is not a member of this room.
NonMember,
/// The user has requested to join the room, but hasn't heard back from the
/// server yet.
Joining,
/// The user is a member of the room.
Member,
/// The user has request to leave the room, but hasn't heard back from the
/// server yet.
Leaving,
}
/// This enumeration is the list of visibility types for rooms that the user is
/// a member of.
#[derive(Clone, Copy, Debug, RustcDecodable, RustcEncodable)]
@ -20,6 +36,8 @@ pub enum Visibility {
/// room hash table.
#[derive(Clone, Copy, Debug, RustcDecodable, RustcEncodable)]
pub struct Room {
/// The membership state of the user for the room.
pub membership: Membership,
/// The visibility of the room.
pub visibility: Visibility,
/// True if the user is one of the room's operators, False if the user is a
@ -29,6 +47,14 @@ pub struct Room {
pub user_count: usize,
}
impl Room {
/// Merges the previous version of the room's information into the new
/// version.
fn merge(&mut self, old_room: &Self) {
self.membership = old_room.membership;
}
}
/// Contains the mapping from room names to room data and provides a clean
/// interface to interact with it.
#[derive(Debug)]
@ -51,56 +77,69 @@ impl RoomMap {
self.map.get(name)
}
/// Looks up the given room name in the map, returning a mutable reference
/// to the associated data if found.
/// Looks up the given room name in the map, returning a mutable
/// reference to the associated data if found.
pub fn get_mut(&mut self, name: &str) -> Option<&mut Room> {
self.map.get_mut(name)
}
/// Updates one room in the mapping.
fn update_one(
&mut self, name: String, mut new_room: Room,
old_map: & collections::HashMap<String, Room>)
{
if let Some(old_room) = old_map.get(&name) {
new_room.merge(old_room);
}
if let Some(_) = self.map.insert(name, new_room) {
error!("Room present twice in room list response");
}
}
/// Updates the map to reflect the information contained in the given
/// server response.
pub fn update(&mut self, mut response: server::RoomListResponse) {
// First, clear the current map, keeping backing memory.
self.map.clear();
// Replace the old mapping with an empty one.
let old_map = mem::replace(&mut self.map, collections::HashMap::new());
// Add all public rooms.
for (name, user_count) in response.rooms.drain(..) {
self.map.insert(name, Room {
let new_room = Room {
membership: Membership::NonMember,
visibility: Visibility::Public,
operated: false,
user_count: user_count as usize,
});
};
self.update_one(name, new_room, &old_map);
}
// Add all private, owned, rooms.
for (name, user_count) in response.owned_private_rooms.drain(..) {
let room = Room {
let new_room = Room {
membership: Membership::NonMember,
visibility: Visibility::PrivateOwned,
operated: false,
user_count: user_count as usize,
};
if let Some(_) = self.map.insert(name, room) {
error!("Room is both public and owned_private");
}
self.update_one(name, new_room, &old_map);
}
// Add all private, unowned, rooms.
for (name, user_count) in response.other_private_rooms.drain(..) {
let room = Room {
let new_room = Room {
membership: Membership::NonMember,
visibility: Visibility::PrivateOther,
operated: false,
user_count: user_count as usize,
};
if let Some(_) = self.map.insert(name, room) {
error!("Room is both public and other_private");
}
self.update_one(name, new_room, &old_map);
}
// Mark all operated rooms as necessary.
for name in response.operated_private_room_names.drain(..) {
match self.map.get_mut(&name) {
None => error!("Room {} is operated but does not exist", name),
Some(room) => room.operated = true,
None => error!("Room {} is operated but does not exist", name),
}
}
}
@ -116,3 +155,4 @@ impl RoomMap {
response
}
}

Loading…
Cancel
Save