WIP - break server logic into two files

This commit is contained in:
Joel Therrien 2024-12-25 20:40:19 -08:00
parent 254bbe8723
commit 0f36ab55f2
2 changed files with 142 additions and 132 deletions

134
server/src/lib.rs Normal file
View file

@ -0,0 +1,134 @@
use rocket::serde::{Deserialize, Serialize};
use rocket::tokio::sync::broadcast::Sender;
use rocket::tokio::sync::RwLock;
use std::collections::HashMap;
use std::sync::{LazyLock, Weak};
use word_grid::api::{APIGame, ApiState, Update};
use word_grid::dictionary::{Dictionary, DictionaryImpl};
use word_grid::game::{Error, PlayedTile};
use word_grid::player_interaction::ai::Difficulty;
use ws::frame::{CloseCode, CloseFrame};
pub static DICTIONARY: LazyLock<DictionaryImpl> =
LazyLock::new(|| DictionaryImpl::create_from_path("../resources/dictionary.csv"));
pub fn escape_characters(x: &str) -> String {
let x = x
.replace("&", "&amp;")
.replace("<", "&lt;")
.replace(">", "&gt;");
x
}
#[derive(Clone, Debug, Serialize, PartialEq)]
pub struct Player {
pub name: String,
}
#[derive(Clone, Serialize, Debug)]
pub struct PartyInfo {
pub ais: Vec<Difficulty>,
pub players: Vec<Player>,
}
impl PartyInfo {
fn new(host: Player) -> Self {
Self {
ais: Vec::new(),
players: vec![host],
}
}
}
pub struct Room {
pub party_info: PartyInfo,
pub game: Option<APIGame>,
pub sender: Sender<(Option<Player>, InnerRoomMessage)>,
}
impl Room {
pub fn new(host: Player) -> Self {
Self {
party_info: PartyInfo::new(host),
game: None,
sender: Sender::new(5),
}
}
}
#[derive(Clone, Serialize, Debug)]
#[serde(tag = "type")]
pub enum RoomEvent {
PlayerJoined { player: Player },
PlayerLeft { player: Player },
AIJoined { difficulty: Difficulty },
AILeft { index: usize },
}
#[derive(Clone, Serialize, Debug)]
#[serde(tag = "type")]
pub enum GameEvent {
TurnAction { state: ApiState, committed: bool },
}
#[derive(Clone, Serialize, Debug)]
#[serde(tag = "type")]
pub enum ServerToClientMessage {
RoomChange { event: RoomEvent, info: PartyInfo },
GameEvent { event: GameEvent },
GameError { error: Error },
Invalid { reason: String },
}
#[derive(Deserialize, Debug)]
#[serde(tag = "type")]
pub enum GameMove {
Pass,
Exchange {
tiles: Vec<bool>,
},
Play {
played_tiles: Vec<Option<PlayedTile>>,
commit_move: bool,
},
AddToDictionary {
word: String,
},
}
#[derive(Deserialize, Debug)]
#[serde(tag = "type")]
pub enum ClientToServerMessage {
Load,
StartGame,
GameMove { r#move: GameMove },
AddAI { difficulty: Difficulty },
RemoveAI { index: usize },
}
#[derive(Clone, Debug)]
pub enum InnerRoomMessage {
PassThrough(ServerToClientMessage),
GameEvent(Option<Update>),
}
pub type RoomMap = HashMap<String, Weak<RwLock<Room>>>;
pub fn reject_websocket_with_reason(
ws: ws::WebSocket,
close_code: CloseCode,
reason: String,
) -> ws::Channel<'static> {
ws.channel(move |mut stream| {
Box::pin(async move {
let closeframe = CloseFrame {
code: close_code,
reason: reason.into(),
};
let _ = stream.close(Some(closeframe)).await;
Ok(())
})
})
}

View file

@ -10,125 +10,19 @@ use rocket::tokio::sync::broadcast::Sender;
use rocket::tokio::sync::{Mutex, RwLock}; use rocket::tokio::sync::{Mutex, RwLock};
use rocket::tokio::time::interval; use rocket::tokio::time::interval;
use rocket::{tokio, State}; use rocket::{tokio, State};
use serde::{Deserialize, Serialize}; use server::{
use std::collections::HashMap; escape_characters, reject_websocket_with_reason, ClientToServerMessage, GameEvent, GameMove,
use std::sync::{Arc, LazyLock, Weak}; InnerRoomMessage, Player, Room, RoomEvent, RoomMap, ServerToClientMessage, DICTIONARY,
};
use std::sync::Arc;
use std::time::Duration; use std::time::Duration;
use tokio::select; use tokio::select;
use word_grid::api::{APIGame, ApiState, Update}; use word_grid::api::{APIGame, Update};
use word_grid::dictionary::{Dictionary, DictionaryImpl}; use word_grid::game::Game;
use word_grid::game::{Error, Game, PlayedTile}; use ws::frame::CloseCode;
use word_grid::player_interaction::ai::Difficulty;
use ws::frame::{CloseCode, CloseFrame};
use ws::stream::DuplexStream; use ws::stream::DuplexStream;
use ws::Message; use ws::Message;
static DICTIONARY: LazyLock<DictionaryImpl> =
LazyLock::new(|| DictionaryImpl::create_from_path("../resources/dictionary.csv"));
fn escape_characters(x: &str) -> String {
let x = x
.replace("&", "&amp;")
.replace("<", "&lt;")
.replace(">", "&gt;");
x
}
#[derive(Clone, Debug, Serialize, PartialEq)]
struct Player {
name: String,
}
#[derive(Clone, Serialize, Debug)]
struct PartyInfo {
ais: Vec<Difficulty>,
players: Vec<Player>,
}
impl PartyInfo {
fn new(host: Player) -> Self {
Self {
ais: Vec::new(),
players: vec![host],
}
}
}
struct Room {
party_info: PartyInfo,
game: Option<APIGame>,
sender: Sender<(Option<Player>, InnerRoomMessage)>,
}
impl Room {
fn new(host: Player) -> Self {
Self {
party_info: PartyInfo::new(host),
game: None,
sender: Sender::new(5),
}
}
}
#[derive(Clone, Serialize, Debug)]
#[serde(tag = "type")]
enum RoomEvent {
PlayerJoined { player: Player },
PlayerLeft { player: Player },
AIJoined { difficulty: Difficulty },
AILeft { index: usize },
}
#[derive(Clone, Serialize, Debug)]
#[serde(tag = "type")]
enum GameEvent {
TurnAction { state: ApiState, committed: bool },
}
#[derive(Clone, Serialize, Debug)]
#[serde(tag = "type")]
enum ServerToClientMessage {
RoomChange { event: RoomEvent, info: PartyInfo },
GameEvent { event: GameEvent },
GameError { error: Error },
Invalid { reason: String },
}
#[derive(Deserialize, Debug)]
#[serde(tag = "type")]
enum GameMove {
Pass,
Exchange {
tiles: Vec<bool>,
},
Play {
played_tiles: Vec<Option<PlayedTile>>,
commit_move: bool,
},
AddToDictionary {
word: String,
},
}
#[derive(Deserialize, Debug)]
#[serde(tag = "type")]
enum ClientToServerMessage {
Load,
StartGame,
GameMove { r#move: GameMove },
AddAI { difficulty: Difficulty },
RemoveAI { index: usize },
}
#[derive(Clone, Debug)]
enum InnerRoomMessage {
PassThrough(ServerToClientMessage),
GameEvent(Option<Update>),
}
type RoomMap = HashMap<String, Weak<RwLock<Room>>>;
async fn incoming_message_handler<E: std::fmt::Display>( async fn incoming_message_handler<E: std::fmt::Display>(
message: Option<Result<Message, E>>, message: Option<Result<Message, E>>,
sender: &Sender<(Option<Player>, InnerRoomMessage)>, sender: &Sender<(Option<Player>, InnerRoomMessage)>,
@ -357,24 +251,6 @@ async fn outgoing_message_handler<E: std::fmt::Debug>(
} }
} }
fn reject_websocket_with_reason(
ws: ws::WebSocket,
close_code: CloseCode,
reason: String,
) -> ws::Channel<'static> {
ws.channel(move |mut stream| {
Box::pin(async move {
let closeframe = CloseFrame {
code: close_code,
reason: reason.into(),
};
let _ = stream.close(Some(closeframe)).await;
Ok(())
})
})
}
#[get("/room/<id>?<player_name>")] #[get("/room/<id>?<player_name>")]
async fn room( async fn room(
id: &str, id: &str,