Skip to content
This repository was archived by the owner on Jun 25, 2026. It is now read-only.

Commit c6cd7d0

Browse files
committed
added main server and room manager implementation
1 parent b9cac05 commit c6cd7d0

4 files changed

Lines changed: 655 additions & 2 deletions

File tree

README.md

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,7 @@ Quick rant/yap
22

33
Networking in game is super frustrating, even a simple relay server takes lots of time.
44
Forgot the actual game, now you are stuck with networking code chaos, forever and eternally, and you will never finish
5-
your
6-
game, and you will never be satisfied, and you will never be proud of your work, and you will never be able to show it
5+
your game, and you will never be satisfied, and you will never be proud of your work, and you will never be able to show it
76
to anyone or PLAY, and you will never be able to have fun at all......YOU WILL NEVER BE LOVED EVER....AGAIN....
87
and you will just be stuck in this endless loop of networking code forever.
98

src/lib.rs

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
mod client;
2+
mod handler;
3+
mod log;
4+
mod protocol;
5+
mod room;
6+
mod server;
7+
mod types;
8+
9+
pub use client::{Client, ClientBuilder};
10+
pub use handler::{NoopHandler, ServerHandler};
11+
pub use server::{Server, ServerBuilder, ServerHandle};
12+
pub use types::{Result, ServerConfig, ServerEvent, SyncError};
13+
14+
pub use uuid::Uuid;

src/room.rs

Lines changed: 135 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,135 @@
1+
use crate::types::{Result, ServerWire, SyncError};
2+
use dashmap::DashMap;
3+
use tokio::sync::mpsc;
4+
use uuid::Uuid;
5+
6+
/// A broadcast frame to be sent to a client.
7+
pub type BroadcastFrame = Vec<u8>;
8+
9+
/// A single room holding connected clients.
10+
///
11+
/// Rooms are created explicitly via [`RoomManager::create`] and hold
12+
/// connected clients identified by their UUIDs. Each client has a
13+
/// write channel for outgoing frames.
14+
pub struct Room {
15+
clients: DashMap<Uuid, mpsc::Sender<BroadcastFrame>>,
16+
}
17+
18+
#[allow(dead_code)]
19+
impl Room {
20+
fn new() -> Self {
21+
Self {
22+
clients: DashMap::new(),
23+
}
24+
}
25+
26+
#[inline]
27+
pub fn insert(&self, id: Uuid, tx: mpsc::Sender<BroadcastFrame>) {
28+
self.clients.insert(id, tx);
29+
}
30+
31+
#[inline]
32+
pub fn remove(&self, id: &Uuid) -> bool {
33+
self.clients.remove(id).is_some()
34+
}
35+
36+
pub fn is_empty(&self) -> bool {
37+
self.clients.is_empty()
38+
}
39+
40+
pub fn len(&self) -> usize {
41+
self.clients.len()
42+
}
43+
44+
/// Serialize a ServerWire message and broadcast to all clients except `sender`.
45+
/// Returns the UUIDs of clients whose write channels were full (frame dropped).
46+
#[inline(always)]
47+
pub async fn broadcast(&self, sender: Uuid, msg: &ServerWire) -> Vec<Uuid> {
48+
let payload = match wincode::serialize(msg) {
49+
Ok(p) => p,
50+
Err(_) => return Vec::new(),
51+
};
52+
self.broadcast_raw(sender, &payload).await
53+
}
54+
55+
/// Send raw bytes to all clients except `sender`.
56+
/// Returns the UUIDs of clients whose write channels were full (frame dropped).
57+
#[inline(always)]
58+
pub async fn broadcast_raw(&self, sender: Uuid, payload: &[u8]) -> Vec<Uuid> {
59+
let mut dropped = Vec::new();
60+
for entry in self.clients.iter() {
61+
if *entry.key() != sender && entry.value().try_send(payload.to_vec()).is_err() {
62+
dropped.push(*entry.key());
63+
}
64+
}
65+
dropped
66+
}
67+
68+
/// Send a ServerWire message to a specific client.
69+
#[inline(always)]
70+
pub async fn send_to(&self, id: &Uuid, msg: &ServerWire) {
71+
if let Some(tx) = self.clients.get(id) {
72+
let payload = match wincode::serialize(msg) {
73+
Ok(p) => p,
74+
Err(_) => return,
75+
};
76+
let _ = tx.try_send(payload);
77+
}
78+
}
79+
80+
/// Get all client IDs in this room.
81+
#[inline]
82+
pub fn client_ids(&self) -> Vec<Uuid> {
83+
self.clients.iter().map(|e| *e.key()).collect()
84+
}
85+
}
86+
87+
/// Manages all active rooms.
88+
pub struct RoomManager {
89+
rooms: DashMap<String, Room>,
90+
}
91+
92+
impl RoomManager {
93+
/// Init a room with 64s pre-allocated
94+
pub fn new() -> Self {
95+
Self {
96+
rooms: DashMap::with_capacity(64),
97+
}
98+
}
99+
100+
/// Create a room with the given ID. Fails if the room already exists.
101+
#[inline(always)]
102+
pub fn create(&self, room_id: &str) -> Result<()> {
103+
if self.rooms.contains_key(room_id) {
104+
return Err(SyncError::RoomAlreadyExists(room_id.to_string()));
105+
}
106+
self.rooms.insert(room_id.to_string(), Room::new());
107+
Ok(())
108+
}
109+
110+
/// Delete a room. Returns true if the room existed.
111+
#[inline(always)]
112+
pub fn delete(&self, room_id: &str) -> bool {
113+
self.rooms.remove(room_id).is_some()
114+
}
115+
116+
/// Remove a client from a room. Does NOT auto-delete the room.
117+
#[inline(always)]
118+
pub fn remove_client(&self, room_id: &str, client_id: &Uuid) {
119+
if let Some(room) = self.rooms.get(room_id) {
120+
room.remove(client_id);
121+
}
122+
}
123+
124+
/// Get a reference to a room. Returns None if room doesn't exist.
125+
#[inline]
126+
pub fn get(&self, room_id: &str) -> Option<dashmap::mapref::one::Ref<'_, String, Room>> {
127+
self.rooms.get(room_id)
128+
}
129+
130+
/// Number of active rooms.
131+
#[inline]
132+
pub fn len(&self) -> usize {
133+
self.rooms.len()
134+
}
135+
}

0 commit comments

Comments
 (0)