use std::error::Error; use futures::stream::{SplitSink, SplitStream}; use futures::StreamExt; use log::{debug, error, info, Level, trace, warn}; use wasm_bindgen::prelude::*; use ws_stream_wasm::{WsMessage, WsMeta, WsStream}; use protocol::State; use protocol::PROTOCOL_VERSION; use protocol::MessageS2C; use protocol::MessageC2S; use futures::SinkExt; use lazy_static::lazy_static; use std::sync::Arc; use std::sync::RwLock; use futures::FutureExt; use web_sys::Window; #[macro_use] pub mod macros; pub mod chat; #[wasm_bindgen] extern { pub fn alert(s: &str); } #[wasm_bindgen] pub async fn rust_init(gateway: &str, username: &str) -> Result<(), JsError> { console_log::init_with_level(Level::Debug).unwrap(); info!("Logger setup successfully"); match main(gateway, username).await { Ok(c) => c, Err(e) => { error!("Error initializing gateway client: {}", e); return Err(JsError::new(&e.to_string())); } }; info!("Gateway client set up successfully"); Ok(()) } pub struct Client { pub client_data: Option, } pub struct ClientData { pub state: State, pub tx: SplitSink, pub rx: SplitStream } lazy_static! { pub static ref CLIENT: Arc> = Arc::new(RwLock::new(Client { client_data: None })); } pub async fn main(gateway: &str, username: &str) -> Result<(), Box> { info!("FAST CONNECT: {}", gateway); let gateway_url = url::Url::parse(gateway)?; trace!("Gateway URL parsed"); let (_ws, ws_stream) = WsMeta::connect(gateway_url, None).await?; trace!("Connected to gateway socket"); let (tx, rx) = ws_stream.split(); let mut client_data = ClientData { state: State::Handshake, tx, rx }; trace!("Split stream, handshaking with server"); send!(client_data.tx, &MessageC2S::Hello { next_state: State::Play, version: PROTOCOL_VERSION, requested_username: username.to_string() }).await?; trace!("Sent handshake start packet"); if let Some(msg) = recv_now!(client_data.rx)? { let typed_msg: MessageS2C = msg; match typed_msg { MessageS2C::Hello { version, given_username, next_state } => { info!("FAST CONNECT - connected to server protocol {} given username {}, switching to state {:?}", version, given_username, next_state); client_data.state = next_state; }, MessageS2C::Goodbye { reason } => { error!("server disconnected before finishing handshake: {:?}", reason); return Err(format!("disconnected by server: {:?}", reason).into()); }, _ => { warn!("received unexpected packet from server: {:?}", typed_msg); } } } else { error!("Server closed the connection") } CLIENT.write()?.client_data = Some(client_data); Ok(()) } #[wasm_bindgen] pub async fn update_socket() -> Result<(), JsError> { let mut client = CLIENT.write()?; if client.client_data.is_none() { return Err(JsError::new("Client not yet initialized")); } let client_data = client.client_data.as_mut().unwrap(); let maybe_msg: Option = match recv!(client_data.rx) { Ok(r) => r, Err(e) => { return Err(JsError::new(e)) } }; if let Some(msg) = maybe_msg { match msg { MessageS2C::Goodbye { reason } => { info!("server sent disconnect: {:?}", reason); client.client_data = None; return Err(JsError::new("disconnected by server")); } MessageS2C::Chat { from, message } => { info!("[CHAT] {}: {}", from, message); let window: Window = web_sys::window().expect("no global `window` exists"); let document = window.document().expect("should have a document on window"); let chatbox = document.get_element_by_id("chats").expect("chatbox does not exist"); let new_elem = document.create_element("p").expect("could not create element"); new_elem.set_inner_html(&format!("{}: {}", from, message)); chatbox.append_child(&new_elem).unwrap(); }, _ => { warn!("server sent unexpected packet {:?}, ignoring", msg); } } } Ok(()) }