From 77178c459e97ff43df1ce400bb5b5aec7084d25c Mon Sep 17 00:00:00 2001 From: Andrew Date: Sun, 8 Dec 2024 14:48:13 +0100 Subject: [PATCH] dt_server impl (#9) Co-authored-by: Uriadov Aleksey <63245550+LedinecMing@users.noreply.github.com> --- Cargo.lock | 23 +++--- dt_client/Cargo.toml | 17 +++- dt_client/src/lib.rs | 188 ++++++++++++++++++++++++------------------- 3 files changed, 129 insertions(+), 99 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index b2bd3e4..cf3dc60 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -7,7 +7,7 @@ name = "DT_Remastered" version = "0.1.0" dependencies = [ "advini", - "alkahest 0.3.0 (git+https://github.com/zakarumych/alkahest.git?rev=99a5b68)", + "alkahest", "anyhow", "bytemuck", "derive_builder", @@ -280,12 +280,6 @@ dependencies = [ "memchr", ] -[[package]] -name = "alkahest" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d3afb453d344e42ba592019bb80778e8c0a4976c4f4986e0dba6a0ae7d5a9616" - [[package]] name = "alkahest" version = "0.3.0" @@ -1822,10 +1816,13 @@ checksum = "f25c0e292a7ca6d6498557ff1df68f32c99850012b6ea401cf8daf771f22ff53" name = "dt_client" version = "0.1.0" dependencies = [ - "alkahest 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", - "js-sys", - "wasm-bindgen", - "web-sys", + "alkahest", + "dt_lib", + "futures-channel", + "futures-util", + "tokio", + "tokio-tungstenite", + "tungstenite", ] [[package]] @@ -1859,7 +1856,7 @@ name = "dt_lib" version = "0.1.0" dependencies = [ "advini", - "alkahest 0.3.0 (git+https://github.com/zakarumych/alkahest.git?rev=99a5b68)", + "alkahest", "bytes", "bzip2", "derive_more", @@ -1886,7 +1883,7 @@ dependencies = [ name = "dt_server" version = "0.1.0" dependencies = [ - "alkahest 0.3.0 (git+https://github.com/zakarumych/alkahest.git?rev=99a5b68)", + "alkahest", "axum", "bincode", "dt_lib", diff --git a/dt_client/Cargo.toml b/dt_client/Cargo.toml index 30d6b7b..e2967dc 100644 --- a/dt_client/Cargo.toml +++ b/dt_client/Cargo.toml @@ -2,10 +2,19 @@ name = "dt_client" version = "0.1.0" edition = "2021" + [lib] crate-type = ["cdylib"] + [dependencies] -alkahest = "0.3.0" -js-sys = "0.3.72" -wasm-bindgen = "0.2.93" -web-sys = { version = "0.3.72", features = ["BinaryType","Blob","ErrorEvent","FileReader","MessageEvent","ProgressEvent","WebSocket"] } +# alkahest = "0.3.0" +# js-sys = "0.3.72" +# wasm-bindgen = "0.2.93" +# web-sys = { version = "0.3.72", features = ["BinaryType","Blob","ErrorEvent","FileReader","MessageEvent","ProgressEvent","WebSocket"] } +dt_lib = { path = "../dt_lib" } +tokio = { version = "*", features = ["sync", "rt", "process", "macros", "fs", "time"] } +tokio-tungstenite = "0.24.0" +tungstenite = "0.24.0" +futures-util = "*" +alkahest = { git = "https://github.com/zakarumych/alkahest.git", rev = "99a5b68", features=["derive"] } +futures-channel = { version = "0.3.31", features = ["futures-sink", "sink"] } diff --git a/dt_client/src/lib.rs b/dt_client/src/lib.rs index fd0d28f..67c147e 100644 --- a/dt_client/src/lib.rs +++ b/dt_client/src/lib.rs @@ -1,92 +1,116 @@ -use wasm_bindgen::prelude::*; -use web_sys::{ErrorEvent, MessageEvent, WebSocket}; +use std::sync::Arc; +use dt_lib::{ + battle::{army::*, battlefield::*, troop::Troop}, + items::item::*, + locale::{parse_locale, Locale}, + map::{ + event::{execute_event, Event, Execute}, + map::*, + object::ObjectInfo, + tile::*, + }, + network::net::*, + parse::{parse_items, parse_objects, parse_settings, parse_story, parse_units}, + time::time::Data as TimeData, + units::{ + unit::{ActionResult, Unit, UnitPos}, + unitstats::ModifyUnitStats, + }, +}; +use futures_util::StreamExt; +use tokio::sync::oneshot; +use tokio_tungstenite::{connect_async, tungstenite::protocol::Message}; +use tungstenite::client::IntoClientRequest; -// Import the `window.alert` function from the Web. -#[wasm_bindgen] -extern "C" { - fn alert(s: &str); -} -macro_rules! console_log { - ($($t:tt)*) => (log(&format_args!($($t)*).to_string())) -} -#[wasm_bindgen] -extern "C" { - #[wasm_bindgen(js_namespace = console)] - fn log(s: &str); -} -// Export a `greet` function from Rust to JavaScript, that alerts a -// hello message. -#[wasm_bindgen] -pub fn greet(name: &str) { - alert(&format!("Hello, {}!", name)); +pub struct IncomingEvent((Vec, BattleInfo)); +pub struct OutcomingEvent((usize, usize)); + +pub struct Connection { + pub incoming_events: futures_channel::mpsc::Receiver, + pub events_sender: futures_channel::mpsc::Sender, + // tasks_handle: JoinAll>, } -#[wasm_bindgen] -pub fn connect(room: &str) -> Result<(), JsValue> { +pub async fn connect(room: &str, id: &str) -> Connection { + let mut request = "ws://localhost:3000/ws".into_client_request().unwrap(); + { + let headers = request.headers_mut(); + headers.insert("room-code", room.parse().unwrap()); + headers.insert("player-id", id.parse().unwrap()); + + // welp you're gonna need to serialize it to a safe string + // let initial_data = Vec::new(); + // alkahest::serialize_to_vec((0, 0), &mut initial_data); + // headers.insert("init-data", initial_data); + } + // Connect to an echo server - let ws = WebSocket::new("ws://localhost:3000/ws")?; - // For small binary messages, like CBOR, Arraybuffer is more efficient than Blob handling - ws.set_binary_type(web_sys::BinaryType::Arraybuffer); - // create callback - let mut state = (); - let cloned_ws = ws.clone(); - let onmessage_callback = Closure::::new(move |e: MessageEvent| { - // Handle difference Text/Binary,... - if let Ok(abuf) = e.data().dyn_into::() { - console_log!("message event, received arraybuffer: {:?}", abuf); - let array = js_sys::Uint8Array::new(&abuf); - let buf = array.to_vec(); - - // here you can for example use Serde Deserialize decode the message - // for demo purposes - } else if let Ok(blob) = e.data().dyn_into::() { - console_log!("message event, received blob: {:?}", blob); - // better alternative to juggling with FileReader is to use https://crates.io/crates/gloo-file - let fr = web_sys::FileReader::new().unwrap(); - let fr_c = fr.clone(); - // create onLoadEnd callback - let onloadend_cb = Closure::::new(move |_e: web_sys::ProgressEvent| { - let array = js_sys::Uint8Array::new(&fr_c.result().unwrap()); - let len = array.byte_length() as usize; - console_log!("Blob received {}bytes: {:?}", len, array.to_vec()); - // here you can for example use the received image/png data - }); - fr.set_onloadend(Some(onloadend_cb.as_ref().unchecked_ref())); - fr.read_as_array_buffer(&blob).expect("blob not readable"); - onloadend_cb.forget(); - } else if let Ok(txt) = e.data().dyn_into::() { - console_log!("message event, received Text: {:?}", txt); - } else { - console_log!("message event, received Unknown: {:?}", e.data()); - } - }); - // set message event handler on WebSocket - ws.set_onmessage(Some(onmessage_callback.as_ref().unchecked_ref())); - // forget the callback to keep it alive - onmessage_callback.forget(); + let (ws_stream, _) = connect_async(request) + .await + .expect("failed to connect to the server"); - let onerror_callback = Closure::::new(move |e: ErrorEvent| { - console_log!("error event: {:?}", e); - }); - ws.set_onerror(Some(onerror_callback.as_ref().unchecked_ref())); - onerror_callback.forget(); - - let cloned_ws = ws.clone(); - let onopen_callback = Closure::::new(move || { - console_log!("socket opened"); - match cloned_ws.send_with_str("ping") { - Ok(_) => console_log!("message successfully sent"), - Err(err) => console_log!("error sending message: {:?}", err), - } - // send off binary message - match cloned_ws.send_with_u8_array(&[0, 1, 2, 3]) { - Ok(_) => console_log!("binary message successfully sent"), - Err(err) => console_log!("error sending message: {:?}", err), + let (write, read) = ws_stream.split(); + + // ie = incoming events, oe = outcoming events + let (ie_tx, ie_rx) = futures_channel::mpsc::channel::(256); + let (oe_tx, oe_rx) = futures_channel::mpsc::channel::(256); + + let i = read.filter_map(|msg| async { + let Message::Binary(e) = msg.ok()? else {return None}; + let data = + alkahest::deserialize::<(Vec, BattleInfo), (Vec, BattleInfo)>(&e).ok()?; + Some(Ok(IncomingEvent(data))) + }).forward(ie_tx); + + let o = oe_rx.map(|OutcomingEvent(msg)| { + let mut result = Vec::new(); + alkahest::serialize_to_vec::<(usize, usize), (usize, usize)>(msg, &mut result); + Ok(Message::binary(result)) + }).forward(write); + + tokio::spawn(async move { + tokio::select! { + _ = i => (), + _ = o => (), } }); - ws.set_onopen(Some(onopen_callback.as_ref().unchecked_ref())); - onopen_callback.forget(); - Ok(()) + Connection { + incoming_events: ie_rx, + events_sender: oe_tx, + } +} +#[cfg(test)] +mod tests { + use futures_util::StreamExt; + use tokio::sync::oneshot; + use crate::connect; + + #[tokio::test] + async fn test() { + let mut server_proc = tokio::process::Command::new(tokio::fs::canonicalize("../dt/dt_server").await.unwrap()) + .current_dir("../dt/") + .spawn() + .unwrap(); + + tokio::time::sleep(std::time::Duration::from_secs(1)).await; + + let mut conn = connect("000000", "1").await; + + conn.events_sender.try_send(crate::OutcomingEvent((0,0))).unwrap(); + tokio::spawn(conn.incoming_events.for_each(|msg| async { + // dbg!(msg); + // + // HEEYAWYEYYAYSDYAYS READ THIS + // SO BASICALLY + // YOU SHOULD CHANGE THE TYPE OF INCOMINGEVENT TO YOUR NEED + // THE SERVER CAN RETURN AN ERROR OR SOME SHIT + // HANDLE THAT + () + })); + + // stop_signal.0.send(()).unwrap(); + server_proc.kill().await.unwrap(); + } }