frontend: WIP! in progress setting up id request
This commit is contained in:
parent
39c8177914
commit
83a9ab2ea0
6 changed files with 315 additions and 62 deletions
1
front_end/Cargo.lock
generated
1
front_end/Cargo.lock
generated
|
@ -43,6 +43,7 @@ dependencies = [
|
||||||
"js-sys",
|
"js-sys",
|
||||||
"log",
|
"log",
|
||||||
"oorandom",
|
"oorandom",
|
||||||
|
"serde_json",
|
||||||
"wasm-bindgen",
|
"wasm-bindgen",
|
||||||
"wasm-bindgen-futures",
|
"wasm-bindgen-futures",
|
||||||
"wasm-logger",
|
"wasm-logger",
|
||||||
|
|
|
@ -9,8 +9,9 @@ edition = "2021"
|
||||||
yew = "0.19"
|
yew = "0.19"
|
||||||
log = "0.4.6"
|
log = "0.4.6"
|
||||||
wasm-logger = "0.2.0"
|
wasm-logger = "0.2.0"
|
||||||
web-sys = { version = "0.3.56", features = ["Window", "Document", "Element"] }
|
web-sys = { version = "0.3.56", features = ["Window", "Document", "Element", "Request", "RequestInit"] }
|
||||||
js-sys = "0.3.56"
|
js-sys = "0.3.56"
|
||||||
oorandom = "11.1.3"
|
oorandom = "11.1.3"
|
||||||
wasm-bindgen = "0.2.79"
|
wasm-bindgen = "0.2.79"
|
||||||
wasm-bindgen-futures = "0.4.29"
|
wasm-bindgen-futures = "0.4.29"
|
||||||
|
serde_json = "1.0"
|
||||||
|
|
|
@ -5,8 +5,14 @@ pub const INFO_TEXT_MAX_ITEMS: u32 = 100;
|
||||||
|
|
||||||
pub const AI_EASY_MAX_CHOICES: usize = 5;
|
pub const AI_EASY_MAX_CHOICES: usize = 5;
|
||||||
pub const AI_NORMAL_MAX_CHOICES: usize = 3;
|
pub const AI_NORMAL_MAX_CHOICES: usize = 3;
|
||||||
|
pub const AI_CHOICE_DURATION_MILLIS: i32 = 1000;
|
||||||
|
|
||||||
pub const PLAYER_COUNT_LIMIT: usize = 1000;
|
pub const PLAYER_COUNT_LIMIT: usize = 1000;
|
||||||
pub const TURN_SECONDS: u64 = 25;
|
pub const TURN_SECONDS: u64 = 25;
|
||||||
pub const GAME_CLEANUP_TIMEOUT: u64 = (TURN_SECONDS + 1) * ((ROWS * COLS) as u64 + 5u64);
|
pub const GAME_CLEANUP_TIMEOUT: u64 = (TURN_SECONDS + 1) * ((ROWS * COLS) as u64 + 5u64);
|
||||||
pub const PLAYER_CLEANUP_TIMEOUT: u64 = 300;
|
pub const PLAYER_CLEANUP_TIMEOUT: u64 = 300;
|
||||||
|
|
||||||
|
pub const BACKEND_TICK_DURATION_MILLIS: i32 = 500;
|
||||||
|
|
||||||
|
// TODO: Change this to "https://asdm.seodisparate.com/api" when backend is installed
|
||||||
|
pub const BACKEND_URL: &str = "http://localhost:1237/";
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
use web_sys::{window, Document, Window};
|
use wasm_bindgen::JsValue;
|
||||||
|
use web_sys::{window, Document, Request, RequestInit, Window};
|
||||||
|
|
||||||
pub fn get_window_document() -> Result<(Window, Document), String> {
|
pub fn get_window_document() -> Result<(Window, Document), String> {
|
||||||
let window = window().ok_or_else(|| String::from("Failed to get window"))?;
|
let window = window().ok_or_else(|| String::from("Failed to get window"))?;
|
||||||
|
@ -77,3 +78,14 @@ pub fn element_remove_class(document: &Document, id: &str, class: &str) -> Resul
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn create_json_request(target_url: &str, json_body: &str) -> Result<Request, String> {
|
||||||
|
let mut req_init: RequestInit = RequestInit::new();
|
||||||
|
req_init.body(Some(&JsValue::from_str(json_body)));
|
||||||
|
req_init.headers(
|
||||||
|
&JsValue::from_serde("'headers': { 'Content-Type': 'application/json' }")
|
||||||
|
.map_err(|e| format!("{}", e))?,
|
||||||
|
);
|
||||||
|
|
||||||
|
Ok(Request::new_with_str_and_init(target_url, &req_init).map_err(|e| format!("{:?}", e))?)
|
||||||
|
}
|
||||||
|
|
|
@ -12,10 +12,27 @@ pub enum GameState {
|
||||||
MainMenu,
|
MainMenu,
|
||||||
SinglePlayer(Turn, AIDifficulty),
|
SinglePlayer(Turn, AIDifficulty),
|
||||||
LocalMultiplayer,
|
LocalMultiplayer,
|
||||||
NetworkedMultiplayer(Turn),
|
NetworkedMultiplayer {
|
||||||
|
paired: bool,
|
||||||
|
current_side: Option<Turn>,
|
||||||
|
current_turn: Turn,
|
||||||
|
},
|
||||||
PostGameResults(BoardState),
|
PostGameResults(BoardState),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl GameState {
|
||||||
|
pub fn is_networked_multiplayer(self) -> bool {
|
||||||
|
matches!(
|
||||||
|
self,
|
||||||
|
GameState::NetworkedMultiplayer {
|
||||||
|
paired,
|
||||||
|
current_side,
|
||||||
|
current_turn
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl Default for GameState {
|
impl Default for GameState {
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
GameState::MainMenu
|
GameState::MainMenu
|
||||||
|
@ -27,7 +44,11 @@ impl From<MainMenuMessage> for GameState {
|
||||||
match msg {
|
match msg {
|
||||||
MainMenuMessage::SinglePlayer(t, ai) => GameState::SinglePlayer(t, ai),
|
MainMenuMessage::SinglePlayer(t, ai) => GameState::SinglePlayer(t, ai),
|
||||||
MainMenuMessage::LocalMultiplayer => GameState::LocalMultiplayer,
|
MainMenuMessage::LocalMultiplayer => GameState::LocalMultiplayer,
|
||||||
MainMenuMessage::NetworkedMultiplayer(t) => GameState::NetworkedMultiplayer(t),
|
MainMenuMessage::NetworkedMultiplayer => GameState::NetworkedMultiplayer {
|
||||||
|
paired: false,
|
||||||
|
current_side: None,
|
||||||
|
current_turn: Turn::CyanPlayer,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -298,7 +319,7 @@ impl Default for SharedState {
|
||||||
pub enum MainMenuMessage {
|
pub enum MainMenuMessage {
|
||||||
SinglePlayer(Turn, AIDifficulty),
|
SinglePlayer(Turn, AIDifficulty),
|
||||||
LocalMultiplayer,
|
LocalMultiplayer,
|
||||||
NetworkedMultiplayer(Turn),
|
NetworkedMultiplayer,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn new_string_board() -> String {
|
pub fn new_string_board() -> String {
|
||||||
|
@ -402,3 +423,27 @@ pub fn string_from_board(board: BoardType, placed: usize) -> (String, Option<Boa
|
||||||
(board_string, None)
|
(board_string, None)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_is_networked_multiplayer_enum() {
|
||||||
|
let state = GameState::MainMenu;
|
||||||
|
assert!(!state.is_networked_multiplayer());
|
||||||
|
let state = GameState::LocalMultiplayer;
|
||||||
|
assert!(!state.is_networked_multiplayer());
|
||||||
|
let state = GameState::NetworkedMultiplayer {
|
||||||
|
paired: false,
|
||||||
|
current_side: None,
|
||||||
|
current_turn: Turn::CyanPlayer,
|
||||||
|
};
|
||||||
|
assert!(state.is_networked_multiplayer());
|
||||||
|
let state = GameState::NetworkedMultiplayer {
|
||||||
|
paired: true,
|
||||||
|
current_side: Some(Turn::CyanPlayer),
|
||||||
|
current_turn: Turn::MagentaPlayer,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -1,8 +1,12 @@
|
||||||
use crate::ai::{get_ai_choice, AIDifficulty};
|
use crate::ai::{get_ai_choice, AIDifficulty};
|
||||||
use crate::constants::{COLS, INFO_TEXT_MAX_ITEMS, ROWS};
|
use crate::constants::{
|
||||||
|
AI_CHOICE_DURATION_MILLIS, BACKEND_TICK_DURATION_MILLIS, BACKEND_URL, COLS,
|
||||||
|
INFO_TEXT_MAX_ITEMS, ROWS,
|
||||||
|
};
|
||||||
use crate::game_logic::{check_win_draw, WinType};
|
use crate::game_logic::{check_win_draw, WinType};
|
||||||
use crate::html_helper::{
|
use crate::html_helper::{
|
||||||
append_to_info_text, element_append_class, element_remove_class, get_window_document,
|
append_to_info_text, create_json_request, element_append_class, element_remove_class,
|
||||||
|
get_window_document,
|
||||||
};
|
};
|
||||||
use crate::random_helper::get_seeded_random;
|
use crate::random_helper::get_seeded_random;
|
||||||
use crate::state::{BoardState, GameState, MainMenuMessage, SharedState, Turn};
|
use crate::state::{BoardState, GameState, MainMenuMessage, SharedState, Turn};
|
||||||
|
@ -10,8 +14,11 @@ use crate::state::{BoardState, GameState, MainMenuMessage, SharedState, Turn};
|
||||||
use std::cell::Cell;
|
use std::cell::Cell;
|
||||||
use std::rc::Rc;
|
use std::rc::Rc;
|
||||||
|
|
||||||
use js_sys::Promise;
|
use js_sys::{JsString, Promise};
|
||||||
|
|
||||||
|
use serde_json::Value as SerdeJSONValue;
|
||||||
|
|
||||||
|
use wasm_bindgen::{JsCast, JsValue};
|
||||||
use wasm_bindgen_futures::JsFuture;
|
use wasm_bindgen_futures::JsFuture;
|
||||||
|
|
||||||
use yew::prelude::*;
|
use yew::prelude::*;
|
||||||
|
@ -60,6 +67,10 @@ impl Component for MainMenu {
|
||||||
let onclick_local_multiplayer =
|
let onclick_local_multiplayer =
|
||||||
ctx.link().callback(|_| MainMenuMessage::LocalMultiplayer);
|
ctx.link().callback(|_| MainMenuMessage::LocalMultiplayer);
|
||||||
|
|
||||||
|
let onclick_networked_multiplayer = ctx
|
||||||
|
.link()
|
||||||
|
.callback(|_| MainMenuMessage::NetworkedMultiplayer);
|
||||||
|
|
||||||
html! {
|
html! {
|
||||||
<div class={"menu"} id={"mainmenu"}>
|
<div class={"menu"} id={"mainmenu"}>
|
||||||
<b class={"menuText"}>{"Please pick a game mode."}</b>
|
<b class={"menuText"}>{"Please pick a game mode."}</b>
|
||||||
|
@ -77,7 +88,7 @@ impl Component for MainMenu {
|
||||||
<button class={"menuLocalMultiplayer"} onclick={onclick_local_multiplayer}>
|
<button class={"menuLocalMultiplayer"} onclick={onclick_local_multiplayer}>
|
||||||
{"Local Multiplayer"}
|
{"Local Multiplayer"}
|
||||||
</button>
|
</button>
|
||||||
<button class={"menuMultiplayer"}>
|
<button class={"menuMultiplayer"} onclick={onclick_networked_multiplayer}>
|
||||||
{"Networked Multiplayer"}
|
{"Networked Multiplayer"}
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
@ -110,7 +121,8 @@ impl Component for MainMenu {
|
||||||
.get_element_by_id("info_text1")
|
.get_element_by_id("info_text1")
|
||||||
.expect("info_text1 should exist");
|
.expect("info_text1 should exist");
|
||||||
|
|
||||||
if let GameState::SinglePlayer(turn, _) = shared.game_state.get() {
|
match shared.game_state.get() {
|
||||||
|
GameState::SinglePlayer(turn, _) => {
|
||||||
if shared.turn.get() == turn {
|
if shared.turn.get() == turn {
|
||||||
info_text_turn.set_inner_html(
|
info_text_turn.set_inner_html(
|
||||||
"<p><b class=\"cyan\">It is CyanPlayer's (player) Turn</b></p>",
|
"<p><b class=\"cyan\">It is CyanPlayer's (player) Turn</b></p>",
|
||||||
|
@ -119,15 +131,6 @@ impl Component for MainMenu {
|
||||||
info_text_turn.set_inner_html(
|
info_text_turn.set_inner_html(
|
||||||
"<p><b class=\"cyan\">It is CyanPlayer's (ai) Turn</b></p>",
|
"<p><b class=\"cyan\">It is CyanPlayer's (ai) Turn</b></p>",
|
||||||
);
|
);
|
||||||
}
|
|
||||||
} else {
|
|
||||||
info_text_turn
|
|
||||||
.set_inner_html("<p><b class=\"cyan\">It is CyanPlayer's Turn</b></p>");
|
|
||||||
}
|
|
||||||
|
|
||||||
if let GameState::SinglePlayer(Turn::MagentaPlayer, _ai_difficulty) =
|
|
||||||
shared.game_state.get()
|
|
||||||
{
|
|
||||||
// AI player starts first
|
// AI player starts first
|
||||||
ctx.link()
|
ctx.link()
|
||||||
.get_parent()
|
.get_parent()
|
||||||
|
@ -137,6 +140,25 @@ impl Component for MainMenu {
|
||||||
.send_message(WrapperMsg::AIChoice);
|
.send_message(WrapperMsg::AIChoice);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
GameState::NetworkedMultiplayer {
|
||||||
|
paired: _,
|
||||||
|
current_side: _,
|
||||||
|
current_turn: _,
|
||||||
|
} => {
|
||||||
|
// start the Wrapper Tick loop
|
||||||
|
ctx.link()
|
||||||
|
.get_parent()
|
||||||
|
.expect("Wrapper should be a parent of MainMenu")
|
||||||
|
.clone()
|
||||||
|
.downcast::<Wrapper>()
|
||||||
|
.send_message(WrapperMsg::BackendTick);
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
|
info_text_turn
|
||||||
|
.set_inner_html("<p><b class=\"cyan\">It is CyanPlayer's Turn</b></p>");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
|
@ -190,9 +212,21 @@ impl Component for Slot {
|
||||||
|
|
||||||
match shared.game_state.get() {
|
match shared.game_state.get() {
|
||||||
GameState::MainMenu => return false,
|
GameState::MainMenu => return false,
|
||||||
GameState::SinglePlayer(_, _)
|
GameState::SinglePlayer(_, _) | GameState::LocalMultiplayer => (),
|
||||||
| GameState::LocalMultiplayer
|
GameState::NetworkedMultiplayer {
|
||||||
| GameState::NetworkedMultiplayer(_) => (),
|
paired,
|
||||||
|
current_side,
|
||||||
|
current_turn,
|
||||||
|
} => {
|
||||||
|
// notify Wrapper with picked slot
|
||||||
|
if let Some(p) = ctx.link().get_parent() {
|
||||||
|
p.clone()
|
||||||
|
.downcast::<Wrapper>()
|
||||||
|
.send_message(WrapperMsg::BackendRequest {
|
||||||
|
place: ctx.props().idx,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
GameState::PostGameResults(_) => return false,
|
GameState::PostGameResults(_) => return false,
|
||||||
}
|
}
|
||||||
if shared.game_state.get() == GameState::MainMenu {
|
if shared.game_state.get() == GameState::MainMenu {
|
||||||
|
@ -213,14 +247,166 @@ impl Component for Slot {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct Wrapper {}
|
pub struct Wrapper {
|
||||||
|
player_id: Option<u32>,
|
||||||
|
place_request: Option<u8>,
|
||||||
|
do_backend_tick: bool,
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
|
impl Wrapper {
|
||||||
|
fn defer_message(
|
||||||
|
&self,
|
||||||
|
ctx: &Context<Self>,
|
||||||
|
msg: <Wrapper as Component>::Message,
|
||||||
|
millis: i32,
|
||||||
|
) {
|
||||||
|
ctx.link().send_future(async move {
|
||||||
|
let promise = Promise::new(&mut |resolve: js_sys::Function, _reject| {
|
||||||
|
let window = web_sys::window();
|
||||||
|
if window.is_none() {
|
||||||
|
resolve.call0(&resolve).ok();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
let window = window.unwrap();
|
||||||
|
if window
|
||||||
|
.set_timeout_with_callback_and_timeout_and_arguments_0(&resolve, millis)
|
||||||
|
.is_err()
|
||||||
|
{
|
||||||
|
resolve.call0(&resolve).ok();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
let js_fut = JsFuture::from(promise);
|
||||||
|
js_fut.await.ok();
|
||||||
|
msg
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_networked_player_id(&mut self, ctx: &Context<Self>) {
|
||||||
|
// make a request to get the player_id
|
||||||
|
ctx.link().send_future(async {
|
||||||
|
// get window
|
||||||
|
let window = web_sys::window().expect("Should be able to get Window");
|
||||||
|
// get request
|
||||||
|
let request = create_json_request(BACKEND_URL, "'type': 'pairing_request'")
|
||||||
|
.expect("Should be able to create the JSON request for player_id");
|
||||||
|
// send request
|
||||||
|
let promise = window.fetch_with_request(&request);
|
||||||
|
// get request result
|
||||||
|
let response_result = JsFuture::from(promise)
|
||||||
|
.await
|
||||||
|
.map_err(|e| format!("{:?}", e));
|
||||||
|
if let Err(e) = response_result {
|
||||||
|
return WrapperMsg::BackendResponse(BREnum::Error(format!("ERROR: {:?}", e)));
|
||||||
|
}
|
||||||
|
// get response from request result
|
||||||
|
let response = response_result.unwrap();
|
||||||
|
let json_value_result: Result<SerdeJSONValue, _> = response.into_serde();
|
||||||
|
if let Err(e) = json_value_result {
|
||||||
|
return WrapperMsg::BackendResponse(BREnum::Error(format!("ERROR: {:?}", e)));
|
||||||
|
}
|
||||||
|
// get serde json Value from result
|
||||||
|
let json_value = json_value_result.unwrap();
|
||||||
|
|
||||||
|
// get and check "type" in JSON
|
||||||
|
let type_opt = json_value.get("type");
|
||||||
|
if type_opt.is_none() {
|
||||||
|
return WrapperMsg::BackendResponse(BREnum::Error(
|
||||||
|
"ERROR: No \"type\" entry in JSON".into(),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
let json_type = type_opt.unwrap();
|
||||||
|
if let Some(type_string) = json_type.as_str() {
|
||||||
|
if type_string != "pairing_response" {
|
||||||
|
return WrapperMsg::BackendResponse(BREnum::Error(
|
||||||
|
"ERROR: Invalid \"type\" from response JSON".into(),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return WrapperMsg::BackendResponse(BREnum::Error(
|
||||||
|
"ERROR: Missing \"type\" from response JSON".into(),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
// get and check "id" in JSON
|
||||||
|
let player_id: u32;
|
||||||
|
if let Some(wrapped_player_id) = json_value.get("id") {
|
||||||
|
if let Some(player_id_u64) = wrapped_player_id.as_u64() {
|
||||||
|
let player_id_conv_result: Result<u32, _> = player_id_u64.try_into();
|
||||||
|
if player_id_conv_result.is_err() {
|
||||||
|
return WrapperMsg::BackendResponse(BREnum::Error(
|
||||||
|
"ERROR: \"id\" is too large".into(),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
player_id = player_id_conv_result.unwrap();
|
||||||
|
} else {
|
||||||
|
return WrapperMsg::BackendResponse(BREnum::Error(
|
||||||
|
"ERROR: \"id\" is not a u64".into(),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return WrapperMsg::BackendResponse(BREnum::Error(
|
||||||
|
"ERROR: Missing \"id\" from response JSON".into(),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
// get and check status
|
||||||
|
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
|
||||||
|
enum Status {
|
||||||
|
Waiting,
|
||||||
|
Paired,
|
||||||
|
}
|
||||||
|
let mut status: Status;
|
||||||
|
if let Some(status_value) = json_value.get("status") {
|
||||||
|
if let Some(status_str) = status_value.as_str() {
|
||||||
|
if status_str == "waiting" {
|
||||||
|
status = Status::Waiting;
|
||||||
|
} else if status_str == "paired" {
|
||||||
|
status = Status::Paired;
|
||||||
|
} else {
|
||||||
|
return WrapperMsg::BackendResponse(BREnum::Error(
|
||||||
|
"ERROR: Got invalid \"status\" response in JSON".into(),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return WrapperMsg::BackendResponse(BREnum::Error(
|
||||||
|
"ERROR: \"status\" response in JSON is not a str".into(),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return WrapperMsg::BackendResponse(BREnum::Error(
|
||||||
|
"ERROR: \"status\" response is missing in JSON".into(),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO set "disconnect" callback here so that the client sends
|
||||||
|
// disconnect message when the page is closed
|
||||||
|
|
||||||
|
if status == Status::Paired {
|
||||||
|
// Get which side current player is on if paired
|
||||||
|
// TODO
|
||||||
|
return WrapperMsg::BackendResponse(BREnum::Error("ERROR: unimplemented".into()));
|
||||||
|
} else {
|
||||||
|
WrapperMsg::BackendResponse(BREnum::GotID(player_id, None))
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||||
|
pub enum BREnum {
|
||||||
|
Error(String),
|
||||||
|
GotID(u32, Option<Turn>),
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||||
pub enum WrapperMsg {
|
pub enum WrapperMsg {
|
||||||
Pressed(u8),
|
Pressed(u8),
|
||||||
AIPressed(u8),
|
AIPressed(u8),
|
||||||
AIChoice,
|
AIChoice,
|
||||||
AIChoiceImpl,
|
AIChoiceImpl,
|
||||||
|
BackendTick,
|
||||||
|
BackendRequest { place: u8 },
|
||||||
|
BackendResponse(BREnum),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl WrapperMsg {
|
impl WrapperMsg {
|
||||||
|
@ -234,7 +420,11 @@ impl Component for Wrapper {
|
||||||
type Properties = ();
|
type Properties = ();
|
||||||
|
|
||||||
fn create(_ctx: &Context<Self>) -> Self {
|
fn create(_ctx: &Context<Self>) -> Self {
|
||||||
Self {}
|
Self {
|
||||||
|
player_id: None,
|
||||||
|
place_request: None,
|
||||||
|
do_backend_tick: true,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn view(&self, ctx: &Context<Self>) -> Html {
|
fn view(&self, ctx: &Context<Self>) -> Html {
|
||||||
|
@ -335,10 +525,12 @@ impl Component for Wrapper {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
GameState::LocalMultiplayer => (),
|
GameState::LocalMultiplayer => (),
|
||||||
GameState::NetworkedMultiplayer(turn) => {
|
GameState::NetworkedMultiplayer {
|
||||||
if current_player != turn {
|
paired,
|
||||||
return false;
|
current_side,
|
||||||
}
|
current_turn,
|
||||||
|
} => {
|
||||||
|
// TODO
|
||||||
}
|
}
|
||||||
GameState::PostGameResults(_) => (),
|
GameState::PostGameResults(_) => (),
|
||||||
}
|
}
|
||||||
|
@ -786,25 +978,7 @@ impl Component for Wrapper {
|
||||||
} // WrapperMsg::Pressed(idx) =>
|
} // WrapperMsg::Pressed(idx) =>
|
||||||
WrapperMsg::AIChoice => {
|
WrapperMsg::AIChoice => {
|
||||||
// defer by 1 second
|
// defer by 1 second
|
||||||
ctx.link().send_future(async {
|
self.defer_message(ctx, WrapperMsg::AIChoiceImpl, AI_CHOICE_DURATION_MILLIS);
|
||||||
let promise = Promise::new(&mut |resolve: js_sys::Function, _reject| {
|
|
||||||
let window = web_sys::window();
|
|
||||||
if window.is_none() {
|
|
||||||
resolve.call0(&resolve).ok();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
let window = window.unwrap();
|
|
||||||
if window
|
|
||||||
.set_timeout_with_callback_and_timeout_and_arguments_0(&resolve, 1000)
|
|
||||||
.is_err()
|
|
||||||
{
|
|
||||||
resolve.call0(&resolve).ok();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
let js_fut = JsFuture::from(promise);
|
|
||||||
js_fut.await.ok();
|
|
||||||
WrapperMsg::AIChoiceImpl
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
WrapperMsg::AIChoiceImpl => {
|
WrapperMsg::AIChoiceImpl => {
|
||||||
// get AI's choice
|
// get AI's choice
|
||||||
|
@ -819,6 +993,20 @@ impl Component for Wrapper {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
WrapperMsg::BackendTick => {
|
||||||
|
if self.player_id.is_none() {
|
||||||
|
self.get_networked_player_id(ctx);
|
||||||
|
}
|
||||||
|
|
||||||
|
// repeat BackendTick handling while "connected" to backend
|
||||||
|
if self.do_backend_tick {
|
||||||
|
self.defer_message(ctx, WrapperMsg::BackendTick, BACKEND_TICK_DURATION_MILLIS);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
WrapperMsg::BackendRequest { place } => {
|
||||||
|
self.place_request = Some(place);
|
||||||
|
}
|
||||||
|
WrapperMsg::BackendResponse(string) => {}
|
||||||
} // match (msg)
|
} // match (msg)
|
||||||
|
|
||||||
true
|
true
|
||||||
|
|
Loading…
Reference in a new issue