Finish rewriting socket request handling

This commit is contained in:
2026-05-18 09:50:40 -04:00
committed by savanni
parent 2c085ac22f
commit d633b5275c

View File

@@ -10,9 +10,11 @@ use visions_core::{
commands::{GmGame, PlayGame, SpectateGame},
substrate::Substrate,
types::User,
Fatal,
Error, Fatal,
};
use visions_types::{
identifier, GameId, GameMessage, GameOverview, GameRequest, SessionId, Tabletop, UserId,
};
use visions_types::{identifier, GameId, GameMessage, GameRequest, SessionId, Tabletop, UserId};
use crate::{executor::invoke, AppData};
@@ -43,8 +45,9 @@ impl Socket {
pub enum SocketState {
Connected,
Authenticated(SessionId, User),
InGame(SessionId, User, GameId),
Spectator(GameId, Option<(SessionId, UserId)>),
Gm(SessionId, User, GameId),
Player(SessionId, User, GameId),
Spectator(GameId, Option<(SessionId, User)>),
}
#[derive(Clone)]
@@ -74,8 +77,9 @@ impl SocketManager {
.filter(|s| match s.state {
SocketState::Connected => false,
SocketState::Authenticated(_, ref user) => user.id == user_id,
SocketState::InGame(_, ref user, _) => user.id == user_id,
SocketState::Spectator(_, Some((_, ref uid))) => *uid == user_id,
SocketState::Gm(_, ref user, _) => user.id == user_id,
SocketState::Player(_, ref user, _) => user.id == user_id,
SocketState::Spectator(_, Some((_, ref user))) => user.id == user_id,
SocketState::Spectator(_, _) => false,
})
.collect();
@@ -87,7 +91,8 @@ impl SocketManager {
.filter(|s| match s.state {
SocketState::Connected => false,
SocketState::Authenticated(_, _) => false,
SocketState::InGame(_, _, ref game_id_) => *game_id_ == game_id,
SocketState::Gm(_, _, ref game_id_) => *game_id_ == game_id,
SocketState::Player(_, _, ref game_id_) => *game_id_ == game_id,
SocketState::Spectator(ref game_id_, _) => *game_id_ == game_id,
})
.collect();
@@ -176,95 +181,136 @@ pub async fn handle_request(
}
};
let result = if game.gm == user.id {
invoke(
app,
GmGame {
game_id: game_id.clone(),
},
Some(&user),
)
.await?
} else if game.contains_user(&user.id) {
invoke(
app,
PlayGame {
game_id: game_id.clone(),
},
Some(&user),
)
.await?
} else {
invoke(
app,
SpectateGame {
game_id: game_id.clone(),
},
Some(&user),
)
.await?
};
let classification = classify_user(&user, &game);
let result = invoke_join(app, &user, &game.id, classification).await?;
match result {
Ok(messages) => {
match (result, classification) {
(Ok(messages), UserClassification::Spectator) => {
app.socket_manager
.change_socket_state(
&socket_id,
SocketState::InGame(session_id, user, game_id),
SocketState::Spectator(game_id, Some((session_id, user))),
)
.await;
Ok(messages)
}
Err(err) => Ok(vec![GameMessage::Error(err.to_string())]),
(Ok(messages), UserClassification::Player) => {
app.socket_manager
.change_socket_state(
&socket_id,
SocketState::Player(session_id, user, game_id),
)
.await;
Ok(messages)
}
(Ok(messages), UserClassification::Gm) => {
app.socket_manager
.change_socket_state(&socket_id, SocketState::Gm(session_id, user, game_id))
.await;
Ok(messages)
}
(Err(err), _) => Ok(vec![GameMessage::Error(err.to_string())]),
}
}
// Authenticated spectator trying to switch games (might become player)
(
SocketState::Spectator(_, Some((_session_id, _user_id))),
GameRequest::JoinGame(_game_id),
) => {
// Retrieve the user from the session
/*
let user = match self.user(&user_id).await? {
Ok(Some(user)) => user,
Ok(_) => return Ok(Some(GameMessage::PermissionDenied)),
Err(err) => return Ok(Some(GameMessage::Error(err.to_string()))),
};
let game = match self.game(&game_id).await? {
(SocketState::Spectator(_, Some((session_id, user))), GameRequest::JoinGame(game_id)) => {
let game = match app.substrate.game(&game_id).await? {
Ok(game) => game,
Err(err) => {
return Ok(Some(GameMessage::Error(err.to_string())));
Err(_) => {
return Ok(vec![GameMessage::Error("Game not found".to_string())]);
}
};
if game.contains_user(&user.id) {
// User is a player or GM in the game
self.change_websocket_state(
&socket_id,
SocketState::InGame(session_id.clone(), user.clone(), game_id),
)
.await;
Ok(Some(GameMessage::Ok))
} else if game.open_to_spectators {
// User is not a player but game is open to spectators
self.change_websocket_state(
&socket_id,
SocketState::Spectator(
game_id,
Some((session_id.clone(), user.id.clone())),
),
)
.await;
Ok(Some(GameMessage::Ok))
} else {
Ok(Some(GameMessage::PermissionDenied))
let classification = classify_user(&user, &game);
let result = invoke_join(app, &user, &game.id, classification).await?;
match (result, classification) {
(Ok(messages), UserClassification::Spectator) => {
app.socket_manager
.change_socket_state(
&socket_id,
SocketState::Spectator(game_id, Some((session_id, user))),
)
.await;
Ok(messages)
}
(Ok(messages), UserClassification::Player) => {
app.socket_manager
.change_socket_state(
&socket_id,
SocketState::Player(session_id, user, game_id),
)
.await;
Ok(messages)
}
(Ok(messages), UserClassification::Gm) => {
app.socket_manager
.change_socket_state(&socket_id, SocketState::Gm(session_id, user, game_id))
.await;
Ok(messages)
}
(Err(err), _) => Ok(vec![GameMessage::Error(err.to_string())]),
}
*/
Ok(vec![GameMessage::Error("Unhandled".to_owned())])
}
// Spectators cannot send any other messages
(SocketState::Spectator(_, _), _) => Ok(vec![GameMessage::PermissionDenied]),
_ => Ok(vec![GameMessage::InvalidState]),
}
}
#[derive(Copy, Clone)]
enum UserClassification {
Spectator,
Player,
Gm,
}
fn classify_user(user: &User, game: &GameOverview) -> UserClassification {
if game.gm == user.id {
UserClassification::Gm
} else if game.contains_user(&user.id) {
UserClassification::Player
} else {
UserClassification::Spectator
}
}
async fn invoke_join(
app: &AppData,
user: &User,
game_id: &GameId,
classification: UserClassification,
) -> Result<Result<Vec<GameMessage>, Error>, Fatal> {
match classification {
UserClassification::Spectator => {
invoke(
app,
SpectateGame {
game_id: game_id.clone(),
},
Some(&user),
)
.await
}
UserClassification::Player => {
invoke(
app,
PlayGame {
game_id: game_id.clone(),
},
Some(&user),
)
.await
}
UserClassification::Gm => {
invoke(
app,
GmGame {
game_id: game_id.clone(),
},
Some(&user),
)
.await
}
}
}