use a SendTo enum instead of using Option to know who to send the broadcast message

This commit is contained in:
Rusty Striker 2025-07-25 14:56:57 +03:00
parent 23e23ad1a2
commit 0d99649e56
Signed by: RustyStriker
GPG key ID: 87E4D691632DFF15
3 changed files with 58 additions and 30 deletions

View file

@ -63,6 +63,7 @@ pub enum Request {
GetScene {
id: usize,
},
/// TODO: Perhaps remove it, as the client should auto get it on login and on scene list updates
GetSceneList,
GetTokens {
scene: usize,

View file

@ -78,7 +78,7 @@ impl GameServer {
pub async fn server_loop(
mut self,
mut msgs: mpsc::Receiver<(String, api::Request)>,
broadcast: broadcast::Sender<(Option<String>, api::Response)>,
broadcast: broadcast::Sender<(SendTo, api::Response)>,
) {
while let Some(req) = msgs.recv().await {
let (id, req) = req;
@ -98,18 +98,18 @@ impl GameServer {
self.users.insert(login.username.clone(), admin);
// Send login confirmation
_ = broadcast.send((
Some(id.clone()),
SendTo::User(id.clone()),
api::Response::Login(api::login::LoginResult {
success: true,
username: login.username.clone(),
}),
));
// Send the list of scenes and chat history and such
_ = broadcast.send((Some(login.username.clone()), self.get_scene_list(admin)));
_ = broadcast.send((Some(login.username.clone()), self.get_last_messages(50)));
_ = broadcast.send((SendTo::User(login.username.clone()), self.get_scene_list(admin)));
_ = broadcast.send((SendTo::User(login.username.clone()), self.get_last_messages(50)));
} else {
_ = broadcast.send((
Some(id.clone()),
SendTo::User(id.clone()),
api::Response::Login(api::login::LoginResult {
success: false,
username: login.username,
@ -131,15 +131,18 @@ impl GameServer {
self.chat.push((id.clone(), msg.clone()));
if msg.whisper.is_some() {
_ = broadcast.send((Some(id.clone()), api::Response::Message(msg.clone())));
_ = broadcast.send((SendTo::User(id.clone()), api::Response::Message(msg.clone())));
}
_ = broadcast.send((msg.whisper.clone(), api::Response::Message(msg)));
_ = broadcast.send((
msg.whisper.clone().map(|id| SendTo::User(id)).unwrap_or_default(),
api::Response::Message(msg),
));
}
api::Request::GetChatHistory { amount, from } => {
_ = broadcast.send((Some(id), self.get_chat_history(amount, from)));
_ = broadcast.send((SendTo::User(id), self.get_chat_history(amount, from)));
}
api::Request::GetLastMessages { amount } => {
_ = broadcast.send((Some(id), self.get_last_messages(amount)));
_ = broadcast.send((SendTo::User(id), self.get_last_messages(amount)));
}
api::Request::GetTokens { scene } => {
for token_id in self.game.available_tokens(scene, self.is_admin(&id)) {
@ -147,7 +150,7 @@ impl GameServer {
(ti.visible_to_players)
{
_ = broadcast.send((
Some(id.clone()),
SendTo::User(id.clone()),
api::Response::SpawnToken(SpawnToken {
token_id: token_id,
x: ti.x,
@ -160,14 +163,14 @@ impl GameServer {
}
api::Request::GetScene { id: scene_id } => {
if let Some(response) = self.get_scene(&id, scene_id) {
_ = broadcast.send((Some(id), response));
_ = broadcast.send((SendTo::User(id), response));
}
}
api::Request::CreateScene { title } => {
if self.is_admin(&id) {
self.game.create_scene(title);
let scenes = self.get_scene_list(true);
_ = broadcast.send((Some(id), scenes));
_ = broadcast.send((SendTo::User(id), scenes));
}
}
api::Request::SetSceneGrid {
@ -180,7 +183,11 @@ impl GameServer {
if let Some(map) = s.map.as_mut() {
map.grid_cell_size = grid_cell_size;
map.grid_offset = grid_offset;
let return_id = if s.visible_to_users { None } else { Some(id.clone()) };
let return_id = if s.visible_to_users {
SendTo::All
} else {
SendTo::User(id.clone())
};
if let Some(scene) = self.get_scene(&id, scene) {
_ = broadcast.send((return_id, scene));
}
@ -191,12 +198,12 @@ impl GameServer {
api::Request::SceneSetVisible { scene, visible } => {
if self.is_admin(&id) {
self.game.scene_visible(scene, visible);
_ = broadcast.send((None, self.get_scene_list(false)));
_ = broadcast.send((Some(id), self.get_scene_list(true)));
_ = broadcast.send((SendTo::AllBut(id.clone()), self.get_scene_list(false)));
_ = broadcast.send((SendTo::User(id), self.get_scene_list(true)));
}
}
api::Request::GetSceneList => {
_ = broadcast.send((Some(id.clone()), self.get_scene_list(self.is_admin(&id))));
_ = broadcast.send((SendTo::User(id.clone()), self.get_scene_list(self.is_admin(&id))));
}
api::Request::SpawnToken {
map_id,
@ -213,9 +220,9 @@ impl GameServer {
.create_token(map_id, character, img_path.clone(), x, y, visible_to_players);
_ = broadcast.send((
if visible_to_players && scene_visible {
None
SendTo::All
} else {
Some(id)
SendTo::User(id)
},
api::Response::SpawnToken(SpawnToken {
token_id,
@ -231,7 +238,7 @@ impl GameServer {
if self.game.move_token(0, token_id, x, y) {
// TODO: maybe chage move_token to return optional x,y values if succeeded to make sure the token is where it was going to be
// TODO: Check if the token is visible to players and if not, only update it's position to admins or something
_ = broadcast.send((None, api::Response::MoveToken { token_id, x, y }));
_ = broadcast.send((SendTo::All, api::Response::MoveToken { token_id, x, y }));
}
}
api::Request::ActionResult(result) => {
@ -249,27 +256,27 @@ impl GameServer {
.targets(Some(result.targets))
.id(self.chat.len() + 1);
self.chat.push((id, msg.clone()));
_ = broadcast.send((None, api::Response::Message(msg)));
_ = broadcast.send((SendTo::All, api::Response::Message(msg)));
}
api::Request::CreateCharacter => {
// check if user is admin
if self.is_admin(&id) {
let new_id = self.game.create_character();
// return the new id with the character i think
_ = broadcast.send((Some(id), api::Response::CharacterCreated(new_id)));
_ = broadcast.send((SendTo::User(id), api::Response::CharacterCreated(new_id)));
}
}
api::Request::Quit => {
if self.users.contains_key(&id) {
self.users.remove(&id);
}
_ = broadcast.send((None, api::Response::Quit { id }));
_ = broadcast.send((SendTo::All, api::Response::Quit { id }));
}
api::Request::Kick(id) => {
if self.users.contains_key(&id) {
self.users.remove(&id);
}
_ = broadcast.send((Some(id), api::Response::Shutdown));
_ = broadcast.send((SendTo::User(id), api::Response::Shutdown));
}
api::Request::Shutdown => {
if self.is_admin(&id) {
@ -292,7 +299,7 @@ impl GameServer {
self.save().await;
}
}
_ = broadcast.send((None, api::Response::Shutdown));
_ = broadcast.send((SendTo::All, api::Response::Shutdown));
self.save().await; // Save the game one last time :)
}
@ -369,3 +376,20 @@ impl GameServer {
self.users.get(id).map(|a| *a).unwrap_or(false)
}
}
#[derive(Default, Clone, Debug)]
pub enum SendTo {
#[default]
All,
User(String),
AllBut(String),
}
impl SendTo {
pub fn should_send(&self, to: &str) -> bool {
match self {
SendTo::All => true,
SendTo::User(user) => user == to,
SendTo::AllBut(user) => user != to,
}
}
}

View file

@ -9,7 +9,10 @@ use futures_util::{
SinkExt, StreamExt,
stream::{SplitSink, SplitStream},
};
use open_tavern::api::{Request, RequestError, Response};
use open_tavern::{
SendTo,
api::{Request, RequestError, Response},
};
use tokio::sync::{broadcast, mpsc};
use tokio_util::io::ReaderStream;
@ -55,7 +58,7 @@ async fn run_web_server(listener: tokio::net::TcpListener, app: Router) {
async fn ws_handler(
ws: ws::WebSocketUpgrade,
msend: mpsc::Sender<(String, Request)>,
brecv: broadcast::Receiver<(Option<String>, Response)>,
brecv: broadcast::Receiver<(SendTo, Response)>,
) -> impl axum::response::IntoResponse {
ws.on_upgrade(|w| handle_socket(w, msend, brecv))
}
@ -89,10 +92,10 @@ async fn socket_receiver(mut recv: SplitStream<ws::WebSocket>, msend: mpsc::Send
async fn socket_sender(
id: String,
mut send: SplitSink<ws::WebSocket, ws::Message>,
mut brecv: broadcast::Receiver<(Option<String>, Response)>,
mut brecv: broadcast::Receiver<(SendTo, Response)>,
) {
while let Ok((to_id, msg)) = brecv.recv().await {
if to_id.is_none() || to_id.map(|t| t == id).unwrap_or(false) {
if to_id.should_send(&id) {
println!("Sending a message to {}: {:?}", &id, &msg);
let err = send
.send(ws::Message::Text(serde_json::to_string(&msg).unwrap().into()))
@ -110,7 +113,7 @@ async fn socket_sender(
async fn handle_socket(
mut socket: ws::WebSocket,
msend: mpsc::Sender<(String, Request)>,
mut brecv: broadcast::Receiver<(Option<String>, Response)>,
mut brecv: broadcast::Receiver<(SendTo, Response)>,
) {
let mut id: Option<String> = None;
// this is a temp id, and as long as 2 people dont try to connect to the socket at the same milisecond and to the same user it would be fine (i hope)
@ -128,7 +131,7 @@ async fn handle_socket(
println!("{} trying to log in: {:?}", &temp_id, &b);
if let Ok((to_id, msg)) = b {
if let Response::Login(open_tavern::api::login::LoginResult { success, username }) = &msg {
let to_id = to_id.map(|ti| ti == temp_id).unwrap_or(false);
let to_id = to_id.should_send(&temp_id);
if to_id && *success && id.as_ref().map(|id| id == username).unwrap_or(false) {
_ = socket.send(Message::Text(serde_json::to_string(&msg).unwrap_or_default().into())).await;
break;