forked from lavina/lavina
implement room bans
This commit is contained in:
parent
204126b9fb
commit
e813fb7c69
|
@ -8,7 +8,7 @@
|
|||
//! A player actor is a serial handler of commands from a single player. It is preferable to run all per-player validations in the player actor,
|
||||
//! so that they don't overload the room actor.
|
||||
use std::{
|
||||
collections::HashMap,
|
||||
collections::{HashMap, HashSet},
|
||||
sync::{Arc, RwLock},
|
||||
};
|
||||
|
||||
|
@ -58,7 +58,7 @@ impl PlayerConnection {
|
|||
.await
|
||||
}
|
||||
|
||||
pub async fn join_room(&mut self, room_id: RoomId) -> Result<RoomInfo> {
|
||||
pub async fn join_room(&mut self, room_id: RoomId) -> Result<JoinResult> {
|
||||
self.player_handle
|
||||
.join_room(room_id, self.connection_id.clone())
|
||||
.await
|
||||
|
@ -96,7 +96,9 @@ impl PlayerConnection {
|
|||
|
||||
pub async fn get_rooms(&self) -> Result<Vec<RoomInfo>> {
|
||||
let (promise, deferred) = oneshot();
|
||||
self.player_handle.send(PlayerCommand::GetRooms(promise)).await;
|
||||
self.player_handle
|
||||
.send(PlayerCommand::GetRooms(promise))
|
||||
.await;
|
||||
Ok(deferred.await?)
|
||||
}
|
||||
}
|
||||
|
@ -141,7 +143,7 @@ impl PlayerHandle {
|
|||
&self,
|
||||
room_id: RoomId,
|
||||
connection_id: ConnectionId,
|
||||
) -> Result<RoomInfo> {
|
||||
) -> Result<JoinResult> {
|
||||
let (promise, deferred) = oneshot();
|
||||
let cmd = Cmd::JoinRoom {
|
||||
room_id,
|
||||
|
@ -177,7 +179,7 @@ enum PlayerCommand {
|
|||
pub enum Cmd {
|
||||
JoinRoom {
|
||||
room_id: RoomId,
|
||||
promise: Promise<RoomInfo>,
|
||||
promise: Promise<JoinResult>,
|
||||
},
|
||||
LeaveRoom {
|
||||
room_id: RoomId,
|
||||
|
@ -195,6 +197,11 @@ pub enum Cmd {
|
|||
},
|
||||
}
|
||||
|
||||
pub enum JoinResult {
|
||||
Success(RoomInfo),
|
||||
Banned,
|
||||
}
|
||||
|
||||
/// Player update event type which is sent to a player actor and from there to a connection handler.
|
||||
#[derive(Clone)]
|
||||
pub enum Updates {
|
||||
|
@ -215,6 +222,8 @@ pub enum Updates {
|
|||
room_id: RoomId,
|
||||
former_member_id: PlayerId,
|
||||
},
|
||||
/// The player was banned from the room and left it immediately.
|
||||
BannedFrom(RoomId),
|
||||
}
|
||||
|
||||
/// Handle to a player registry — a shared data structure containing information about players.
|
||||
|
@ -266,6 +275,7 @@ struct Player {
|
|||
player_id: PlayerId,
|
||||
connections: AnonTable<Sender<Updates>>,
|
||||
my_rooms: HashMap<RoomId, RoomHandle>,
|
||||
banned_from: HashSet<RoomId>,
|
||||
rx: Receiver<PlayerCommand>,
|
||||
handle: PlayerHandle,
|
||||
rooms: RoomRegistry,
|
||||
|
@ -279,6 +289,7 @@ impl Player {
|
|||
player_id,
|
||||
connections: AnonTable::new(),
|
||||
my_rooms: HashMap::new(),
|
||||
banned_from: HashSet::from([RoomId::from_bytes(b"empty".to_vec()).unwrap()]),
|
||||
rx,
|
||||
handle,
|
||||
rooms,
|
||||
|
@ -312,6 +323,13 @@ impl Player {
|
|||
"Player received an update, broadcasting to {} connections",
|
||||
self.connections.len()
|
||||
);
|
||||
match update {
|
||||
Updates::BannedFrom(ref room_id) => {
|
||||
self.banned_from.insert(room_id.clone());
|
||||
self.my_rooms.remove(room_id);
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
for (_, connection) in &self.connections {
|
||||
connection.send(update.clone()).await;
|
||||
}
|
||||
|
@ -331,12 +349,17 @@ impl Player {
|
|||
async fn handle_cmd(&mut self, cmd: Cmd, connection_id: ConnectionId) {
|
||||
match cmd {
|
||||
Cmd::JoinRoom { room_id, promise } => {
|
||||
if self.banned_from.contains(&room_id) {
|
||||
promise.send(JoinResult::Banned);
|
||||
return;
|
||||
}
|
||||
|
||||
let room = self.rooms.get_or_create_room(room_id.clone());
|
||||
room.subscribe(self.player_id.clone(), self.handle.clone())
|
||||
.await;
|
||||
self.my_rooms.insert(room_id.clone(), room.clone());
|
||||
let room_info = room.get_room_info().await;
|
||||
promise.send(room_info);
|
||||
promise.send(JoinResult::Success(room_info));
|
||||
let update = Updates::RoomJoined {
|
||||
room_id,
|
||||
new_member_id: self.player_id.clone(),
|
||||
|
|
|
@ -7,7 +7,7 @@ use tokio::net::tcp::{ReadHalf, WriteHalf};
|
|||
use tokio::net::{TcpListener, TcpStream};
|
||||
use tokio::sync::oneshot::channel;
|
||||
|
||||
use crate::core::player::{PlayerConnection, PlayerId, PlayerRegistry, Updates};
|
||||
use crate::core::player::*;
|
||||
use crate::core::room::{RoomId, RoomInfo, RoomRegistry};
|
||||
use crate::prelude::*;
|
||||
use crate::protos::irc::client::{client_message, ClientMessage};
|
||||
|
@ -320,6 +320,17 @@ async fn handle_update(
|
|||
.await?;
|
||||
writer.flush().await?
|
||||
}
|
||||
Updates::BannedFrom(room_id) => {
|
||||
// TODO think about the case when the user was banned, but was not in the room - no need to send PART in this case
|
||||
ServerMessage {
|
||||
tags: vec![],
|
||||
sender: Some(player_id.as_bytes().clone()),
|
||||
body: ServerMessageBody::Part(Chan::Global(room_id.as_bytes().clone())),
|
||||
}
|
||||
.write_async(writer)
|
||||
.await?;
|
||||
writer.flush().await?
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
@ -453,7 +464,10 @@ async fn handle_incoming_message(
|
|||
ServerMessage {
|
||||
tags: vec![],
|
||||
sender: Some(config.server_name.as_bytes().to_vec()),
|
||||
body: ServerMessageBody::N221UserModeIs { client: user.nickname.clone(), modes: b"+r".to_vec() },
|
||||
body: ServerMessageBody::N221UserModeIs {
|
||||
client: user.nickname.clone(),
|
||||
modes: b"+r".to_vec(),
|
||||
},
|
||||
}
|
||||
.write_async(writer)
|
||||
.await?;
|
||||
|
@ -461,12 +475,12 @@ async fn handle_incoming_message(
|
|||
} else {
|
||||
// TODO send 502 (not 401) if the user is not the sender
|
||||
}
|
||||
},
|
||||
}
|
||||
Recipient::Chan(_) => {
|
||||
// TODO handle chan mode handling
|
||||
},
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
cmd => {
|
||||
log::warn!("Not implemented handler for client command: {cmd:?}");
|
||||
}
|
||||
|
@ -478,7 +492,11 @@ async fn handle_incoming_message(
|
|||
Ok(())
|
||||
}
|
||||
|
||||
fn user_to_who_msg(config: &ServerConfig, requestor: &RegisteredUser, target_user_nickname: &ByteVec) -> ServerMessageBody {
|
||||
fn user_to_who_msg(
|
||||
config: &ServerConfig,
|
||||
requestor: &RegisteredUser,
|
||||
target_user_nickname: &ByteVec,
|
||||
) -> ServerMessageBody {
|
||||
// Username is equal to nickname
|
||||
let mut username = Vec::with_capacity(target_user_nickname.len() + 1);
|
||||
username.push(b'~');
|
||||
|
@ -511,8 +529,21 @@ async fn handle_join(
|
|||
match chan {
|
||||
Chan::Global(chan_name) => {
|
||||
let room_id = RoomId::from_bytes(chan_name.clone())?;
|
||||
let room_info = user_handle.join_room(room_id).await?;
|
||||
if let JoinResult::Success(room_info) = user_handle.join_room(room_id).await? {
|
||||
produce_on_join_cmd_messages(&config, &user, chan, &room_info, writer).await?;
|
||||
} else {
|
||||
ServerMessage {
|
||||
tags: vec![],
|
||||
sender: Some(config.server_name.as_bytes().to_vec()),
|
||||
body: ServerMessageBody::N474BannedFromChan {
|
||||
client: user.nickname.clone(),
|
||||
chan: chan.clone(),
|
||||
message: b"U dun goofed".to_vec(),
|
||||
},
|
||||
}
|
||||
.write_async(writer)
|
||||
.await?;
|
||||
}
|
||||
writer.flush().await?;
|
||||
}
|
||||
Chan::Local(_) => {}
|
||||
|
|
|
@ -143,7 +143,7 @@ fn client_message_mode(input: &[u8]) -> IResult<&[u8], ClientMessage> {
|
|||
let (input, _) = tag("MODE ")(input)?;
|
||||
let (input, target) = recipient(input)?;
|
||||
|
||||
Ok((input, ClientMessage::Mode { target } ))
|
||||
Ok((input, ClientMessage::Mode { target }))
|
||||
}
|
||||
|
||||
fn client_message_who(input: &[u8]) -> IResult<&[u8], ClientMessage> {
|
||||
|
|
|
@ -124,6 +124,11 @@ pub enum ServerMessageBody {
|
|||
client: ByteVec,
|
||||
chan: Chan,
|
||||
},
|
||||
N474BannedFromChan {
|
||||
client: ByteVec,
|
||||
chan: Chan,
|
||||
message: ByteVec,
|
||||
},
|
||||
}
|
||||
|
||||
impl ServerMessageBody {
|
||||
|
@ -192,9 +197,7 @@ impl ServerMessageBody {
|
|||
writer.write_all(&hostname).await?;
|
||||
writer.write_all(b" ").await?;
|
||||
writer.write_all(&softname).await?;
|
||||
writer
|
||||
.write_all(b" r CFILPQbcefgijklmnopqrstvz")
|
||||
.await?;
|
||||
writer.write_all(b" r CFILPQbcefgijklmnopqrstvz").await?;
|
||||
// TODO remove hardcoded modes
|
||||
}
|
||||
ServerMessageBody::N005ISupport { client, params } => {
|
||||
|
@ -209,7 +212,7 @@ impl ServerMessageBody {
|
|||
writer.write_all(&client).await?;
|
||||
writer.write_all(b" ").await?;
|
||||
writer.write_all(&modes).await?;
|
||||
},
|
||||
}
|
||||
ServerMessageBody::N315EndOfWho { client, mask, msg } => {
|
||||
writer.write_all(b"315 ").await?;
|
||||
writer.write_all(&client).await?;
|
||||
|
@ -279,6 +282,18 @@ impl ServerMessageBody {
|
|||
chan.write_async(writer).await?;
|
||||
writer.write_all(b" :End of /NAMES list").await?;
|
||||
}
|
||||
ServerMessageBody::N474BannedFromChan {
|
||||
client,
|
||||
chan,
|
||||
message,
|
||||
} => {
|
||||
writer.write_all(b"474 ").await?;
|
||||
writer.write_all(&client).await?;
|
||||
writer.write_all(b" ").await?;
|
||||
chan.write_async(writer).await?;
|
||||
writer.write_all(b" :").await?;
|
||||
writer.write_all(&message).await?;
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue