Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

uhm cat #9

Merged
merged 2 commits into from
Dec 8, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 10 additions & 13 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

17 changes: 13 additions & 4 deletions dt_client/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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"] }
188 changes: 106 additions & 82 deletions dt_client/src/lib.rs
Original file line number Diff line number Diff line change
@@ -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<Army>, BattleInfo));
pub struct OutcomingEvent((usize, usize));

pub struct Connection {
pub incoming_events: futures_channel::mpsc::Receiver<IncomingEvent>,
pub events_sender: futures_channel::mpsc::Sender<OutcomingEvent>,
// tasks_handle: JoinAll<JoinHandle<()>>,
}

#[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::<dyn FnMut(_)>::new(move |e: MessageEvent| {
// Handle difference Text/Binary,...
if let Ok(abuf) = e.data().dyn_into::<js_sys::ArrayBuffer>() {
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::<web_sys::Blob>() {
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::<dyn FnMut(_)>::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::<js_sys::JsString>() {
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::<dyn FnMut(_)>::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::<dyn FnMut()>::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::<IncomingEvent>(256);
let (oe_tx, oe_rx) = futures_channel::mpsc::channel::<OutcomingEvent>(256);

let i = read.filter_map(|msg| async {
let Message::Binary(e) = msg.ok()? else {return None};
let data =
alkahest::deserialize::<(Vec<Army>, BattleInfo), (Vec<Army>, 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();
}
}