diff --git a/Cargo.lock b/Cargo.lock
index 805e95d..8ca753e 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -164,6 +164,7 @@ dependencies = [
"serde_json",
"socketioxide",
"sqlx",
+ "thiserror",
"tokio",
"tower-http",
"tracing",
diff --git a/Cargo.toml b/Cargo.toml
index ec78f89..c0e54d5 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -12,6 +12,7 @@ serde = { version = "1.0.210", features = ["derive"] }
serde_json = "1.0.128"
socketioxide = { version = "0.14.1", features = ["state", "tracing"] }
sqlx = { version = "0.8.2", features = ["macros", "postgres", "runtime-tokio"] }
+thiserror = "1.0.63"
tokio = { version = "1.40.0", features = ["full"] }
tower-http = { version = "0.5.2", features = ["cors"] }
tracing = "0.1.40"
diff --git a/app/src/lib/board.svelte b/app/src/lib/board.svelte
index 3d344aa..7273966 100644
--- a/app/src/lib/board.svelte
+++ b/app/src/lib/board.svelte
@@ -19,7 +19,7 @@
{:else if cell === 'h'}
{:else if cell === 'm'}
-
+
{/if}
{/each}
diff --git a/src/game.rs b/src/game.rs
index ef4e838..fd5cbe6 100644
--- a/src/game.rs
+++ b/src/game.rs
@@ -1,11 +1,30 @@
+use std::convert::Infallible;
+
use axum::Json;
use rand::Rng;
use serde::Deserialize;
use socketioxide::socket::Sid;
+use thiserror::Error;
pub const ROOM_CODE_LENGTH: usize = 4;
-pub async fn add_room(sid: Sid, code: String, pool: &sqlx::PgPool) -> Result<(), sqlx::Error> {
+pub type Result = std::result::Result;
+
+#[derive(Debug, Error)]
+pub enum Error {
+ #[error("Room full")]
+ RoomFull,
+ #[error("Room not full")]
+ RoomNotFull,
+ #[error("Already in room")]
+ AlreadyInRoom,
+ #[error("Not in room")]
+ NotInRoom,
+ #[error("SQL Error\n{0:?}")]
+ Sqlx(#[from] sqlx::Error),
+}
+
+pub async fn add_room(sid: Sid, code: String, pool: &sqlx::PgPool) -> Result<()> {
sqlx::query!(
r"WITH new_user AS (INSERT INTO players (id, room_code) VALUES ($1, $2) RETURNING id) INSERT INTO rooms (player1_id, code) SELECT $1, $2 FROM new_user",
sid.as_str(),
@@ -16,7 +35,7 @@ pub async fn add_room(sid: Sid, code: String, pool: &sqlx::PgPool) -> Result<(),
Ok(())
}
-pub async fn join_room(sid: Sid, code: String, pool: &sqlx::PgPool) -> Result<(), sqlx::Error> {
+pub async fn join_room(sid: Sid, code: String, pool: &sqlx::PgPool) -> Result<()> {
let room = sqlx::query!(
r#"SELECT player1_id, player2_id FROM rooms WHERE code = $1"#,
code
@@ -27,16 +46,16 @@ pub async fn join_room(sid: Sid, code: String, pool: &sqlx::PgPool) -> Result<()
let sid = sid.as_str();
if room.player1_id.is_some() && room.player2_id.is_some() {
- return Err(sqlx::Error::RowNotFound); // room full
+ return Err(Error::RoomFull);
}
if let Some(id) = room.player1_id.as_ref() {
if id == sid {
- return Err(sqlx::Error::RowNotFound); // already in room
+ return Err(Error::AlreadyInRoom);
}
}
if let Some(id) = room.player2_id.as_ref() {
if id == sid {
- return Err(sqlx::Error::RowNotFound); // already in room
+ return Err(Error::AlreadyInRoom);
}
}
@@ -65,7 +84,7 @@ pub async fn join_room(sid: Sid, code: String, pool: &sqlx::PgPool) -> Result<()
Ok(())
}
-pub async fn add_board(sid: Sid, board: Board, pool: &sqlx::PgPool) -> Result<(), sqlx::Error> {
+pub async fn add_board(sid: Sid, board: Board, pool: &sqlx::PgPool) -> Result<()> {
let query = format!(
"UPDATE players SET board = ARRAY[{}] WHERE id = '{}'",
board
@@ -84,11 +103,11 @@ pub async fn add_board(sid: Sid, board: Board, pool: &sqlx::PgPool) -> Result<()
.join(","),
sid.as_str()
);
- sqlx::query(&query).execute(pool).await.unwrap();
+ sqlx::query(&query).execute(pool).await?;
Ok(())
}
-pub async fn start(sid: Sid, code: String, pool: &sqlx::PgPool) -> Result<(), sqlx::Error> {
+pub async fn start(sid: Sid, code: String, pool: &sqlx::PgPool) -> Result<()> {
let room = sqlx::query!(
r"SELECT player1_id, player2_id FROM rooms WHERE code = $1",
code
@@ -97,7 +116,7 @@ pub async fn start(sid: Sid, code: String, pool: &sqlx::PgPool) -> Result<(), sq
.await?;
let (Some(player1), Some(player2)) = (room.player1_id, room.player2_id) else {
- return Err(sqlx::Error::RowNotFound); // room not full
+ return Err(Error::RoomNotFull); // room not full
};
let status = if sid.as_str() == player1 {
@@ -105,7 +124,7 @@ pub async fn start(sid: Sid, code: String, pool: &sqlx::PgPool) -> Result<(), sq
} else if sid.as_str() == player2 {
Status::P1Turn
} else {
- return Err(sqlx::Error::RowNotFound); // not in room
+ return Err(Error::NotInRoom); // not in room
};
sqlx::query!(
@@ -118,11 +137,7 @@ pub async fn start(sid: Sid, code: String, pool: &sqlx::PgPool) -> Result<(), sq
Ok(())
}
-pub async fn attack(
- sid: Sid,
- (i, j): (usize, usize),
- pool: &sqlx::PgPool,
-) -> Result {
+pub async fn attack(sid: Sid, (i, j): (usize, usize), pool: &sqlx::PgPool) -> Result {
let player = sqlx::query!(r"SELECT room_code FROM players WHERE id = $1", sid.as_str())
.fetch_one(pool)
.await?;
@@ -141,7 +156,7 @@ pub async fn attack(
(Some(p1), Some(p2)) if p2 == sid.as_str() && room.stat == Status::P2Turn => {
(p2, p1, Status::P1Turn)
}
- _ => return Err(sqlx::Error::RowNotFound), // room not full
+ _ => return Err(Error::RoomNotFull), // room not full
};
let mut txn = pool.begin().await?;
@@ -182,11 +197,10 @@ pub async fn attack(
Ok(turn.hit.unwrap() == "s")
}
-pub async fn disconnect(sid: Sid, pool: &sqlx::PgPool) -> Result<(), sqlx::Error> {
+pub async fn disconnect(sid: Sid, pool: &sqlx::PgPool) -> Result<()> {
sqlx::query!(r"DELETE FROM players WHERE id = $1", sid.as_str())
.execute(pool)
- .await
- .unwrap();
+ .await?;
Ok(())
}
@@ -232,7 +246,7 @@ impl Board {
for i in -1..2 {
for j in -1..=length {
let (tx, ty) = if dir { (x + i, y + j) } else { (x + j, y + i) };
- if tx < 0 || tx >= 10 || ty < 0 || ty >= 10 {
+ if !(0..10).contains(&tx) || !(0..10).contains(&ty) {
continue;
}
if self.0[tx as usize][ty as usize] != 'e' {
diff --git a/src/main.rs b/src/main.rs
index f1ff68f..8c71bc2 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -1,16 +1,12 @@
mod game;
-use std::{str::FromStr, sync::Arc};
use axum::Router;
use dotenv::dotenv;
use futures_util::stream::StreamExt;
use game::{add_board, add_room, attack, disconnect, join_room, start, Board, ROOM_CODE_LENGTH};
use rand::Rng;
-use serde_json::Value;
use socketioxide::{
- adapter::Room,
- extract::{AckSender, Data, SocketRef, State},
- socket::Sid,
+ extract::{Data, SocketRef, State},
SocketIo,
};
use sqlx::PgPool;
@@ -43,16 +39,8 @@ async fn main() -> Result<(), Box> {
Ok(())
}
-fn on_connect(socket: SocketRef, io: SocketIo) {
+fn on_connect(socket: SocketRef) {
tracing::info!("Connected: {:?}", socket.id);
- // tracing::info!(
- // "All rooms and sockets: {:?}",
- // io.rooms()
- // .unwrap()
- // .iter()
- // .map(|room| { (room, io.within(room.clone()).sockets().unwrap()) })
- // );
-
socket.on(
"create",
|socket: SocketRef, pool: State| async move {
@@ -70,7 +58,11 @@ fn on_connect(socket: SocketRef, io: SocketIo) {
.map(|x| char::to_ascii_uppercase(&(x as char)))
.collect();
tracing::info!("Creating room: {:?}", room);
- add_room(socket.id, room.clone(), &pool).await.unwrap();
+
+ if let Err(e) = add_room(socket.id, room.clone(), &pool).await {
+ tracing::error!("{:?}", e);
+ return;
+ }
socket.leave_all().unwrap();
socket.join(room.clone()).unwrap();
socket.emit("created-room", &room).unwrap();
@@ -84,7 +76,10 @@ fn on_connect(socket: SocketRef, io: SocketIo) {
return;
}
tracing::info!("Joining room: {:?}", room);
- join_room(socket.id, room.clone(), &pool).await.unwrap();
+ if let Err(e) = join_room(socket.id, room.clone(), &pool).await {
+ tracing::error!("{:?}", e);
+ return;
+ }
socket.leave_all().unwrap();
socket.join(room.clone()).unwrap();
if socket.within(room.clone()).sockets().unwrap().len() != 2 {
@@ -100,14 +95,21 @@ fn on_connect(socket: SocketRef, io: SocketIo) {
async move {
match ack {
Ok(mut ack) => {
- add_board(id, ack.data.pop().unwrap(), &pool).await.unwrap();
+ if let Err(e) = add_board(id, ack.data.pop().unwrap(), &pool).await
+ {
+ tracing::error!("{:?}", e);
+ return;
+ }
}
Err(err) => tracing::error!("Ack error, {}", err),
}
}
})
.await;
- start(socket.id, room.clone(), &pool).await.unwrap();
+ if let Err(e) = start(socket.id, room.clone(), &pool).await {
+ tracing::error!("{:?}", e);
+ return;
+ }
tracing::info!("Game started");
socket
.within(room.clone())
@@ -119,7 +121,13 @@ fn on_connect(socket: SocketRef, io: SocketIo) {
socket.on(
"attack",
|socket: SocketRef, Data::<[usize; 2]>([i, j]), pool: State| async move {
- let res = attack(socket.id, (i, j), &pool).await.unwrap();
+ let res = match attack(socket.id, (i, j), &pool).await {
+ Ok(res) => res,
+ Err(e) => {
+ tracing::error!("{:?}", e);
+ return;
+ }
+ };
tracing::info!("Attacking at: ({}, {}), result: {}", i, j, res);
socket
.within(socket.rooms().unwrap().first().unwrap().clone())
@@ -134,7 +142,9 @@ fn on_connect(socket: SocketRef, io: SocketIo) {
socket.on_disconnect(|socket: SocketRef, pool: State| async move {
tracing::info!("Disconnecting: {:?}", socket.id);
socket.leave_all().unwrap();
- disconnect(socket.id, &pool).await.unwrap();
- // TODO: Delete room
+ if let Err(e) = disconnect(socket.id, &pool).await {
+ tracing::error!("{:?}", e);
+ return;
+ }
});
}