WIP - break server logic into two files
This commit is contained in:
parent
254bbe8723
commit
0f36ab55f2
2 changed files with 142 additions and 132 deletions
134
server/src/lib.rs
Normal file
134
server/src/lib.rs
Normal 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("&", "&")
|
||||||
|
.replace("<", "<")
|
||||||
|
.replace(">", ">");
|
||||||
|
|
||||||
|
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(())
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
|
@ -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("&", "&")
|
|
||||||
.replace("<", "<")
|
|
||||||
.replace(">", ">");
|
|
||||||
|
|
||||||
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,
|
||||||
|
|
Loading…
Reference in a new issue