diff --git a/ewext/Cargo.lock b/ewext/Cargo.lock index 75b404b5..582af134 100644 --- a/ewext/Cargo.lock +++ b/ewext/Cargo.lock @@ -17,6 +17,12 @@ version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "512761e0bb2578dd7380c6baaa0f4ce03e84f95e960231d1dec8bf4d7d6e2627" +[[package]] +name = "arrayvec" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7c02d123df017efcdfbd739ef81735b36c5ba83ec3c59c80a9d7ecc718f92e50" + [[package]] name = "backtrace" version = "0.3.74" @@ -32,22 +38,111 @@ dependencies = [ "windows-targets", ] +[[package]] +name = "bitcode" +version = "0.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ee1bce7608560cd4bf0296a4262d0dbf13e6bcec5ff2105724c8ab88cc7fc784" +dependencies = [ + "arrayvec", + "bitcode_derive", + "bytemuck", + "glam", + "serde", +] + +[[package]] +name = "bitcode_derive" +version = "0.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a539389a13af092cd345a2b47ae7dec12deb306d660b2223d25cd3419b253ebe" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "block-buffer" +version = "0.10.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71" +dependencies = [ + "generic-array", +] + +[[package]] +name = "bytemuck" +version = "1.20.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b37c88a63ffd85d15b406896cc343916d7cf57838a847b3a6f2ca5d39a5695a" + +[[package]] +name = "byteorder" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" + +[[package]] +name = "bytes" +version = "1.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "325918d6fe32f23b19878fe4b34794ae41fc19ddbe53b10571a4874d44ffd39b" + [[package]] name = "cfg-if" version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" +[[package]] +name = "cpufeatures" +version = "0.2.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "16b80225097f2e5ae4e7179dd2266824648f3e2f49d9134d584b76389d31c4c3" +dependencies = [ + "libc", +] + +[[package]] +name = "crypto-common" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" +dependencies = [ + "generic-array", + "typenum", +] + +[[package]] +name = "data-encoding" +version = "2.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e8566979429cf69b49a5c740c60791108e86440e8be149bbea4fe54d2c32d6e2" + +[[package]] +name = "digest" +version = "0.10.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" +dependencies = [ + "block-buffer", + "crypto-common", +] + [[package]] name = "ewext" version = "0.4.0" dependencies = [ "backtrace", + "bitcode", "eyre", "iced-x86", "libloading", "noita_api", "noita_api_macro", + "shared", + "tungstenite", ] [[package]] @@ -60,18 +155,68 @@ dependencies = [ "once_cell", ] +[[package]] +name = "fnv" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" + +[[package]] +name = "generic-array" +version = "0.14.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" +dependencies = [ + "typenum", + "version_check", +] + +[[package]] +name = "getrandom" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c4567c8db10ae91089c99af84c68c38da3ec2f087c3f82960bcdbf3656b6f4d7" +dependencies = [ + "cfg-if", + "libc", + "wasi", +] + [[package]] name = "gimli" version = "0.31.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "07e28edb80900c19c28f1072f2e8aeca7fa06b23cd4169cefe1af5aa3260783f" +[[package]] +name = "glam" +version = "0.29.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc46dd3ec48fdd8e693a98d2b8bafae273a2d54c1de02a2a7e3d57d501f39677" + [[package]] name = "heck" version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" +[[package]] +name = "http" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "21b9ddb458710bc376481b842f5da65cdf31522de232c1ca8146abce2a358258" +dependencies = [ + "bytes", + "fnv", + "itoa", +] + +[[package]] +name = "httparse" +version = "1.9.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7d71d3574edd2771538b901e6549113b4006ece66150fb69c0fb6d9a2adae946" + [[package]] name = "iced-x86" version = "1.21.0" @@ -115,6 +260,12 @@ dependencies = [ "windows-targets", ] +[[package]] +name = "log" +version = "0.4.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a7a70ba024b9dc04c27ea2f0c0548feb474ec5c54bba33a7f72f873a39d07b24" + [[package]] name = "memchr" version = "2.7.4" @@ -165,6 +316,15 @@ version = "1.20.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1261fe7e33c73b354eab43b1273a57c8f967d0391e80353e51f764ac02cf6775" +[[package]] +name = "ppv-lite86" +version = "0.2.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77957b295656769bb8ad2b6a6b09d897d94f05c41b069aede1fcdaa675eaea04" +dependencies = [ + "zerocopy", +] + [[package]] name = "proc-macro2" version = "1.0.89" @@ -183,6 +343,36 @@ dependencies = [ "proc-macro2", ] +[[package]] +name = "rand" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" +dependencies = [ + "libc", + "rand_chacha", + "rand_core", +] + +[[package]] +name = "rand_chacha" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" +dependencies = [ + "ppv-lite86", + "rand_core", +] + +[[package]] +name = "rand_core" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" +dependencies = [ + "getrandom", +] + [[package]] name = "rustc-demangle" version = "0.1.24" @@ -227,6 +417,24 @@ dependencies = [ "serde", ] +[[package]] +name = "sha1" +version = "0.10.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3bf829a2d51ab4a5ddf1352d8470c140cadc8301b2ae1789db023f01cedd6ba" +dependencies = [ + "cfg-if", + "cpufeatures", + "digest", +] + +[[package]] +name = "shared" +version = "0.1.0" +dependencies = [ + "bitcode", +] + [[package]] name = "syn" version = "2.0.87" @@ -238,12 +446,74 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "thiserror" +version = "1.0.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6aaf5339b578ea85b50e080feb250a3e8ae8cfcdff9a461c9ec2904bc923f52" +dependencies = [ + "thiserror-impl", +] + +[[package]] +name = "thiserror-impl" +version = "1.0.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "tungstenite" +version = "0.24.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "18e5b8366ee7a95b16d32197d0b2604b43a0be89dc5fac9f8e96ccafbaedda8a" +dependencies = [ + "byteorder", + "bytes", + "data-encoding", + "http", + "httparse", + "log", + "rand", + "sha1", + "thiserror", + "utf-8", +] + +[[package]] +name = "typenum" +version = "1.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825" + [[package]] name = "unicode-ident" version = "1.0.13" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e91b56cd4cadaeb79bbf1a5645f6b4f8dc5bde8834ad5894a8db35fda9efa1fe" +[[package]] +name = "utf-8" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09cc8ee72d2a9becf2f2febe0205bbed8fc6615b7cb429ad062dc7b7ddd036a9" + +[[package]] +name = "version_check" +version = "0.9.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" + +[[package]] +name = "wasi" +version = "0.11.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" + [[package]] name = "windows-targets" version = "0.52.6" @@ -307,3 +577,24 @@ name = "windows_x86_64_msvc" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" + +[[package]] +name = "zerocopy" +version = "0.7.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b9b4fd18abc82b8136838da5d50bae7bdea537c574d8dc1a34ed098d6c166f0" +dependencies = [ + "byteorder", + "zerocopy-derive", +] + +[[package]] +name = "zerocopy-derive" +version = "0.7.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] diff --git a/ewext/Cargo.toml b/ewext/Cargo.toml index 4c0d1862..0b6eb052 100644 --- a/ewext/Cargo.toml +++ b/ewext/Cargo.toml @@ -10,6 +10,7 @@ crate-type = ["cdylib"] [profile.release] lto = true strip = true +panic = "abort" [dependencies] libloading = "0.8.5" @@ -17,4 +18,7 @@ backtrace = "0.3.74" iced-x86 = "1.21.0" noita_api_macro = {path = "noita_api_macro"} eyre = "0.6.12" -noita_api = {path = "noita_api"} \ No newline at end of file +noita_api = {path = "noita_api"} +tungstenite = "0.24.0" +bitcode = "0.6.3" +shared = {path = "../shared"} diff --git a/ewext/noita_api/src/lua.rs b/ewext/noita_api/src/lua.rs index 18969282..58ef6c50 100644 --- a/ewext/noita_api/src/lua.rs +++ b/ewext/noita_api/src/lua.rs @@ -71,6 +71,17 @@ impl LuaState { .wrap_err("Attempting to get lua string, expecting it to be utf-8")?) } + pub fn to_raw_string(&self, index: i32) -> eyre::Result> { + let mut size = 0; + let buf = unsafe { LUA.lua_tolstring(self.lua, index, &mut size) }; + if buf.is_null() { + bail!("Expected a string, but got a null pointer"); + } + let slice = unsafe { slice::from_raw_parts(buf as *const u8, size) }; + + Ok(slice.to_owned()) + } + pub fn to_cfunction(&self, index: i32) -> lua_CFunction { unsafe { LUA.lua_tocfunction(self.lua, index) } } @@ -93,6 +104,12 @@ impl LuaState { } } + pub fn push_raw_string(&self, s: &[u8]) { + unsafe { + LUA.lua_pushlstring(self.lua, s.as_ptr() as *const c_char, s.len()); + } + } + pub fn push_nil(&self) { unsafe { LUA.lua_pushnil(self.lua) } } @@ -139,6 +156,22 @@ impl LuaState { fn is_nil_or_none(&self, index: i32) -> bool { (unsafe { LUA.lua_type(self.lua, index) }) <= 0 } + + pub fn create_table(&self, narr: c_int, nrec: c_int) { + unsafe { LUA.lua_createtable(self.lua, narr, nrec) }; + } + + pub fn rawset_table(&self, table_index: i32, index_in_table: i32) { + unsafe { LUA.lua_rawseti(self.lua, table_index, index_in_table) }; + } +} + +pub struct RawString(Vec); + +impl From> for RawString { + fn from(value: Vec) -> Self { + Self(value) + } } /// Used for types that can be returned from functions that were defined in rust to lua. @@ -172,6 +205,37 @@ impl LuaFnRet for eyre::Result { } } +impl LuaFnRet for Option { + fn do_return(self, lua: LuaState) -> c_int { + match self { + Some(val) => val.do_return(lua), + None => { + lua.push_nil(); + 1 + } + } + } +} + +impl LuaFnRet for Vec { + fn do_return(self, lua: LuaState) -> c_int { + lua.create_table(self.len() as c_int, 0); + for (i, el) in self.into_iter().enumerate() { + let elements = el.do_return(lua); + assert_eq!(elements, 1, "Vec's T should only put one value on stack"); + lua.rawset_table(-2, (i + 1) as i32); + } + 1 + } +} + +impl LuaFnRet for RawString { + fn do_return(self, lua: LuaState) -> c_int { + lua.push_raw_string(&self.0); + 1 + } +} + /// Trait for arguments that can be put on lua stack. pub(crate) trait LuaPutValue { fn put(&self, lua: LuaState); diff --git a/ewext/src/lib.rs b/ewext/src/lib.rs index d7303333..c995a241 100644 --- a/ewext/src/lib.rs +++ b/ewext/src/lib.rs @@ -2,6 +2,7 @@ use std::{ arch::asm, cell::{LazyCell, RefCell}, ffi::{c_int, c_void}, + sync::{LazyLock, Mutex}, time::Instant, }; @@ -9,18 +10,19 @@ use addr_grabber::{grab_addrs, grabbed_fns, grabbed_globals}; use eyre::{bail, OptionExt}; use modules::{entity_sync::EntitySync, Module}; +use net::NetManager; use noita::{ntypes::Entity, pixel::NoitaPixelRun, ParticleWorldState}; use noita_api::{ - lua::{lua_bindings::lua_State, LuaState, ValuesOnStack, LUA}, + lua::{lua_bindings::lua_State, LuaFnRet, LuaState, RawString, ValuesOnStack, LUA}, DamageModelComponent, }; use noita_api_macro::add_lua_fn; - -pub mod noita; - -mod modules; +use shared::{NoitaInbound, ProxyKV}; mod addr_grabber; +mod modules; +mod net; +pub mod noita; thread_local! { static STATE: LazyCell> = LazyCell::new(|| { @@ -29,6 +31,8 @@ thread_local! { }); } +static NETMANAGER: LazyLock>> = LazyLock::new(|| Default::default()); + #[derive(Default)] struct ExtState { particle_world_state: Option, @@ -94,6 +98,77 @@ fn make_ephemerial(lua: LuaState) -> eyre::Result<()> { Ok(()) } +struct InitKV { + key: String, + value: String, +} + +impl From for InitKV { + fn from(value: ProxyKV) -> Self { + InitKV { + key: value.key, + value: value.value, + } + } +} + +fn netmanager_connect(_lua: LuaState) -> eyre::Result> { + println!("Connecting to proxy..."); + let mut netman = NetManager::new()?; + + let mut kvs = Vec::new(); + + loop { + match netman + .recv()? + .ok_or_eyre("Expected to be in non-blocking mode")? + { + // shared::NoitaInbound::ProxyKV(proxy_kv) => kvs.push(proxy_kv.into()), + NoitaInbound::RawMessage(msg) => kvs.push(msg.into()), + NoitaInbound::Ready => break, + // _ => bail!("Received an unexpected message type during init"), + } + } + + netman.switch_to_non_blocking()?; + + *NETMANAGER.lock().unwrap() = Some(netman); + println!("Ok!"); + Ok(kvs) +} + +fn netmanager_recv(_lua: LuaState) -> eyre::Result> { + let mut binding = NETMANAGER.lock().unwrap(); + let netmanager = binding.as_mut().unwrap(); + Ok(match netmanager.recv()? { + Some(NoitaInbound::RawMessage(msg)) => Some(msg.into()), + Some(NoitaInbound::Ready) => { + bail!("Unexpected Ready message") + } + None => None, + }) +} + +fn netmanager_send(lua: LuaState) -> eyre::Result<()> { + let arg = lua.to_raw_string(1)?; + let mut binding = NETMANAGER.lock().unwrap(); + let netmanager = binding.as_mut().unwrap(); + netmanager.send(&shared::NoitaOutbound::Raw(arg))?; + + Ok(()) +} + +impl LuaFnRet for InitKV { + fn do_return(self, lua: LuaState) -> c_int { + lua.create_table(2, 0); + lua.push_string(&self.key); + lua.rawset_table(-2, 1); + lua.push_string(&self.value); + lua.rawset_table(-2, 2); + 1 + } +} + fn on_world_initialized(lua: LuaState) { grab_addrs(lua); @@ -175,7 +250,10 @@ fn test_fn(_lua: LuaState) -> eyre::Result<()> { #[no_mangle] pub unsafe extern "C" fn luaopen_ewext0(lua: *mut lua_State) -> c_int { println!("Initializing ewext"); + + // Reset some stuff STATE.with(|state| state.take()); + NETMANAGER.lock().unwrap().take(); unsafe { LUA.lua_createtable(lua, 0, 0); @@ -187,6 +265,10 @@ pub unsafe extern "C" fn luaopen_ewext0(lua: *mut lua_State) -> c_int { add_lua_fn!(test_fn); add_lua_fn!(bench_fn); + add_lua_fn!(netmanager_connect); + add_lua_fn!(netmanager_recv); + add_lua_fn!(netmanager_send); + add_lua_fn!(module_on_world_update); } println!("Initializing ewext - Ok"); diff --git a/ewext/src/net.rs b/ewext/src/net.rs new file mode 100644 index 00000000..6ab0a270 --- /dev/null +++ b/ewext/src/net.rs @@ -0,0 +1,65 @@ +use std::{ + env, + io::ErrorKind, + net::{SocketAddr, TcpStream}, + time::Duration, +}; + +use shared::{NoitaInbound, NoitaOutbound}; +use tungstenite::{client, WebSocket}; + +pub(crate) struct NetManager { + ws: WebSocket, +} + +impl NetManager { + pub(crate) fn new() -> eyre::Result { + let address: SocketAddr = env::var("NP_NOITA_ADDR") + .ok() + .and_then(|x| x.parse().ok()) + .unwrap_or_else(|| SocketAddr::new("127.0.0.1".parse().unwrap(), 21251)); + + let request = format!("ws://{address}"); + + let tcp = TcpStream::connect_timeout(&address, Duration::from_secs(2))?; + tcp.set_nodelay(true)?; + let (ws, _) = client(request, tcp)?; + + Ok(NetManager { ws }) + } + + pub(crate) fn switch_to_non_blocking(&mut self) -> eyre::Result<()> { + self.ws.get_mut().set_nonblocking(true)?; + Ok(()) + } + + pub(crate) fn send(&mut self, msg: &NoitaOutbound) -> eyre::Result<()> { + self.ws + .send(tungstenite::Message::Binary(bitcode::encode(msg)))?; + Ok(()) + } + + pub(crate) fn recv(&mut self) -> eyre::Result> { + loop { + match self.ws.read() { + Ok(tungstenite::Message::Binary(msg)) => break Ok(Some(bitcode::decode(&msg)?)), + Ok(_) => {} + Err(tungstenite::Error::Io(err)) + if err.kind() == ErrorKind::WouldBlock || err.kind() == ErrorKind::TimedOut => + { + break Ok(None) + } + Err(err) => break Err(err.into()), + } + } + } +} + +impl Drop for NetManager { + fn drop(&mut self) { + println!("Closing netmanager"); + self.ws.get_mut().set_nonblocking(false).ok(); + self.ws.close(None).ok(); + self.ws.flush().ok(); + } +} diff --git a/noita-proxy/Cargo.lock b/noita-proxy/Cargo.lock index dca1d6c2..83a9f4d9 100644 --- a/noita-proxy/Cargo.lock +++ b/noita-proxy/Cargo.lock @@ -2072,6 +2072,7 @@ dependencies = [ "self-replace", "serde", "serde_json", + "shared", "shlex", "socket2", "steamworks", @@ -3208,6 +3209,13 @@ dependencies = [ "lazy_static", ] +[[package]] +name = "shared" +version = "0.1.0" +dependencies = [ + "bitcode", +] + [[package]] name = "shlex" version = "1.3.0" diff --git a/noita-proxy/Cargo.toml b/noita-proxy/Cargo.toml index e6fba0b3..518c2ae2 100644 --- a/noita-proxy/Cargo.toml +++ b/noita-proxy/Cargo.toml @@ -23,7 +23,7 @@ tracing-subscriber = { version = "0.3.18", features = ["env-filter"] } tracing = "0.1.40" tangled = { path = "tangled" } serde = { version = "1.0.207", features = ["serde_derive", "derive"] } -bitcode = "0.6.0" +bitcode = "0.6.3" lz4_flex = { version = "0.11.3", default-features = false, features = ["std"]} rand = "0.8.5" steamworks = { git = "https://github.com/IntQuant/steamworks-rs", branch = "avatar-fix" } @@ -50,6 +50,7 @@ dashmap = "6.0.1" eyre = "0.6.12" tokio = { version = "1.40.0", features = ["macros", "rt-multi-thread"] } tracing-appender = "0.2.3" +shared = {path = "../shared"} [build-dependencies] winresource = "0.1.17" diff --git a/noita-proxy/src/net.rs b/noita-proxy/src/net.rs index 0f8ff4b8..09e0f1fc 100644 --- a/noita-proxy/src/net.rs +++ b/noita-proxy/src/net.rs @@ -2,15 +2,17 @@ use bitcode::{Decode, Encode}; use messages::{MessageRequest, NetMsg}; use omni::OmniPeerId; use proxy_opt::ProxyOpt; +use shared::{NoitaInbound, NoitaOutbound}; use socket2::{Domain, Socket, Type}; use std::fs::{create_dir, remove_dir_all, File}; +use std::io::Write; use std::path::PathBuf; use std::sync::atomic::{AtomicBool, AtomicI32, AtomicU16, Ordering}; use std::sync::{Arc, Mutex}; use std::{ env, fmt::Display, - io::{self, Write}, + io::{self}, net::{SocketAddr, TcpListener, TcpStream}, thread::{self, JoinHandle}, time::{Duration, Instant}, @@ -33,11 +35,15 @@ mod proxy_opt; pub mod steam_networking; pub mod world; +fn ws_bitcode(proxy_kv: &NoitaInbound) -> tungstenite::Message { + tungstenite::Message::Binary(bitcode::encode(proxy_kv)) +} + pub(crate) fn ws_encode_proxy(key: &'static str, value: impl Display) -> tungstenite::Message { let mut buf = Vec::new(); buf.push(2); write!(buf, "{} {}", key, value).unwrap(); - tungstenite::Message::Binary(buf) + ws_bitcode(&NoitaInbound::RawMessage(buf)) } pub fn ws_encode_proxy_bin(key: u8, data: &[u8]) -> tungstenite::Message { @@ -45,7 +51,7 @@ pub fn ws_encode_proxy_bin(key: u8, data: &[u8]) -> tungstenite::Message { buf.push(3); buf.push(key); buf.extend(data); - tungstenite::Message::Binary(buf) + ws_bitcode(&NoitaInbound::RawMessage(buf)) } pub(crate) fn ws_encode_mod(peer: OmniPeerId, data: &[u8]) -> tungstenite::Message { @@ -53,9 +59,30 @@ pub(crate) fn ws_encode_mod(peer: OmniPeerId, data: &[u8]) -> tungstenite::Messa buf.push(1u8); buf.extend_from_slice(&peer.0.to_le_bytes()); buf.extend_from_slice(data); - tungstenite::Message::Binary(buf) + ws_bitcode(&NoitaInbound::RawMessage(buf)) } +// pub(crate) fn ws_encode_proxy(key: &'static str, value: impl Display) -> tungstenite::Message { +// ws_bitcode(&NoitaInbound::ProxyKV(ProxyKV { +// key: key.to_owned(), +// value: value.to_string(), +// })) +// } + +// pub fn ws_encode_proxy_bin(key: u8, data: &[u8]) -> tungstenite::Message { +// ws_bitcode(&NoitaInbound::ProxyKVBin(ProxyKVBin { +// key, +// value: data.to_owned(), +// })) +// } + +// pub(crate) fn ws_encode_mod(peer: OmniPeerId, data: &[u8]) -> tungstenite::Message { +// ws_bitcode(&NoitaInbound::ModMessage(ModMessage { +// peer: peer.into(), +// value: data.to_owned(), +// })) +// } + pub struct DebugMarker { pub x: f64, pub y: f64, @@ -94,7 +121,7 @@ impl NetInnerState { let mut buf = Vec::new(); buf.push(2); value.write_opt(&mut buf, key); - let message = tungstenite::Message::Binary(buf); + let message = ws_bitcode(&NoitaInbound::RawMessage(buf)); self.try_ws_write(message); } } @@ -440,7 +467,9 @@ impl NetManager { match msg { Ok(msg) => { if let tungstenite::Message::Binary(msg) = msg { - self.handle_mod_message_2(msg, &mut state); + if let Ok(msg) = bitcode::decode(&msg) { + self.handle_mod_message_2(msg, &mut state); + } } } Err(tungstenite::Error::Io(io_err)) @@ -610,7 +639,7 @@ impl NetManager { let progress = settings.progress.join(","); state.try_ws_write_option("progress", progress.as_str()); - state.try_ws_write(ws_encode_proxy("ready", "")); + state.try_ws_write(ws_bitcode(&NoitaInbound::Ready)); // TODO? those are currently ignored by mod for id in self.peer.iter_peer_ids() { state.try_ws_write(ws_encode_proxy("join", id.as_hex())); @@ -618,31 +647,35 @@ impl NetManager { info!("Settings sent") } - fn handle_mod_message_2(&self, msg: Vec, state: &mut NetInnerState) { - match msg[0] & 0b11 { - // Message to proxy - 1 => { - self.handle_message_to_proxy(&msg[1..], state); - } - // Broadcast - 2 => { - let msg_to_send = NetMsg::ModRaw { - data: msg[1..].to_owned(), - }; - let reliable = msg[0] & 4 > 0; - self.broadcast( - &msg_to_send, - if reliable { - Reliability::Reliable - } else { - Reliability::Unreliable - }, - ); - } - // Binary message to proxy - 3 => self.handle_bin_message_to_proxy(&msg[1..], state), - msg_variant => { - error!("Unknown msg variant from mod: {}", msg_variant) + fn handle_mod_message_2(&self, msg: NoitaOutbound, state: &mut NetInnerState) { + match msg { + NoitaOutbound::Raw(raw_msg) => { + match raw_msg[0] & 0b11 { + // Message to proxy + 1 => { + self.handle_message_to_proxy(&raw_msg[1..], state); + } + // Broadcast + 2 => { + let msg_to_send = NetMsg::ModRaw { + data: raw_msg[1..].to_owned(), + }; + let reliable = raw_msg[0] & 4 > 0; + self.broadcast( + &msg_to_send, + if reliable { + Reliability::Reliable + } else { + Reliability::Unreliable + }, + ); + } + // Binary message to proxy + 3 => self.handle_bin_message_to_proxy(&raw_msg[1..], state), + msg_variant => { + error!("Unknown msg variant from mod: {}", msg_variant) + } + } } } } diff --git a/noita-proxy/src/net/omni.rs b/noita-proxy/src/net/omni.rs index f7d18bae..5e0efbc3 100644 --- a/noita-proxy/src/net/omni.rs +++ b/noita-proxy/src/net/omni.rs @@ -7,6 +7,18 @@ use tangled::{PeerId, Reliability}; #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Decode, Encode)] pub struct OmniPeerId(pub u64); +impl From for OmniPeerId { + fn from(value: shared::PeerId) -> Self { + OmniPeerId(value.0) + } +} + +impl From for shared::PeerId { + fn from(value: OmniPeerId) -> Self { + shared::PeerId(value.0) + } +} + impl From for OmniPeerId { fn from(value: PeerId) -> Self { Self(value.0.into()) diff --git a/quant.ew/files/core/net.lua b/quant.ew/files/core/net.lua index 38beddc0..16bb79db 100644 --- a/quant.ew/files/core/net.lua +++ b/quant.ew/files/core/net.lua @@ -1,21 +1,14 @@ local bitser = dofile_once("mods/quant.ew/files/lib/bitser.lua") -local pollnet = dofile_once("mods/quant.ew/files/lib/pollnet.lua") local hex_table = dofile_once("mods/quant.ew/files/lib/hex.lua") local ctx = dofile_once("mods/quant.ew/files/core/ctx.lua") local util = dofile_once("mods/quant.ew/files/core/util.lua") local player_fns = dofile_once("mods/quant.ew/files/core/player_fns.lua") -local reactor = pollnet.Reactor() - local net_handling = dofile_once("mods/quant.ew/files/core/net_handling.lua") local net = {} net.net_handling = net_handling -function net.update() - reactor:update() -end - local string_split = util.string_split net._rpc_inner = { @@ -111,81 +104,76 @@ function net.new_rpc_namespace_with_id(id) return ret end -function net.init() - local ready = false - local addr = os.getenv("NP_NOITA_ADDR") or "127.0.0.1:21251" - net.sock = pollnet.open_ws("ws://"..addr) - reactor:run(function() - local sock = net.sock - while true do - local msg_decoded - local msg = sock:await() - if string.byte(msg, 1, 1) == 2 then - local msg_l = string.sub(msg, 2) - local res = string_split(msg_l, " ") - if res[1] == "ready" then - ready = true - else - msg_decoded = { - kind = "proxy", - key = res[1], - value = res[2], - value2 = res[3], - } - end - elseif string.byte(msg, 1, 1) == 1 then - local peer_id_b = {string.byte(msg, 2, 2+8-1)} - -- local mult = 1 - -- local peer_id = 0 - -- for _, b in ipairs(peer_id_b) do - -- peer_id = peer_id + b * mult - -- mult = mult * 256 - -- end - local peer_id = "" - for _, b in ipairs(peer_id_b) do - peer_id = hex_table[b+1] .. peer_id - end - - local msg_l = string.sub(msg, 2+8) - local success, item = pcall(bitser.loads, msg_l) - if success then - msg_decoded = { - kind = "mod", - peer_id = peer_id, - key = item.key, - value = item.value, - } - else - print("Could not deserialize: "..item) - end - elseif string.byte(msg, 1, 1) == 3 then - msg_decoded = { - kind = "proxy", - peer_id = nil, - key = string.byte(msg, 2, 2), - value = string.sub(msg, 3), - } - else - print("Unknown msg") - end - if msg_decoded ~= nil and net_handling[msg_decoded.kind] ~= nil and net_handling[msg_decoded.kind][msg_decoded.key] ~= nil then - if ctx.ready or msg_decoded.kind ~= "mod" then - util.tpcall(net_handling[msg_decoded.kind][msg_decoded.key], msg_decoded.peer_id, msg_decoded.value, msg_decoded.value2) - end - end - end - end) - while not ready do - reactor:update() - pollnet.sleep_ms(100) - local status = net.sock:status() - if status == "error" or status == "closed" then - net.connect_failed = true - return - end - --print("Waiting for connection...") +local function handle_message(msg) + local msg_decoded + if string.byte(msg, 1, 1) == 2 then + local msg_l = string.sub(msg, 2) + local res = string_split(msg_l, " ") + if res[1] == "ready" then + ready = true + else + msg_decoded = { + kind = "proxy", + key = res[1], + value = res[2], + value2 = res[3], + } + end + elseif string.byte(msg, 1, 1) == 1 then + local peer_id_b = {string.byte(msg, 2, 2+8-1)} + local peer_id = "" + for _, b in ipairs(peer_id_b) do + peer_id = hex_table[b+1] .. peer_id end + local msg_l = string.sub(msg, 2+8) + local success, item = pcall(bitser.loads, msg_l) + if success then + msg_decoded = { + kind = "mod", + peer_id = peer_id, + key = item.key, + value = item.value, + } + else + print("Could not deserialize: "..item) + end + elseif string.byte(msg, 1, 1) == 3 then + msg_decoded = { + kind = "proxy", + peer_id = nil, + key = string.byte(msg, 2, 2), + value = string.sub(msg, 3), + } + else + print("Unknown msg") + end + if msg_decoded ~= nil and net_handling[msg_decoded.kind] ~= nil and net_handling[msg_decoded.kind][msg_decoded.key] ~= nil then + if ctx.ready or msg_decoded.kind ~= "mod" then + util.tpcall(net_handling[msg_decoded.kind][msg_decoded.key], msg_decoded.peer_id, msg_decoded.value, msg_decoded.value2) + end + end +end + +function net.update() + while true do + local msg = ewext.netmanager_recv() + if msg == nil then + break + end + handle_message(msg) + end +end + +function net.init() + local ok, res = util.tpcall(ewext.netmanager_connect) + if not ok then + net.connect_failed = true + return + end + for _, opt in ipairs(res) do + handle_message(opt) + end end local DEST_PROXY = 1 @@ -198,7 +186,7 @@ function net.send_internal(msg, dest, reliable) if reliable then dest = dest + MOD_RELIABLE end - net.sock:send_binary(string.char(dest)..msg) + ewext.netmanager_send(string.char(dest)..msg) end function net.send(key, value, reliable) @@ -226,7 +214,7 @@ function net.proxy_send(key, value) end function net.proxy_bin_send(key, value) - net.sock:send_binary(string.char(DEST_PROXY_BIN, key)..value) + ewext.netmanager_send(string.char(DEST_PROXY_BIN, key)..value) end function net.proxy_notify_game_over() diff --git a/shared/Cargo.toml b/shared/Cargo.toml new file mode 100644 index 00000000..537edc48 --- /dev/null +++ b/shared/Cargo.toml @@ -0,0 +1,7 @@ +[package] +name = "shared" +version = "0.1.0" +edition = "2021" + +[dependencies] +bitcode = "0.6.3" diff --git a/shared/src/lib.rs b/shared/src/lib.rs new file mode 100644 index 00000000..7e2d803c --- /dev/null +++ b/shared/src/lib.rs @@ -0,0 +1,37 @@ +use bitcode::{Decode, Encode}; + +#[derive(Encode, Decode)] +pub struct PeerId(pub u64); + +#[derive(Encode, Decode)] +pub struct ProxyKV { + pub key: String, + pub value: String, +} + +#[derive(Encode, Decode)] +pub struct ProxyKVBin { + pub key: u8, + pub value: Vec, +} + +#[derive(Encode, Decode)] +pub struct ModMessage { + pub peer: PeerId, + pub value: Vec, +} + +#[derive(Encode, Decode)] +pub enum NoitaInbound { + // ProxyKV(ProxyKV), + // ProxyKVBin(ProxyKVBin), + // ModMessage(ModMessage), + RawMessage(Vec), + Ready, +} + +#[derive(Encode, Decode)] +pub enum NoitaOutbound { + // ModMessage(ModMessage), + Raw(Vec), +}