add db
This commit is contained in:
203
src/game.rs
203
src/game.rs
@@ -1,13 +1,190 @@
|
||||
use std::{collections::HashMap, sync::Arc};
|
||||
|
||||
use axum::Json;
|
||||
use rand::Rng;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use serde::Deserialize;
|
||||
use socketioxide::socket::Sid;
|
||||
use tokio::sync::RwLock;
|
||||
|
||||
#[derive(Debug, Deserialize, Serialize)]
|
||||
pub const ROOM_CODE_LENGTH: usize = 4;
|
||||
|
||||
// #[derive(Default, Clone)]
|
||||
// pub struct Store {
|
||||
// rooms: Arc<RwLock<HashMap<String, Room>>>,
|
||||
// sockets: Arc<RwLock<HashMap<Sid, String>>>,
|
||||
// }
|
||||
|
||||
// impl Store {
|
||||
// pub async fn add_room(&self, code: String) {
|
||||
// let mut store = self.rooms.write().await;
|
||||
// store.insert(
|
||||
// code.clone(),
|
||||
// Room {
|
||||
// code,
|
||||
// ..Default::default()
|
||||
// },
|
||||
// );
|
||||
// }
|
||||
pub async fn add_room(code: String, pool: &sqlx::PgPool) -> Result<(), sqlx::Error> {
|
||||
sqlx::query!("INSERT INTO rooms (code) VALUES ($1)", code)
|
||||
.execute(pool)
|
||||
.await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub async fn join_room(sid: Sid, code: String, pool: &sqlx::PgPool) -> Result<(), sqlx::Error> {
|
||||
let room = sqlx::query!(
|
||||
r#"SELECT player1_id, player2_id FROM rooms WHERE code = $1"#,
|
||||
code
|
||||
)
|
||||
.fetch_one(pool)
|
||||
.await?;
|
||||
|
||||
if room.player1_id.is_some() && room.player2_id.is_some() {
|
||||
return Err(sqlx::Error::RowNotFound); // room full
|
||||
}
|
||||
|
||||
let mut txn = pool.begin().await?;
|
||||
|
||||
sqlx::query!(
|
||||
r#"INSERT INTO players (id, room_code) VALUES ($1, $2) ON CONFLICT (id) DO UPDATE SET room_code = $2"#,
|
||||
sid.as_str(),
|
||||
code
|
||||
)
|
||||
.execute(&mut *txn)
|
||||
.await?;
|
||||
|
||||
sqlx::query(&format!(
|
||||
"UPDATE rooms SET player{}_id = $1 WHERE code = $2",
|
||||
if room.player1_id.is_none() { "1" } else { "2" }
|
||||
))
|
||||
.bind(sid.as_str())
|
||||
.bind(code)
|
||||
.execute(&mut *txn)
|
||||
.await?;
|
||||
|
||||
txn.commit().await?;
|
||||
Ok(())
|
||||
}
|
||||
// pub async fn join_room(&self, sid: Sid, code: String) -> Result<(), ()> {
|
||||
// if self.rooms.read().await.get(&code).is_none() {
|
||||
// return Err(());
|
||||
// };
|
||||
// let mut sockets = self.sockets.write().await;
|
||||
// let player = sockets
|
||||
// .entry(sid)
|
||||
// .and_modify(|p| p.room = Some(code.clone()))
|
||||
// .or_insert(Arc::new(Player {
|
||||
// sid,
|
||||
// room: Some(code.clone()),
|
||||
// board: None,
|
||||
// }));
|
||||
// let mut rooms = self.rooms.write().await;
|
||||
// let Some(room) = rooms.get_mut(&code) else {
|
||||
// return Err(());
|
||||
// };
|
||||
|
||||
// if room.player1.is_none() {
|
||||
// room.player1 = Some(Arc::clone(player));
|
||||
// }
|
||||
// Ok(())
|
||||
// }
|
||||
|
||||
// pub async fn add_board(&self, sid: Sid, board: Board) -> Result<(), ()> {
|
||||
// let mut store = self.sockets.write().await;
|
||||
// if let Some(player) = store.get_mut(&sid) {
|
||||
// player.board = Some(board);
|
||||
// } else {
|
||||
// return Err(());
|
||||
// }
|
||||
// Ok(())
|
||||
// }
|
||||
|
||||
// pub async fn start(&self, code: String, sid: Sid) -> Result<(), ()> {
|
||||
// let mut store = self.rooms.write().await;
|
||||
// let Some(room) = store.get_mut(&code) else {
|
||||
// return Err(());
|
||||
// };
|
||||
// dbg!(&room);
|
||||
// let (Some(player1), Some(player2)) = (room.player1, room.player2) else {
|
||||
// return Err(());
|
||||
// };
|
||||
|
||||
// if player1.sid == sid {
|
||||
// room.status = Status::Player1Turn;
|
||||
// } else if player2.sid == sid {
|
||||
// room.status = Status::Player2Turn;
|
||||
// } else {
|
||||
// return Err(());
|
||||
// }
|
||||
// Ok(())
|
||||
// }
|
||||
|
||||
// pub async fn attack(&self, sid: Sid, (i, j): (usize, usize)) -> Result<bool, ()> {
|
||||
// let sockets = self.sockets.read().await;
|
||||
// let Some(player) = sockets.get(&sid) else {
|
||||
// return Err(());
|
||||
// };
|
||||
// let mut rooms = self.rooms.write().await;
|
||||
// let Some(room) = rooms.get_mut(player.room.as_ref().unwrap()) else {
|
||||
// return Err(());
|
||||
// };
|
||||
|
||||
// match room.status {
|
||||
// Status::Player1Turn if player.sid == room.player1.as_ref().unwrap().sid => {
|
||||
// room.status = Status::Player2Turn;
|
||||
// return Ok(room.player2.as_ref().unwrap().board.as_ref().unwrap().0[i][j] == 's');
|
||||
// }
|
||||
// Status::Player2Turn if player.sid == room.player2.as_ref().unwrap().sid => {
|
||||
// room.status = Status::Player1Turn;
|
||||
// return Ok(room.player1.as_ref().unwrap().board.as_ref().unwrap().0[i][j] == 's');
|
||||
// }
|
||||
// _ => return Err(()),
|
||||
// }
|
||||
|
||||
// Err(())
|
||||
// }
|
||||
// }
|
||||
|
||||
// #[derive(Default, Debug)]
|
||||
// struct Room {
|
||||
// code: String,
|
||||
// player1: Option<Arc<Player>>,
|
||||
// player2: Option<Arc<Player>>,
|
||||
// status: Status,
|
||||
// }
|
||||
|
||||
// #[derive(Debug)]
|
||||
// struct Player {
|
||||
// sid: Sid,
|
||||
// board: Option<Board>,
|
||||
// room: Option<String>,
|
||||
// }
|
||||
|
||||
#[derive(Debug, sqlx::Type)]
|
||||
#[sqlx(type_name = "STAT", rename_all = "lowercase")]
|
||||
enum Status {
|
||||
Waiting,
|
||||
P1Turn,
|
||||
P2Turn,
|
||||
}
|
||||
|
||||
// impl Default for Status {
|
||||
// fn default() -> Self {
|
||||
// Status::Waiting
|
||||
// }
|
||||
// }
|
||||
|
||||
#[derive(Debug, Deserialize)]
|
||||
pub struct Board(pub [[char; 10]; 10]);
|
||||
|
||||
impl Board {
|
||||
const SHIPS: [i32; 5] = [5, 4, 3, 3, 2];
|
||||
|
||||
pub fn from_json(Json(board): Json<Board>) -> Self {
|
||||
board
|
||||
}
|
||||
|
||||
pub fn randomize() -> Self {
|
||||
let mut board = Board([['e'; 10]; 10]);
|
||||
for &length in Self::SHIPS.iter() {
|
||||
@@ -43,18 +220,14 @@ impl Board {
|
||||
false
|
||||
}
|
||||
|
||||
pub async fn from_json(Json(board): Json<Board>) -> Self {
|
||||
board
|
||||
}
|
||||
|
||||
fn validate_syntax(&self) -> bool {
|
||||
self.0
|
||||
.iter()
|
||||
.all(|row| row.iter().all(|cell| matches!(cell, 'e' | 'h' | 'm' | 's')))
|
||||
}
|
||||
// fn validate_syntax(&self) -> bool {
|
||||
// self.0
|
||||
// .iter()
|
||||
// .all(|row| row.iter().all(|cell| matches!(cell, 'e' | 'h' | 'm' | 's')))
|
||||
// }
|
||||
}
|
||||
|
||||
pub async fn create_board_route(board: Json<Board>) -> String {
|
||||
let board = Board::from_json(board).await;
|
||||
format!("{:?}", board)
|
||||
}
|
||||
// pub async fn create_board_route(board: Json<Board>) -> Json<String> {
|
||||
// let board = Board::from_json(board).await;
|
||||
// Json(format!("{:?}", board.0))
|
||||
// }
|
||||
|
137
src/main.rs
137
src/main.rs
@@ -1,18 +1,135 @@
|
||||
mod game;
|
||||
use std::{str::FromStr, sync::Arc};
|
||||
|
||||
use axum::{
|
||||
routing::{get, post},
|
||||
Json, Router,
|
||||
use axum::Router;
|
||||
use dotenv::dotenv;
|
||||
use futures_util::stream::StreamExt;
|
||||
use game::{join_room, Board, ROOM_CODE_LENGTH};
|
||||
use rand::Rng;
|
||||
use serde_json::Value;
|
||||
use socketioxide::{
|
||||
adapter::Room,
|
||||
extract::{AckSender, Data, SocketRef, State},
|
||||
socket::Sid,
|
||||
SocketIo,
|
||||
};
|
||||
use game::Board;
|
||||
use serde::Serialize;
|
||||
use tokio::net::TcpListener;
|
||||
use tower_http::cors::CorsLayer;
|
||||
use tracing_subscriber::FmtSubscriber;
|
||||
|
||||
#[tokio::main]
|
||||
async fn main() {
|
||||
let app = Router::new().route("/", post(game::create_board_route));
|
||||
async fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
tracing::subscriber::set_global_default(
|
||||
FmtSubscriber::builder()
|
||||
.with_max_level(tracing::Level::INFO)
|
||||
.finish(),
|
||||
)?;
|
||||
let _ = dotenv();
|
||||
let url = std::env::var("DATABASE_URL")?;
|
||||
let pool = sqlx::postgres::PgPool::connect(&url).await?;
|
||||
sqlx::migrate!("./migrations").run(&pool).await?;
|
||||
join_room(
|
||||
Sid::from_str("aaaaaaaaaaaaaaaa").unwrap(),
|
||||
"AAAB".to_string(),
|
||||
&pool,
|
||||
)
|
||||
.await?;
|
||||
let (layer, io) = SocketIo::builder().with_state(pool).build_layer();
|
||||
// io.ns("/", on_connect);
|
||||
let app = Router::new()
|
||||
// .route("/", post(game::create_board_route))
|
||||
.layer(layer)
|
||||
.layer(CorsLayer::very_permissive());
|
||||
|
||||
let listener = TcpListener::bind("127.0.0.1:3000").await.unwrap();
|
||||
println!("listening on {}", listener.local_addr().unwrap());
|
||||
axum::serve(listener, app).await.unwrap();
|
||||
let listener = TcpListener::bind("127.0.0.1:3000").await?;
|
||||
println!("listening on {}", listener.local_addr()?);
|
||||
axum::serve(listener, app).await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
// fn on_connect(socket: SocketRef, io: SocketIo) {
|
||||
// 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, store: State<Store>| async move {
|
||||
// if !socket.rooms().unwrap().is_empty() {
|
||||
// socket
|
||||
// .emit("created-room", socket.rooms().unwrap().first())
|
||||
// .unwrap();
|
||||
// println!("{} Already in a room", socket.id);
|
||||
// return;
|
||||
// }
|
||||
|
||||
// let room: String = rand::thread_rng()
|
||||
// .sample_iter(&rand::distributions::Alphanumeric)
|
||||
// .take(ROOM_CODE_LENGTH)
|
||||
// .map(|x| char::to_ascii_uppercase(&(x as char)))
|
||||
// .collect();
|
||||
// tracing::info!("Creating room: {:?}", room);
|
||||
// store.add_room(room.clone()).await;
|
||||
// store.join_room(socket.id, room.clone()).await.unwrap();
|
||||
// socket.leave_all().unwrap();
|
||||
// socket.join(room.clone()).unwrap();
|
||||
// socket.emit("created-room", &room).unwrap();
|
||||
// },
|
||||
// );
|
||||
|
||||
// socket.on(
|
||||
// "join",
|
||||
// |socket: SocketRef, Data::<String>(room), store: State<Store>| async move {
|
||||
// if room.len() != ROOM_CODE_LENGTH {
|
||||
// return;
|
||||
// }
|
||||
// tracing::info!("Joining room: {:?}", room);
|
||||
// store.join_room(socket.id, room.clone()).await.unwrap();
|
||||
// socket.leave_all().unwrap();
|
||||
// socket.join(room.clone()).unwrap();
|
||||
// if socket.within(room.clone()).sockets().unwrap().len() != 2 {
|
||||
// return;
|
||||
// }
|
||||
// let ack_stream = socket
|
||||
// .within(room.clone())
|
||||
// .emit_with_ack::<Vec<Board>>("upload", ())
|
||||
// .unwrap();
|
||||
// ack_stream
|
||||
// .for_each(|(id, ack)| {
|
||||
// let store = store.clone();
|
||||
// async move {
|
||||
// match ack {
|
||||
// Ok(mut ack) => {
|
||||
// store.add_board(id, ack.data.pop().unwrap()).await.unwrap();
|
||||
// }
|
||||
// Err(err) => tracing::error!("Ack error, {}", err),
|
||||
// }
|
||||
// }
|
||||
// })
|
||||
// .await;
|
||||
// store.start(room.clone(), socket.id).await.unwrap();
|
||||
// tracing::info!("Game started");
|
||||
// socket.within(room.clone()).emit("turn", socket.id).unwrap();
|
||||
// },
|
||||
// );
|
||||
|
||||
// socket.on(
|
||||
// "attack",
|
||||
// |socket: SocketRef, Data::<[usize; 2]>([i, j]), ack: AckSender, store: State<Store>| async move {
|
||||
// let res = store.attack(socket.id, (i, j)).await.unwrap();
|
||||
// tracing::info!("Attacking at: ({}, {}), result: {}", i, j, res);
|
||||
// ack.send(res).unwrap();
|
||||
// },
|
||||
// );
|
||||
|
||||
// socket.on_disconnect(|socket: SocketRef, store: State<Store>| {
|
||||
// tracing::info!("Disconnecting: {:?}", socket.id);
|
||||
// socket.leave_all().unwrap();
|
||||
// // TODO: Delete room
|
||||
// });
|
||||
// }
|
||||
|
Reference in New Issue
Block a user