Multiplayer #1
4 changed files with 83 additions and 27 deletions
|
@ -15,7 +15,7 @@ use tokio::select;
|
||||||
use uuid::Uuid;
|
use uuid::Uuid;
|
||||||
use word_grid::api::{APIGame, ApiState};
|
use word_grid::api::{APIGame, ApiState};
|
||||||
use word_grid::dictionary::{Dictionary, DictionaryImpl};
|
use word_grid::dictionary::{Dictionary, DictionaryImpl};
|
||||||
use word_grid::game::Game;
|
use word_grid::game::{Error, Game, PlayedTile};
|
||||||
use word_grid::player_interaction::ai::Difficulty;
|
use word_grid::player_interaction::ai::Difficulty;
|
||||||
use ws::stream::DuplexStream;
|
use ws::stream::DuplexStream;
|
||||||
use ws::Message;
|
use ws::Message;
|
||||||
|
@ -73,17 +73,29 @@ enum RoomEvent {
|
||||||
#[serde(tag = "type")]
|
#[serde(tag = "type")]
|
||||||
enum ServerToClientMessage {
|
enum ServerToClientMessage {
|
||||||
RoomChange { event: RoomEvent, info: PartyInfo },
|
RoomChange { event: RoomEvent, info: PartyInfo },
|
||||||
GameEvent { state: ApiState },
|
GameEvent { state: ApiState, committed: bool },
|
||||||
|
GameError { error: Error },
|
||||||
Invalid { reason: String },
|
Invalid { reason: String },
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Deserialize, Debug)]
|
||||||
|
enum GameMove {
|
||||||
|
Pass,
|
||||||
|
Exchange {
|
||||||
|
tiles: Vec<bool>,
|
||||||
|
},
|
||||||
|
Play {
|
||||||
|
played_tiles: Vec<Option<PlayedTile>>,
|
||||||
|
commit_move: bool,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Deserialize, Debug)]
|
#[derive(Deserialize, Debug)]
|
||||||
#[serde(tag = "type")]
|
#[serde(tag = "type")]
|
||||||
enum ClientToServerMessage {
|
enum ClientToServerMessage {
|
||||||
RoomChange,
|
|
||||||
Load,
|
Load,
|
||||||
StartGame,
|
StartGame,
|
||||||
GameMove,
|
GameMove { r#move: GameMove },
|
||||||
AddAI { difficulty: Difficulty },
|
AddAI { difficulty: Difficulty },
|
||||||
RemoveAI { index: usize },
|
RemoveAI { index: usize },
|
||||||
}
|
}
|
||||||
|
@ -146,8 +158,9 @@ async fn incoming_message_handler<E: std::fmt::Display>(
|
||||||
// TODO
|
// TODO
|
||||||
println!("Received {message:#?} from client {}", player.id);
|
println!("Received {message:#?} from client {}", player.id);
|
||||||
match message {
|
match message {
|
||||||
ClientToServerMessage::RoomChange => {}
|
ClientToServerMessage::Load => {
|
||||||
ClientToServerMessage::Load => {}
|
return !game_load(player, room, stream).await
|
||||||
|
}
|
||||||
ClientToServerMessage::StartGame => {
|
ClientToServerMessage::StartGame => {
|
||||||
let mut room = room.write().await;
|
let mut room = room.write().await;
|
||||||
if room.game.is_some() {
|
if room.game.is_some() {
|
||||||
|
@ -176,7 +189,47 @@ async fn incoming_message_handler<E: std::fmt::Display>(
|
||||||
sender.send(InnerRoomMessage::GameEvent).unwrap();
|
sender.send(InnerRoomMessage::GameEvent).unwrap();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
ClientToServerMessage::GameMove => {}
|
ClientToServerMessage::GameMove { r#move } => {
|
||||||
|
let mut room = room.write().await;
|
||||||
|
if room.game.is_none() {
|
||||||
|
let event = ServerToClientMessage::Invalid {
|
||||||
|
reason: format!("Game hasn't been started yet"),
|
||||||
|
};
|
||||||
|
let event = serde_json::to_string(&event).unwrap();
|
||||||
|
let _ = stream.send(event.into()).await;
|
||||||
|
} else {
|
||||||
|
let game = room.game.as_mut().unwrap();
|
||||||
|
let result = match r#move {
|
||||||
|
GameMove::Pass => game.pass(&player.name),
|
||||||
|
GameMove::Exchange { tiles } => {
|
||||||
|
game.exchange(&player.name, tiles)
|
||||||
|
}
|
||||||
|
GameMove::Play {
|
||||||
|
played_tiles,
|
||||||
|
commit_move,
|
||||||
|
} => {
|
||||||
|
let result = game.play(&player.name, played_tiles, commit_move);
|
||||||
|
if result.is_ok() & !commit_move {
|
||||||
|
let event = ServerToClientMessage::GameEvent {state: result.unwrap(), committed: false};
|
||||||
|
let event = serde_json::to_string(&event).unwrap();
|
||||||
|
let _ = stream.send(event.into()).await;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
result
|
||||||
|
},
|
||||||
|
};
|
||||||
|
match result {
|
||||||
|
Ok(_) => {
|
||||||
|
sender.send(InnerRoomMessage::GameEvent).unwrap();
|
||||||
|
}
|
||||||
|
Err(error) => {
|
||||||
|
let event = ServerToClientMessage::GameError { error };
|
||||||
|
let event = serde_json::to_string(&event).unwrap();
|
||||||
|
let _ = stream.send(event.into()).await;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
ClientToServerMessage::AddAI { difficulty } => {
|
ClientToServerMessage::AddAI { difficulty } => {
|
||||||
let mut room = room.write().await;
|
let mut room = room.write().await;
|
||||||
room.party_info.ais.push(difficulty.clone());
|
room.party_info.ais.push(difficulty.clone());
|
||||||
|
@ -216,6 +269,19 @@ async fn incoming_message_handler<E: std::fmt::Display>(
|
||||||
false
|
false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async fn game_load(player: &Player, room: &Arc<RwLock<Room>>, stream: &mut DuplexStream) -> bool {
|
||||||
|
// The game object was modified; we need to trigger a load from this player's perspective
|
||||||
|
let mut room = room.write().await;
|
||||||
|
|
||||||
|
let state = room.game.as_mut().unwrap().load(&player.name).unwrap();
|
||||||
|
let event = ServerToClientMessage::GameEvent { state, committed: true };
|
||||||
|
|
||||||
|
let text = serde_json::to_string(&event).unwrap();
|
||||||
|
let x = stream.send(text.into()).await;
|
||||||
|
|
||||||
|
x.is_ok()
|
||||||
|
}
|
||||||
|
|
||||||
async fn outgoing_message_handler<E: std::fmt::Debug>(
|
async fn outgoing_message_handler<E: std::fmt::Debug>(
|
||||||
message: Result<InnerRoomMessage, E>,
|
message: Result<InnerRoomMessage, E>,
|
||||||
_sender: &Sender<InnerRoomMessage>,
|
_sender: &Sender<InnerRoomMessage>,
|
||||||
|
@ -225,22 +291,14 @@ async fn outgoing_message_handler<E: std::fmt::Debug>(
|
||||||
) -> bool {
|
) -> bool {
|
||||||
let message = message.unwrap();
|
let message = message.unwrap();
|
||||||
println!("Inner room message - {:#?}", message);
|
println!("Inner room message - {:#?}", message);
|
||||||
let message = match message {
|
return match message {
|
||||||
InnerRoomMessage::PassThrough(event) => serde_json::to_string(&event).unwrap(),
|
InnerRoomMessage::PassThrough(event) => {
|
||||||
InnerRoomMessage::GameEvent => {
|
let text = serde_json::to_string(&event).unwrap();
|
||||||
// The game object was modified; we need to trigger a load from this player's perspective
|
let x = stream.send(text.into()).await;
|
||||||
let mut room = room.write().await;
|
x.is_err()
|
||||||
|
|
||||||
let state = room.game.as_mut().unwrap().load(&player.name).unwrap();
|
|
||||||
let event = ServerToClientMessage::GameEvent { state };
|
|
||||||
|
|
||||||
serde_json::to_string(&event).unwrap()
|
|
||||||
}
|
}
|
||||||
|
InnerRoomMessage::GameEvent => !game_load(player, room, stream).await,
|
||||||
};
|
};
|
||||||
|
|
||||||
let _ = stream.send(message.into()).await;
|
|
||||||
|
|
||||||
false
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[get("/room/<id>?<player_name>")]
|
#[get("/room/<id>?<player_name>")]
|
||||||
|
|
|
@ -46,9 +46,7 @@ impl WasmAPI {
|
||||||
let tray_tile_locations: Vec<Option<PlayedTile>> =
|
let tray_tile_locations: Vec<Option<PlayedTile>> =
|
||||||
serde_wasm_bindgen::from_value(tray_tile_locations).unwrap();
|
serde_wasm_bindgen::from_value(tray_tile_locations).unwrap();
|
||||||
|
|
||||||
let result = self
|
let result = self.0.play("Player", tray_tile_locations, commit_move);
|
||||||
.0
|
|
||||||
.play("Player".to_string(), tray_tile_locations, commit_move);
|
|
||||||
serde_wasm_bindgen::to_value(&result).unwrap()
|
serde_wasm_bindgen::to_value(&result).unwrap()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -180,7 +180,7 @@ impl APIGame {
|
||||||
|
|
||||||
pub fn play(
|
pub fn play(
|
||||||
&mut self,
|
&mut self,
|
||||||
player: String,
|
player: &str,
|
||||||
tray_tile_locations: Vec<Option<PlayedTile>>,
|
tray_tile_locations: Vec<Option<PlayedTile>>,
|
||||||
commit_move: bool,
|
commit_move: bool,
|
||||||
) -> Result<ApiState, Error> {
|
) -> Result<ApiState, Error> {
|
||||||
|
@ -190,7 +190,7 @@ impl APIGame {
|
||||||
let tray = self.0.player_states.get_tray(&player).unwrap().clone();
|
let tray = self.0.player_states.get_tray(&player).unwrap().clone();
|
||||||
let update = Update {
|
let update = Update {
|
||||||
r#type: turn_action,
|
r#type: turn_action,
|
||||||
player,
|
player: player.to_string(),
|
||||||
};
|
};
|
||||||
if commit_move {
|
if commit_move {
|
||||||
self.1.push(update.clone())
|
self.1.push(update.clone())
|
||||||
|
|
|
@ -20,7 +20,7 @@ pub enum Player {
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug, Clone)]
|
||||||
pub enum Error {
|
pub enum Error {
|
||||||
InvalidPlayer(String),
|
InvalidPlayer(String),
|
||||||
WrongTurn(String),
|
WrongTurn(String),
|
||||||
|
|
Loading…
Reference in a new issue