Skip to content

Commit 625c3d5

Browse files
authored
Initial Glueball Refactors (#1506)
2 parents 937454e + 48520e0 commit 625c3d5

11 files changed

Lines changed: 977 additions & 170 deletions

File tree

glueball/Cargo.lock

Lines changed: 893 additions & 114 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

glueball/Cargo.toml

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -16,16 +16,13 @@ colored = "3.1.1"
1616
crossterm = "0.28"
1717
dashmap = "6.2.1"
1818
directories = "6.0.0"
19-
futures-channel = "0.3.33"
2019
futures-util = "0.3.33"
2120
rand = "0.10.2"
22-
ratatui = "0.29"
21+
ratatui = "=0.30.0-alpha.1"
2322
rcgen = "0.14.8"
2423
rmp-serde = "1.3.1"
25-
rtrb = "0.3.4"
2624
rustls-pemfile = "2"
2725
serde = { version = "1.0.229", features = ["derive"] }
28-
serde_json = "1.0.151"
2926
tokio = { version = "1.53.1", features = ["rt-multi-thread", "net", "sync", "time", "macros"] }
3027
tokio-rustls = { version = "0.26", default-features = false, features = ["ring", "tls12", "logging"] }
3128
tokio-tungstenite = "0.30.0"

glueball/src/action.rs

Lines changed: 6 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -4,32 +4,27 @@ use crate::{
44
state::State,
55
util::server_sent_msg,
66
};
7-
use rtrb::{Consumer, Producer, RingBuffer};
87
use std::sync::Arc;
8+
use tokio::sync::mpsc::{self, Receiver, Sender};
99
use tokio_tungstenite::tungstenite::Message;
1010

11-
const MAX_PENDING_KICK_MESSAGES: usize = 12;
11+
const MAX_PENDING_ACTIONS: usize = 12;
1212

1313
pub enum UserAction {
1414
Lock(RoomId),
1515
Kick(ClientId),
1616
}
1717

18-
pub fn setup_user_action_system(state: &Arc<State>) -> Producer<UserAction> {
19-
let (tx, rx) = RingBuffer::new(MAX_PENDING_KICK_MESSAGES);
18+
pub fn setup_user_action_system(state: &Arc<State>) -> Sender<UserAction> {
19+
let (tx, rx) = mpsc::channel::<UserAction>(MAX_PENDING_ACTIONS);
2020
spawn_user_action_receiver(rx, state.clone());
2121

2222
tx
2323
}
2424

25-
fn spawn_user_action_receiver(mut rx: Consumer<UserAction>, state: Arc<State>) {
25+
fn spawn_user_action_receiver(mut rx: Receiver<UserAction>, state: Arc<State>) {
2626
tokio::spawn(async move {
27-
loop {
28-
let Ok(user_message) = rx.pop() else {
29-
tokio::task::yield_now().await;
30-
continue;
31-
};
32-
27+
while let Some(user_message) = rx.recv().await {
3328
match user_message {
3429
UserAction::Lock(room_id) => {
3530
let _ = state.toggle_room_lock(&room_id);

glueball/src/config.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ pub const DEFAULT_PORT: u16 = 2610;
1515
pub fn certification_directory() -> Result<PathBuf> {
1616
let dir = match ProjectDirs::from("com", "Autodesk", "synthesis-glueball") {
1717
Some(dirs) => dirs.data_dir().join("secrets"),
18-
None => bail!("Cound not find certificate directory"),
18+
None => bail!("Could not find certificate directory"),
1919
};
2020

2121
Ok(dir)

glueball/src/connection.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -94,7 +94,7 @@ pub async fn handle_client_close(client_id: ClientId, state: &Arc<State>) -> Res
9494
});
9595

9696
let Some(room) = state.get_room_of_client_mut(&client_id) else {
97-
bail!("Client attempted to leave when they were not in a room ");
97+
bail!("Client attempted to leave when they were not in a room");
9898
};
9999

100100
let client_name = room.get_client_name(&client_id)?;

glueball/src/logging.rs

Lines changed: 16 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,13 @@
11
use std::collections::{HashMap, VecDeque};
22

33
use colored::{Color, ColoredString, Colorize};
4-
use ratatui::{
5-
style::{Style, Stylize},
6-
text::Line,
7-
};
4+
use ratatui::{style::Style, text::Line};
85
use tokio::sync::mpsc::{self, Receiver};
96

107
use crate::{LOG_TX, room::RoomId};
118

9+
/// Maximum number of log lines retained in each log (both per-room and system logs)
10+
/// Oldest lines are dropped once the buffer is full.
1211
pub const MAX_LOG_LINES: usize = 500;
1312

1413
pub type RoomLogs = HashMap<RoomId, VecDeque<Event>>;
@@ -85,14 +84,14 @@ pub enum LogDestination {
8584
RemoveRoom(RoomId),
8685
}
8786

88-
#[derive(Debug, Clone)]
87+
#[derive(Debug, Clone, PartialEq, Eq)]
8988
pub enum EventType {
9089
Info,
9190
Warning,
9291
Error,
9392
}
9493

95-
#[derive(Debug, Clone)]
94+
#[derive(Debug, Clone, PartialEq, Eq)]
9695
pub struct Event {
9796
pub message: String,
9897
pub kind: EventType,
@@ -180,9 +179,19 @@ impl Logger {
180179
let _ = self.room_logs.remove(id);
181180
}
182181

183-
pub fn snapshot(&self) -> LogSnapshot {
182+
pub fn new_snapshot(&self) -> LogSnapshot {
184183
(self.global_log.clone(), self.room_logs.clone())
185184
}
185+
186+
pub fn update_snapshot(&self, old_snapshot: &mut LogSnapshot) {
187+
if old_snapshot.0 != self.global_log {
188+
old_snapshot.0.clone_from(&self.global_log);
189+
}
190+
191+
if old_snapshot.1 != self.room_logs {
192+
old_snapshot.1.clone_from(&self.room_logs);
193+
}
194+
}
186195
}
187196

188197
pub fn spawn_log_receiver<F>(mut logging_rx: Receiver<LogRequest>, handle_log: F)

glueball/src/main.rs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -40,10 +40,10 @@ static LOG_TX: OnceLock<Sender<LogRequest>> = OnceLock::new();
4040
async fn main() -> Result<()> {
4141
let _cleanup_trigger = Cleanup;
4242

43-
let config = retrieve_config()?;
44-
4543
let logging_rx = create_logging_channel();
4644

45+
let config = retrieve_config()?;
46+
4747
let state = Arc::new(State::new());
4848

4949
if config.headless {
@@ -53,7 +53,7 @@ async fn main() -> Result<()> {
5353
}
5454

5555
if let Some(ref room_id) = config.permanent_room {
56-
state.new_permanent_room(&room_id);
56+
state.new_permanent_room(room_id);
5757
}
5858

5959
// `listener` will be used regardless of the security level specified
@@ -121,7 +121,7 @@ async fn run_secure_server(
121121
}
122122

123123
fn setup_cli_logging(logging_rx: mpsc::Receiver<LogRequest>) {
124-
// Read the logging channel and immediantly print result
124+
// Read the logging channel and immediately print result
125125
let print_to_terminal =
126126
move |message: String, kind: EventType, log_destination: LogDestination| {
127127
match log_destination {

glueball/src/messaging.rs

Lines changed: 14 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ use std::ops;
2020
use std::sync::Arc;
2121

2222
type WsStream<S> = WebSocketStream<Prefixed<S>>;
23+
const MAX_CLIENT_NAME_LENGTH: usize = 100;
2324

2425
/// Waits for and handles messages from the client that are intended for the server.
2526
///
@@ -51,10 +52,13 @@ where
5152
// When they ask to initialize a connection, then we add them to a room
5253
// Or create a room for them
5354
Some(ClientToServerMessage::InitializeConnection { room_id, name }) => {
55+
if name.len() > MAX_CLIENT_NAME_LENGTH {
56+
break None;
57+
}
58+
5459
let info = state.initialize_client_in_room(tx, room_id, &name);
55-
let (client_id, room_id) = match info {
56-
Some(info) => info,
57-
None => break None,
60+
let Some((client_id, room_id)) = info else {
61+
break None;
5862
};
5963

6064
let message = server_sent_msg(ServerToClientMessage::SendInfo {
@@ -90,10 +94,14 @@ where
9094
S: SynthesisStream,
9195
{
9296
let Some(Ok(Message::Binary(message_data))) = read.next().await else {
93-
warn_global!("Client disconnected before handshake (probably a test)");
97+
warn_global!("Client disconnected before handshake (probably connection test)");
9498
return None;
9599
};
96100

101+
if message_data.len() == 1 {
102+
return None;
103+
}
104+
97105
let Ok(message) = deserialize_messagepack::<ClientToServerMessage>(&message_data[1..]) else {
98106
error_global!("{addr} sent an invalid initial message");
99107
return None;
@@ -134,8 +142,9 @@ pub async fn handle_client_message(
134142
// If we're here, that means the message has a client-client prefix
135143
// which we want anyway, so there's no need to prefix the message
136144
// we can just forward it!
137-
let senders: Vec<ClientSender> = state.get_senders_from_user_room(client_id);
145+
let senders: Vec<ClientSender> = state.get_senders_from_user_room(&client_id);
138146

147+
// TODO perf
139148
let tasks = senders.iter().map(|tx| tx.send(message.clone()));
140149
let _ = futures_util::future::join_all(tasks).await;
141150

glueball/src/state.rs

Lines changed: 27 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -9,10 +9,9 @@ use uuid::Uuid;
99
pub type ClientMap = DashMap<ClientId, RoomId>;
1010
pub type RoomMap = DashMap<RoomId, Room>;
1111

12-
/// Maximum number of log lines retained in each log (both per-room and system logs)
13-
/// Oldest lines are dropped once the buffer is full.
1412
const VALID_ROOM_ID_CHARACTERS: [char; 36] = valid_room_id_characters();
1513
const MAX_ROOM_COUNT: usize = 32;
14+
const MAX_MEMBER_COUNT_PER_ROOM: usize = 10;
1615

1716
pub struct State {
1817
pub users: ClientMap,
@@ -34,7 +33,7 @@ impl State {
3433
name: &str,
3534
) -> Option<(ClientId, RoomId)> {
3635
match room_id {
37-
None if self.room_count() == MAX_ROOM_COUNT => None,
36+
None if self.room_count() >= MAX_ROOM_COUNT => None,
3837
None => Some(self.add_room_and_host(name, tx)),
3938
Some(room_id) => self
4039
.add_client_to_room(name, tx, &room_id)
@@ -66,13 +65,15 @@ impl State {
6665
(host_id, room_id)
6766
}
6867

69-
/// # Safety
68+
/// # Deadlock
7069
/// Relinquish all locks on `self.room` before calling
7170
pub fn remove_client(&self, client_id: &ClientId) {
7271
let Some(room_id) = self.users.get(client_id).map(|a| a.value().clone()) else {
7372
return;
7473
};
7574

75+
self.users.remove(client_id);
76+
7677
// Room lock held
7778
let Some(mut room) = self.rooms.get_mut(&room_id) else {
7879
return;
@@ -94,6 +95,7 @@ impl State {
9495
room_id: &RoomId,
9596
) -> Option<ClientId> {
9697
let client_id = Uuid::new_v4();
98+
9799
let Some(mut room) = self.rooms.get_mut(room_id) else {
98100
warn_global!("Attempted to add {client_id} into non-existant room {room_id}");
99101
return None;
@@ -103,6 +105,10 @@ impl State {
103105
return None;
104106
}
105107

108+
if room.members.len() >= MAX_MEMBER_COUNT_PER_ROOM {
109+
return None;
110+
}
111+
106112
let client = Client::new(client_id, client_name.to_string(), client_tx);
107113
room.members.push(client);
108114
self.users.insert(client_id, room_id.clone());
@@ -125,13 +131,18 @@ impl State {
125131
return;
126132
}
127133

134+
if self.rooms.contains_key(room_id) {
135+
error_global!("Attempted to create permanent room that already exists");
136+
return;
137+
}
138+
128139
let room = Room {
129140
members: Vec::new(),
130141
host: None,
131142
locked: false,
132143
permanent: true,
133144
};
134-
self.rooms.insert(room_id.to_string(), room);
145+
self.rooms.insert(room_id.clone(), room);
135146
}
136147

137148
pub fn get_room_of_client_mut(&self, client_id: &ClientId) -> Option<RefMut<'_, RoomId, Room>> {
@@ -149,12 +160,12 @@ impl State {
149160
self.rooms.get(&room_id)
150161
}
151162

152-
pub fn get_senders_from_user_room(&self, client_id: ClientId) -> Vec<ClientSender> {
153-
let Some(room) = self.get_room_of_client(&client_id) else {
163+
pub fn get_senders_from_user_room(&self, client_id: &ClientId) -> Vec<ClientSender> {
164+
let Some(room) = self.get_room_of_client(client_id) else {
154165
return Vec::new();
155166
};
156167

157-
room.get_peer_senders(&client_id)
168+
room.get_peer_senders(client_id)
158169
}
159170

160171
/// Flips whether new clients can join `room_id`.
@@ -222,9 +233,14 @@ impl State {
222233
}
223234

224235
pub fn is_valid_room_id(s: &str) -> bool {
225-
s.trim().len() == 6
226-
&& s.chars()
227-
.all(|c| c.is_ascii_digit() || c.is_ascii_uppercase())
236+
let s = s.trim();
237+
238+
let correct_length = s.len() == 6;
239+
let all_characters_valid = s
240+
.chars()
241+
.all(|c| c.is_ascii_digit() || c.is_ascii_uppercase());
242+
243+
correct_length && all_characters_valid
228244
}
229245

230246
const fn valid_room_id_characters() -> [char; 36] {

0 commit comments

Comments
 (0)