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 { GetScene {
id: usize, id: usize,
}, },
/// TODO: Perhaps remove it, as the client should auto get it on login and on scene list updates
GetSceneList, GetSceneList,
GetTokens { GetTokens {
scene: usize, scene: usize,

View file

@ -78,7 +78,7 @@ impl GameServer {
pub async fn server_loop( pub async fn server_loop(
mut self, mut self,
mut msgs: mpsc::Receiver<(String, api::Request)>, 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 { while let Some(req) = msgs.recv().await {
let (id, req) = req; let (id, req) = req;
@ -98,18 +98,18 @@ impl GameServer {
self.users.insert(login.username.clone(), admin); self.users.insert(login.username.clone(), admin);
// Send login confirmation // Send login confirmation
_ = broadcast.send(( _ = broadcast.send((
Some(id.clone()), SendTo::User(id.clone()),
api::Response::Login(api::login::LoginResult { api::Response::Login(api::login::LoginResult {
success: true, success: true,
username: login.username.clone(), username: login.username.clone(),
}), }),
)); ));
// Send the list of scenes and chat history and such // Send the list of scenes and chat history and such
_ = broadcast.send((Some(login.username.clone()), self.get_scene_list(admin))); _ = broadcast.send((SendTo::User(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_last_messages(50)));
} else { } else {
_ = broadcast.send(( _ = broadcast.send((
Some(id.clone()), SendTo::User(id.clone()),
api::Response::Login(api::login::LoginResult { api::Response::Login(api::login::LoginResult {
success: false, success: false,
username: login.username, username: login.username,
@ -131,15 +131,18 @@ impl GameServer {
self.chat.push((id.clone(), msg.clone())); self.chat.push((id.clone(), msg.clone()));
if msg.whisper.is_some() { 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 } => { 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 } => { 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 } => { api::Request::GetTokens { scene } => {
for token_id in self.game.available_tokens(scene, self.is_admin(&id)) { for token_id in self.game.available_tokens(scene, self.is_admin(&id)) {
@ -147,7 +150,7 @@ impl GameServer {
(ti.visible_to_players) (ti.visible_to_players)
{ {
_ = broadcast.send(( _ = broadcast.send((
Some(id.clone()), SendTo::User(id.clone()),
api::Response::SpawnToken(SpawnToken { api::Response::SpawnToken(SpawnToken {
token_id: token_id, token_id: token_id,
x: ti.x, x: ti.x,
@ -160,14 +163,14 @@ impl GameServer {
} }
api::Request::GetScene { id: scene_id } => { api::Request::GetScene { id: scene_id } => {
if let Some(response) = self.get_scene(&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 } => { api::Request::CreateScene { title } => {
if self.is_admin(&id) { if self.is_admin(&id) {
self.game.create_scene(title); self.game.create_scene(title);
let scenes = self.get_scene_list(true); let scenes = self.get_scene_list(true);
_ = broadcast.send((Some(id), scenes)); _ = broadcast.send((SendTo::User(id), scenes));
} }
} }
api::Request::SetSceneGrid { api::Request::SetSceneGrid {
@ -180,7 +183,11 @@ impl GameServer {
if let Some(map) = s.map.as_mut() { if let Some(map) = s.map.as_mut() {
map.grid_cell_size = grid_cell_size; map.grid_cell_size = grid_cell_size;
map.grid_offset = grid_offset; 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) { if let Some(scene) = self.get_scene(&id, scene) {
_ = broadcast.send((return_id, scene)); _ = broadcast.send((return_id, scene));
} }
@ -191,12 +198,12 @@ impl GameServer {
api::Request::SceneSetVisible { scene, visible } => { api::Request::SceneSetVisible { scene, visible } => {
if self.is_admin(&id) { if self.is_admin(&id) {
self.game.scene_visible(scene, visible); self.game.scene_visible(scene, visible);
_ = broadcast.send((None, self.get_scene_list(false))); _ = broadcast.send((SendTo::AllBut(id.clone()), self.get_scene_list(false)));
_ = broadcast.send((Some(id), self.get_scene_list(true))); _ = broadcast.send((SendTo::User(id), self.get_scene_list(true)));
} }
} }
api::Request::GetSceneList => { 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 { api::Request::SpawnToken {
map_id, map_id,
@ -213,9 +220,9 @@ impl GameServer {
.create_token(map_id, character, img_path.clone(), x, y, visible_to_players); .create_token(map_id, character, img_path.clone(), x, y, visible_to_players);
_ = broadcast.send(( _ = broadcast.send((
if visible_to_players && scene_visible { if visible_to_players && scene_visible {
None SendTo::All
} else { } else {
Some(id) SendTo::User(id)
}, },
api::Response::SpawnToken(SpawnToken { api::Response::SpawnToken(SpawnToken {
token_id, token_id,
@ -231,7 +238,7 @@ impl GameServer {
if self.game.move_token(0, token_id, x, y) { 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: 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 // 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) => { api::Request::ActionResult(result) => {
@ -249,27 +256,27 @@ impl GameServer {
.targets(Some(result.targets)) .targets(Some(result.targets))
.id(self.chat.len() + 1); .id(self.chat.len() + 1);
self.chat.push((id, msg.clone())); self.chat.push((id, msg.clone()));
_ = broadcast.send((None, api::Response::Message(msg))); _ = broadcast.send((SendTo::All, api::Response::Message(msg)));
} }
api::Request::CreateCharacter => { api::Request::CreateCharacter => {
// check if user is admin // check if user is admin
if self.is_admin(&id) { if self.is_admin(&id) {
let new_id = self.game.create_character(); let new_id = self.game.create_character();
// return the new id with the character i think // 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 => { api::Request::Quit => {
if self.users.contains_key(&id) { if self.users.contains_key(&id) {
self.users.remove(&id); self.users.remove(&id);
} }
_ = broadcast.send((None, api::Response::Quit { id })); _ = broadcast.send((SendTo::All, api::Response::Quit { id }));
} }
api::Request::Kick(id) => { api::Request::Kick(id) => {
if self.users.contains_key(&id) { if self.users.contains_key(&id) {
self.users.remove(&id); self.users.remove(&id);
} }
_ = broadcast.send((Some(id), api::Response::Shutdown)); _ = broadcast.send((SendTo::User(id), api::Response::Shutdown));
} }
api::Request::Shutdown => { api::Request::Shutdown => {
if self.is_admin(&id) { if self.is_admin(&id) {
@ -292,7 +299,7 @@ impl GameServer {
self.save().await; 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 :) 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) 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, SinkExt, StreamExt,
stream::{SplitSink, SplitStream}, 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::sync::{broadcast, mpsc};
use tokio_util::io::ReaderStream; use tokio_util::io::ReaderStream;
@ -55,7 +58,7 @@ async fn run_web_server(listener: tokio::net::TcpListener, app: Router) {
async fn ws_handler( async fn ws_handler(
ws: ws::WebSocketUpgrade, ws: ws::WebSocketUpgrade,
msend: mpsc::Sender<(String, Request)>, msend: mpsc::Sender<(String, Request)>,
brecv: broadcast::Receiver<(Option<String>, Response)>, brecv: broadcast::Receiver<(SendTo, Response)>,
) -> impl axum::response::IntoResponse { ) -> impl axum::response::IntoResponse {
ws.on_upgrade(|w| handle_socket(w, msend, brecv)) 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( async fn socket_sender(
id: String, id: String,
mut send: SplitSink<ws::WebSocket, ws::Message>, 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 { 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); println!("Sending a message to {}: {:?}", &id, &msg);
let err = send let err = send
.send(ws::Message::Text(serde_json::to_string(&msg).unwrap().into())) .send(ws::Message::Text(serde_json::to_string(&msg).unwrap().into()))
@ -110,7 +113,7 @@ async fn socket_sender(
async fn handle_socket( async fn handle_socket(
mut socket: ws::WebSocket, mut socket: ws::WebSocket,
msend: mpsc::Sender<(String, Request)>, msend: mpsc::Sender<(String, Request)>,
mut brecv: broadcast::Receiver<(Option<String>, Response)>, mut brecv: broadcast::Receiver<(SendTo, Response)>,
) { ) {
let mut id: Option<String> = None; 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) // 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); println!("{} trying to log in: {:?}", &temp_id, &b);
if let Ok((to_id, msg)) = b { if let Ok((to_id, msg)) = b {
if let Response::Login(open_tavern::api::login::LoginResult { success, username }) = &msg { 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) { 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; _ = socket.send(Message::Text(serde_json::to_string(&msg).unwrap_or_default().into())).await;
break; break;