#![allow(clippy::panic)]
use crate::*;
use mem6_common::*;
use unwrap::unwrap;
use js_sys::Reflect;
use wasm_bindgen::{prelude::*, JsCast};
use wasm_bindgen_futures::spawn_local;
use web_sys::{ErrorEvent, WebSocket};
use gloo_timers::future::TimeoutFuture;
#[allow(clippy::needless_pass_by_value)]
pub fn setup_ws_connection(
location_href: String,
client_ws_id: usize,
players_ws_uid: String,
) -> WebSocket {
let mut loc_href = location_href
.replace("http://", "ws://")
.replace("https://", "wss://");
logmod::debug_write(&loc_href);
if let Some(pos) = loc_href.find('#') {
loc_href = unwrap!(loc_href.get(..pos)).to_string();
}
logmod::debug_write(&loc_href);
loc_href.push_str("mem6ws/");
loc_href.push_str(client_ws_id.to_string().as_str());
let ws = unwrap!(WebSocket::new(&loc_href), "WebSocket failed to connect.");
let ws_c = ws.clone();
let open_handler = Box::new(move || {
unwrap!(
ws_c.send_with_str(&unwrap!(serde_json::to_string(
&WsMessage::MsgRequestWsUid {
my_ws_uid: client_ws_id,
players_ws_uid: players_ws_uid.clone(),
}
)),),
"Failed to send 'test' to server"
);
let ws2 = ws_c.clone();
let timeout = gloo_timers::callback::Interval::new(10_000, move || {
let u32_size = u32_size();
let msg = WsMessage::MsgPing { msg_id: u32_size };
ws_send_msg(ws2.as_ref(), &msg);
});
timeout.forget();
});
let cb_oh: Closure<dyn Fn()> = Closure::wrap(open_handler);
ws.set_onopen(Some(cb_oh.as_ref().unchecked_ref()));
cb_oh.forget();
ws
}
#[allow(clippy::integer_arithmetic)]
pub fn u32_size() -> u32 {
let now = js_sys::Date::new_0();
now.get_minutes() * 100_000 + now.get_seconds() * 1_000 + now.get_milliseconds()
}
#[allow(clippy::unneeded_field_pattern)]
#[allow(clippy::too_many_lines)]
pub fn setup_ws_msg_recv(ws: &WebSocket, vdom: dodrio::VdomWeak) {
let msg_recv_handler = Box::new(move |msg: JsValue| {
let data: JsValue = unwrap!(
Reflect::get(&msg, &"data".into()),
"No 'data' field in WebSocket message!"
);
let data = unwrap!(data.as_string());
if !(data.to_string().contains("MsgPing") || data.to_string().contains("MsgPong")) {
}
let msg: WsMessage = serde_json::from_str(&data).unwrap_or_else(|_x| WsMessage::MsgDummy {
dummy: String::from("error"),
});
spawn_local({
let vdom = vdom.clone();
async move {
let _result = vdom
.with_component({
let vdom = vdom.clone();
move |root| {
let rrc = root.unwrap_mut::<RootRenderingComponent>();
match msg {
WsMessage::MsgDummy { dummy } => {
logmod::debug_write(dummy.as_str())
}
WsMessage::MsgPing { msg_id: _ } => {
unreachable!("mem6 wasm must not receive MsgPing");
}
WsMessage::MsgPong { msg_id: _ } => {
}
WsMessage::MsgRequestWsUid {
my_ws_uid,
players_ws_uid: _,
} => logmod::debug_write(
format!("MsgRequestWsUid {}", my_ws_uid).as_str(),
),
WsMessage::MsgResponseWsUid {
your_ws_uid,
server_version: _,
} => {
on_response_ws_uid(rrc, your_ws_uid);
}
WsMessage::MsgJoin {
my_ws_uid,
players_ws_uid: _,
my_nickname,
} => {
statusjoinedmod::on_msg_joined(rrc, my_ws_uid, my_nickname);
vdom.schedule_render();
}
WsMessage::MsgStartGame {
my_ws_uid: _,
players_ws_uid: _,
card_grid_data,
game_config,
players,
game_name,
} => {
let vdom = vdom.clone();
statusgamedatainitmod::on_msg_start_game(
rrc,
&card_grid_data,
&game_config,
&players,
&game_name,
);
fncallermod::open_new_local_page("#p11");
vdom.schedule_render();
}
WsMessage::MsgClick1stCard {
my_ws_uid,
players_ws_uid: _,
card_index_of_first_click,
msg_id,
} => {
status1stcardmod::on_msg_click_1st_card(
rrc,
&vdom,
my_ws_uid,
card_index_of_first_click,
msg_id,
);
vdom.schedule_render();
}
WsMessage::MsgClick2ndCard {
my_ws_uid,
players_ws_uid: _,
card_index_of_second_click,
is_point,
msg_id,
} => {
status2ndcardmod::on_msg_click_2nd_card(
rrc,
my_ws_uid,
card_index_of_second_click,
is_point,
msg_id,
);
vdom.schedule_render();
}
WsMessage::MsgDrinkEnd {
my_ws_uid,
players_ws_uid: _,
} => {
statusdrinkmod::on_msg_drink_end(rrc, my_ws_uid, &vdom);
vdom.schedule_render();
}
WsMessage::MsgTakeTurn {
my_ws_uid,
players_ws_uid: _,
msg_id,
} => {
statustaketurnmod::on_msg_take_turn(rrc, my_ws_uid, msg_id);
vdom.schedule_render();
}
WsMessage::MsgGameOver {
my_ws_uid: _,
players_ws_uid: _,
} => {
statusgameovermod::on_msg_game_over(rrc);
vdom.schedule_render();
}
WsMessage::MsgPlayAgain {
my_ws_uid: _,
players_ws_uid: _,
} => {
statusgameovermod::on_msg_play_again(rrc);
}
WsMessage::MsgAck {
my_ws_uid,
players_ws_uid: _,
msg_id,
msg_ack_kind,
} => {
match msg_ack_kind {
MsgAckKind::MsgTakeTurn => {
statustaketurnmod::on_msg_ack_take_turn(
rrc, my_ws_uid, msg_id,
);
}
MsgAckKind::MsgClick1stCard => {
status1stcardmod::on_msg_ack_click_1st_card(
rrc, my_ws_uid, msg_id,
);
}
MsgAckKind::MsgClick2ndCard => {
status2ndcardmod::on_msg_ack_player_click2nd_card(
rrc, my_ws_uid, msg_id, &vdom,
);
}
}
vdom.schedule_render();
}
WsMessage::MsgAskPlayer1ForResync {
my_ws_uid: _,
players_ws_uid: _,
} => {
websocketreconnectmod::send_msg_for_resync(
rrc,
rrc.game_data.my_ws_uid,
);
vdom.schedule_render();
}
WsMessage::MsgAllGameData {
my_ws_uid: _,
players_ws_uid: _,
players,
card_grid_data,
card_index_of_first_click,
card_index_of_second_click,
player_turn,
game_status,
} => {
websocketreconnectmod::on_msg_all_game_data(
rrc,
players,
card_grid_data,
card_index_of_first_click,
card_index_of_second_click,
player_turn,
game_status,
);
vdom.schedule_render();
}
}
}
})
.await;
}
});
});
let cb_mrh: Closure<dyn Fn(JsValue)> = Closure::wrap(msg_recv_handler);
ws.set_onmessage(Some(cb_mrh.as_ref().unchecked_ref()));
cb_mrh.forget();
}
#[allow(clippy::as_conversions)]
pub fn setup_ws_onerror(ws: &WebSocket, vdom: dodrio::VdomWeak) {
let onerror_callback = Closure::wrap(Box::new(move |e: ErrorEvent| {
let err_text = format!("error event {:?}", e);
{
spawn_local({
let vdom = vdom.clone();
async move {
let _result = vdom
.with_component({
let vdom = vdom.clone();
move |root| {
let rrc = root.unwrap_mut::<RootRenderingComponent>();
rrc.game_data.error_text = err_text;
vdom.schedule_render();
}
})
.await;
}
});
}
}) as Box<dyn FnMut(ErrorEvent)>);
ws.set_onerror(Some(onerror_callback.as_ref().unchecked_ref()));
onerror_callback.forget();
}
#[allow(clippy::as_conversions)]
pub fn setup_ws_onclose(ws: &WebSocket, vdom: dodrio::VdomWeak) {
let onclose_callback = Closure::wrap(Box::new(move |e: ErrorEvent| {
let err_text = format!("ws_onclose {:?}", e);
logmod::debug_write(&format!("onclose_callback {}", &err_text));
{
spawn_local({
let vdom = vdom.clone();
async move {
let _result = vdom
.with_component({
let vdom = vdom.clone();
move |root| {
let rrc = root.unwrap_mut::<RootRenderingComponent>();
rrc.game_data.is_reconnect = true;
vdom.schedule_render();
}
})
.await;
}
});
}
}) as Box<dyn FnMut(ErrorEvent)>);
ws.set_onclose(Some(onclose_callback.as_ref().unchecked_ref()));
onclose_callback.forget();
}
pub fn setup_all_ws_events(ws: &WebSocket, vdom: dodrio::VdomWeak) {
setup_ws_msg_recv(ws, vdom.clone());
setup_ws_onerror(ws, vdom.clone());
setup_ws_onclose(ws, vdom);
}
pub fn ws_send_msg(ws: &WebSocket, ws_message: &WsMessage) {
let x = ws.send_with_str(&unwrap!(serde_json::to_string(ws_message)));
if let Err(_err) = x {
let ws = ws.clone();
let ws_message = ws_message.clone();
spawn_local({
async move {
let mut retries: usize = 1;
while retries <= 10 {
logmod::debug_write(&format!("send retries: {}", retries));
TimeoutFuture::new(100).await;
let x = ws.send_with_str(&unwrap!(serde_json::to_string(&ws_message)));
if let Ok(_y) = x {
break;
}
#[allow(clippy::integer_arithmetic)]
{
retries += 1;
}
}
if retries == 0 {
panic!("error 10 times retry ws_send_msg");
}
}
});
}
}
pub fn on_response_ws_uid(rrc: &mut RootRenderingComponent, your_ws_uid: usize) {
if rrc.game_data.my_ws_uid != your_ws_uid {
rrc.game_data.error_text = "my_ws_uid is incorrect!".to_string();
}
}