M Cargo.lock => Cargo.lock +28 -0
@@ 3,6 3,17 @@
version = 3
[[package]]
+name = "async-recursion"
+version = "1.0.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0e97ce7de6cf12de5d7226c73f5ba9811622f4db3a5b91b55c53e987e5f91cba"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 2.0.13",
+]
+
+[[package]]
name = "async_io_stream"
version = "0.3.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ 73,11 84,13 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
name = "client"
version = "0.1.0"
dependencies = [
+ "async-recursion",
"console_log",
"futures",
"js-sys",
"lazy_static",
"log",
+ "markdown",
"protocol",
"rmp-serde",
"serde",
@@ 422,6 435,15 @@ dependencies = [
]
[[package]]
+name = "markdown"
+version = "1.0.0-alpha.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "98de49c677e95e00eaa74c42a0b07ea55e1e0b1ebca5b2cbc7657f288cd714eb"
+dependencies = [
+ "unicode-id",
+]
+
+[[package]]
name = "memchr"
version = "2.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ 896,6 918,12 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "92888ba5573ff080736b3648696b70cafad7d250551175acbaa4e0385b3e1460"
[[package]]
+name = "unicode-id"
+version = "0.3.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d70b6494226b36008c8366c288d77190b3fad2eb4c10533139c1c1f461127f1a"
+
+[[package]]
name = "unicode-ident"
version = "1.0.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
M client/Cargo.toml => client/Cargo.toml +2 -0
@@ 21,6 21,8 @@ rmp-serde = "1.1"
ws_stream_wasm = "0.7"
serde = { version = "1", features = ["derive"] }
lazy_static = "1.4"
+markdown = "1.0.0-alpha.7" # DO NOT DOWNGRADE
+async-recursion = "1"
[dependencies.web-sys]
M client/src/lib.rs => client/src/lib.rs +72 -6
@@ 3,7 3,7 @@ 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 ws_stream_wasm::{WsErr, WsMessage, WsMeta, WsStream};
use protocol::State;
use protocol::PROTOCOL_VERSION;
use protocol::MessageS2C;
@@ 12,7 12,12 @@ use futures::SinkExt;
use lazy_static::lazy_static;
use std::sync::Arc;
use std::sync::RwLock;
+use std::thread;
+use std::time::Duration;
+use async_recursion::async_recursion;
use futures::FutureExt;
+use js_sys::Math::sqrt;
+use wasm_bindgen_futures::JsFuture;
use web_sys::Window;
#[macro_use]
@@ 26,11 31,13 @@ extern {
#[wasm_bindgen]
pub async fn rust_init(gateway: &str, username: &str) -> Result<(), JsError> {
+ update_status("Starting logger...");
+
console_log::init_with_level(Level::Debug).unwrap();
info!("Logger setup successfully");
- match main(gateway, username).await {
+ match main(gateway, username, 1).await {
Ok(c) => c,
Err(e) => {
error!("Error initializing gateway client: {}", e);
@@ 59,11 66,41 @@ lazy_static! {
}));
}
-pub async fn main(gateway: &str, username: &str) -> Result<(), Box<dyn Error>> {
+pub const MAX_CONNECTION_TRIES: i32 = 10;
+
+#[async_recursion(?Send)]
+#[allow(clippy::only_used_in_recursion)]
+pub async fn main(gateway: &str, username: &str, backoff: i32) -> Result<(), Box<dyn Error>> {
+ info!("Backing off connection: waiting {} seconds", backoff * backoff);
+
+ wait_for(sleep(backoff * backoff * 1000)).await;
+
+ if backoff > MAX_CONNECTION_TRIES {
+ update_status("Connection to server failed");
+ return Err("Hit backoff limit during reconnection attempt".into());
+ }
+ if backoff == 1 {
+ update_status("Connecting to server...");
+ } else {
+ update_status(&format!("Connection failed, retrying... (try {}/10)", backoff));
+ }
+
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?;
+ let (_ws, ws_stream) = match WsMeta::connect(gateway_url, None).await {
+ Ok(r) => r,
+ Err(e) => {
+ return match e {
+ WsErr::ConnectionFailed { .. } => {
+ main(gateway, username, backoff + 1).await
+ },
+ _ => {
+ Err(e.into())
+ }
+ }
+ }
+ };
trace!("Connected to gateway socket");
let (tx, rx) = ws_stream.split();
@@ 75,6 112,8 @@ pub async fn main(gateway: &str, username: &str) -> Result<(), Box<dyn Error>> {
trace!("Split stream, handshaking with server");
+ update_status("Handshaking with server...");
+
send!(client_data.tx, &MessageC2S::Hello {
next_state: State::Play,
version: PROTOCOL_VERSION,
@@ 105,6 144,8 @@ pub async fn main(gateway: &str, username: &str) -> Result<(), Box<dyn Error>> {
CLIENT.write()?.client_data = Some(client_data);
+ update_status(&format!("Connected! Username: {}", username));
+
Ok(())
}
@@ 139,8 180,11 @@ pub async fn update_socket() -> Result<(), JsError> {
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!("<b>{}</b>: {}", from, message));
+ let new_elem = document.create_element("div").expect("could not create element");
+
+ let msg_formatted = markdown::to_html(&format!("**[{}]** {}", from, message));
+
+ new_elem.set_inner_html(&msg_formatted);
chatbox.append_child(&new_elem).unwrap();
},
@@ 151,4 195,26 @@ pub async fn update_socket() -> Result<(), JsError> {
}
Ok(())
+}
+
+#[wasm_bindgen]
+pub fn update_status(new_status: &str) {
+ let window: Window = web_sys::window().expect("no global `window` exists");
+ let document = window.document().expect("should have a document on window");
+ let status = document.get_element_by_id("status").expect("statusbox does not exist");
+ status.set_inner_html(new_status);
+}
+
+#[wasm_bindgen]
+pub fn sleep(ms: i32) -> js_sys::Promise {
+ js_sys::Promise::new(&mut |resolve, _| {
+ web_sys::window()
+ .unwrap()
+ .set_timeout_with_callback_and_timeout_and_arguments_0(&resolve, ms)
+ .unwrap();
+ })
+}
+
+pub async fn wait_for(promise: js_sys::Promise) -> JsFuture {
+ wasm_bindgen_futures::JsFuture::from(promise)
}=
\ No newline at end of file
M justfile => justfile +1 -1
@@ 1,4 1,4 @@
-run_server: build_client_bundle
+run_server:
cargo run --bin server
run_http: build_client_bundle
M web/play.html => web/play.html +2 -1
@@ 15,12 15,13 @@
</div>
<input id="chat-value" type="text" placeholder="chat text goes here" />
<button id="chat-submit">submit</button>
+ <p id="status">Loading WASM module...</p>
</div>
<script type="module">
// If you're getting build errors here | you need to run `just build_client_bundle` first, to compile client code
// v
- import init, { rust_init, send_chat, update_socket } from "./dist/client.js";
+ import init, { rust_init, send_chat, update_socket, update_status } from "./dist/client.js";
init().then(() => {
const urlSearchParams = new URLSearchParams(window.location.search);