Switch to ewext for connection between noita and the proxy.

This commit is contained in:
IQuant 2024-11-29 21:35:18 +03:00
parent 7b34f77b68
commit 75d39b2ca5
12 changed files with 713 additions and 121 deletions

291
ewext/Cargo.lock generated
View file

@ -17,6 +17,12 @@ version = "2.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "512761e0bb2578dd7380c6baaa0f4ce03e84f95e960231d1dec8bf4d7d6e2627" checksum = "512761e0bb2578dd7380c6baaa0f4ce03e84f95e960231d1dec8bf4d7d6e2627"
[[package]]
name = "arrayvec"
version = "0.7.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7c02d123df017efcdfbd739ef81735b36c5ba83ec3c59c80a9d7ecc718f92e50"
[[package]] [[package]]
name = "backtrace" name = "backtrace"
version = "0.3.74" version = "0.3.74"
@ -32,22 +38,111 @@ dependencies = [
"windows-targets", "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]] [[package]]
name = "cfg-if" name = "cfg-if"
version = "1.0.0" version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" 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]] [[package]]
name = "ewext" name = "ewext"
version = "0.4.0" version = "0.4.0"
dependencies = [ dependencies = [
"backtrace", "backtrace",
"bitcode",
"eyre", "eyre",
"iced-x86", "iced-x86",
"libloading", "libloading",
"noita_api", "noita_api",
"noita_api_macro", "noita_api_macro",
"shared",
"tungstenite",
] ]
[[package]] [[package]]
@ -60,18 +155,68 @@ dependencies = [
"once_cell", "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]] [[package]]
name = "gimli" name = "gimli"
version = "0.31.1" version = "0.31.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "07e28edb80900c19c28f1072f2e8aeca7fa06b23cd4169cefe1af5aa3260783f" checksum = "07e28edb80900c19c28f1072f2e8aeca7fa06b23cd4169cefe1af5aa3260783f"
[[package]]
name = "glam"
version = "0.29.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dc46dd3ec48fdd8e693a98d2b8bafae273a2d54c1de02a2a7e3d57d501f39677"
[[package]] [[package]]
name = "heck" name = "heck"
version = "0.5.0" version = "0.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" 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]] [[package]]
name = "iced-x86" name = "iced-x86"
version = "1.21.0" version = "1.21.0"
@ -115,6 +260,12 @@ dependencies = [
"windows-targets", "windows-targets",
] ]
[[package]]
name = "log"
version = "0.4.22"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a7a70ba024b9dc04c27ea2f0c0548feb474ec5c54bba33a7f72f873a39d07b24"
[[package]] [[package]]
name = "memchr" name = "memchr"
version = "2.7.4" version = "2.7.4"
@ -165,6 +316,15 @@ version = "1.20.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1261fe7e33c73b354eab43b1273a57c8f967d0391e80353e51f764ac02cf6775" 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]] [[package]]
name = "proc-macro2" name = "proc-macro2"
version = "1.0.89" version = "1.0.89"
@ -183,6 +343,36 @@ dependencies = [
"proc-macro2", "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]] [[package]]
name = "rustc-demangle" name = "rustc-demangle"
version = "0.1.24" version = "0.1.24"
@ -227,6 +417,24 @@ dependencies = [
"serde", "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]] [[package]]
name = "syn" name = "syn"
version = "2.0.87" version = "2.0.87"
@ -238,12 +446,74 @@ dependencies = [
"unicode-ident", "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]] [[package]]
name = "unicode-ident" name = "unicode-ident"
version = "1.0.13" version = "1.0.13"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e91b56cd4cadaeb79bbf1a5645f6b4f8dc5bde8834ad5894a8db35fda9efa1fe" 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]] [[package]]
name = "windows-targets" name = "windows-targets"
version = "0.52.6" version = "0.52.6"
@ -307,3 +577,24 @@ name = "windows_x86_64_msvc"
version = "0.52.6" version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" 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",
]

View file

@ -10,6 +10,7 @@ crate-type = ["cdylib"]
[profile.release] [profile.release]
lto = true lto = true
strip = true strip = true
panic = "abort"
[dependencies] [dependencies]
libloading = "0.8.5" libloading = "0.8.5"
@ -17,4 +18,7 @@ backtrace = "0.3.74"
iced-x86 = "1.21.0" iced-x86 = "1.21.0"
noita_api_macro = {path = "noita_api_macro"} noita_api_macro = {path = "noita_api_macro"}
eyre = "0.6.12" eyre = "0.6.12"
noita_api = {path = "noita_api"} noita_api = {path = "noita_api"}
tungstenite = "0.24.0"
bitcode = "0.6.3"
shared = {path = "../shared"}

View file

@ -71,6 +71,17 @@ impl LuaState {
.wrap_err("Attempting to get lua string, expecting it to be utf-8")?) .wrap_err("Attempting to get lua string, expecting it to be utf-8")?)
} }
pub fn to_raw_string(&self, index: i32) -> eyre::Result<Vec<u8>> {
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 { pub fn to_cfunction(&self, index: i32) -> lua_CFunction {
unsafe { LUA.lua_tocfunction(self.lua, index) } 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) { pub fn push_nil(&self) {
unsafe { LUA.lua_pushnil(self.lua) } unsafe { LUA.lua_pushnil(self.lua) }
} }
@ -139,6 +156,22 @@ impl LuaState {
fn is_nil_or_none(&self, index: i32) -> bool { fn is_nil_or_none(&self, index: i32) -> bool {
(unsafe { LUA.lua_type(self.lua, index) }) <= 0 (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<u8>);
impl From<Vec<u8>> for RawString {
fn from(value: Vec<u8>) -> Self {
Self(value)
}
} }
/// Used for types that can be returned from functions that were defined in rust to lua. /// Used for types that can be returned from functions that were defined in rust to lua.
@ -172,6 +205,37 @@ impl<R: LuaFnRet> LuaFnRet for eyre::Result<R> {
} }
} }
impl<T: LuaFnRet> LuaFnRet for Option<T> {
fn do_return(self, lua: LuaState) -> c_int {
match self {
Some(val) => val.do_return(lua),
None => {
lua.push_nil();
1
}
}
}
}
impl<T: LuaFnRet> LuaFnRet for Vec<T> {
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<T>'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. /// Trait for arguments that can be put on lua stack.
pub(crate) trait LuaPutValue { pub(crate) trait LuaPutValue {
fn put(&self, lua: LuaState); fn put(&self, lua: LuaState);

View file

@ -2,6 +2,7 @@ use std::{
arch::asm, arch::asm,
cell::{LazyCell, RefCell}, cell::{LazyCell, RefCell},
ffi::{c_int, c_void}, ffi::{c_int, c_void},
sync::{LazyLock, Mutex},
time::Instant, time::Instant,
}; };
@ -9,18 +10,19 @@ use addr_grabber::{grab_addrs, grabbed_fns, grabbed_globals};
use eyre::{bail, OptionExt}; use eyre::{bail, OptionExt};
use modules::{entity_sync::EntitySync, Module}; use modules::{entity_sync::EntitySync, Module};
use net::NetManager;
use noita::{ntypes::Entity, pixel::NoitaPixelRun, ParticleWorldState}; use noita::{ntypes::Entity, pixel::NoitaPixelRun, ParticleWorldState};
use noita_api::{ use noita_api::{
lua::{lua_bindings::lua_State, LuaState, ValuesOnStack, LUA}, lua::{lua_bindings::lua_State, LuaFnRet, LuaState, RawString, ValuesOnStack, LUA},
DamageModelComponent, DamageModelComponent,
}; };
use noita_api_macro::add_lua_fn; use noita_api_macro::add_lua_fn;
use shared::{NoitaInbound, ProxyKV};
pub mod noita;
mod modules;
mod addr_grabber; mod addr_grabber;
mod modules;
mod net;
pub mod noita;
thread_local! { thread_local! {
static STATE: LazyCell<RefCell<ExtState>> = LazyCell::new(|| { static STATE: LazyCell<RefCell<ExtState>> = LazyCell::new(|| {
@ -29,6 +31,8 @@ thread_local! {
}); });
} }
static NETMANAGER: LazyLock<Mutex<Option<NetManager>>> = LazyLock::new(|| Default::default());
#[derive(Default)] #[derive(Default)]
struct ExtState { struct ExtState {
particle_world_state: Option<ParticleWorldState>, particle_world_state: Option<ParticleWorldState>,
@ -94,6 +98,77 @@ fn make_ephemerial(lua: LuaState) -> eyre::Result<()> {
Ok(()) Ok(())
} }
struct InitKV {
key: String,
value: String,
}
impl From<ProxyKV> for InitKV {
fn from(value: ProxyKV) -> Self {
InitKV {
key: value.key,
value: value.value,
}
}
}
fn netmanager_connect(_lua: LuaState) -> eyre::Result<Vec<RawString>> {
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<Option<RawString>> {
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) { fn on_world_initialized(lua: LuaState) {
grab_addrs(lua); grab_addrs(lua);
@ -175,7 +250,10 @@ fn test_fn(_lua: LuaState) -> eyre::Result<()> {
#[no_mangle] #[no_mangle]
pub unsafe extern "C" fn luaopen_ewext0(lua: *mut lua_State) -> c_int { pub unsafe extern "C" fn luaopen_ewext0(lua: *mut lua_State) -> c_int {
println!("Initializing ewext"); println!("Initializing ewext");
// Reset some stuff
STATE.with(|state| state.take()); STATE.with(|state| state.take());
NETMANAGER.lock().unwrap().take();
unsafe { unsafe {
LUA.lua_createtable(lua, 0, 0); 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!(test_fn);
add_lua_fn!(bench_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); add_lua_fn!(module_on_world_update);
} }
println!("Initializing ewext - Ok"); println!("Initializing ewext - Ok");

65
ewext/src/net.rs Normal file
View file

@ -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<TcpStream>,
}
impl NetManager {
pub(crate) fn new() -> eyre::Result<Self> {
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<Option<NoitaInbound>> {
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();
}
}

View file

@ -2072,6 +2072,7 @@ dependencies = [
"self-replace", "self-replace",
"serde", "serde",
"serde_json", "serde_json",
"shared",
"shlex", "shlex",
"socket2", "socket2",
"steamworks", "steamworks",
@ -3208,6 +3209,13 @@ dependencies = [
"lazy_static", "lazy_static",
] ]
[[package]]
name = "shared"
version = "0.1.0"
dependencies = [
"bitcode",
]
[[package]] [[package]]
name = "shlex" name = "shlex"
version = "1.3.0" version = "1.3.0"

View file

@ -23,7 +23,7 @@ tracing-subscriber = { version = "0.3.18", features = ["env-filter"] }
tracing = "0.1.40" tracing = "0.1.40"
tangled = { path = "tangled" } tangled = { path = "tangled" }
serde = { version = "1.0.207", features = ["serde_derive", "derive"] } 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"]} lz4_flex = { version = "0.11.3", default-features = false, features = ["std"]}
rand = "0.8.5" rand = "0.8.5"
steamworks = { git = "https://github.com/IntQuant/steamworks-rs", branch = "avatar-fix" } steamworks = { git = "https://github.com/IntQuant/steamworks-rs", branch = "avatar-fix" }
@ -50,6 +50,7 @@ dashmap = "6.0.1"
eyre = "0.6.12" eyre = "0.6.12"
tokio = { version = "1.40.0", features = ["macros", "rt-multi-thread"] } tokio = { version = "1.40.0", features = ["macros", "rt-multi-thread"] }
tracing-appender = "0.2.3" tracing-appender = "0.2.3"
shared = {path = "../shared"}
[build-dependencies] [build-dependencies]
winresource = "0.1.17" winresource = "0.1.17"

View file

@ -2,15 +2,17 @@ use bitcode::{Decode, Encode};
use messages::{MessageRequest, NetMsg}; use messages::{MessageRequest, NetMsg};
use omni::OmniPeerId; use omni::OmniPeerId;
use proxy_opt::ProxyOpt; use proxy_opt::ProxyOpt;
use shared::{NoitaInbound, NoitaOutbound};
use socket2::{Domain, Socket, Type}; use socket2::{Domain, Socket, Type};
use std::fs::{create_dir, remove_dir_all, File}; use std::fs::{create_dir, remove_dir_all, File};
use std::io::Write;
use std::path::PathBuf; use std::path::PathBuf;
use std::sync::atomic::{AtomicBool, AtomicI32, AtomicU16, Ordering}; use std::sync::atomic::{AtomicBool, AtomicI32, AtomicU16, Ordering};
use std::sync::{Arc, Mutex}; use std::sync::{Arc, Mutex};
use std::{ use std::{
env, env,
fmt::Display, fmt::Display,
io::{self, Write}, io::{self},
net::{SocketAddr, TcpListener, TcpStream}, net::{SocketAddr, TcpListener, TcpStream},
thread::{self, JoinHandle}, thread::{self, JoinHandle},
time::{Duration, Instant}, time::{Duration, Instant},
@ -33,11 +35,15 @@ mod proxy_opt;
pub mod steam_networking; pub mod steam_networking;
pub mod world; 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 { pub(crate) fn ws_encode_proxy(key: &'static str, value: impl Display) -> tungstenite::Message {
let mut buf = Vec::new(); let mut buf = Vec::new();
buf.push(2); buf.push(2);
write!(buf, "{} {}", key, value).unwrap(); 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 { 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(3);
buf.push(key); buf.push(key);
buf.extend(data); buf.extend(data);
tungstenite::Message::Binary(buf) ws_bitcode(&NoitaInbound::RawMessage(buf))
} }
pub(crate) fn ws_encode_mod(peer: OmniPeerId, data: &[u8]) -> tungstenite::Message { 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.push(1u8);
buf.extend_from_slice(&peer.0.to_le_bytes()); buf.extend_from_slice(&peer.0.to_le_bytes());
buf.extend_from_slice(data); 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 struct DebugMarker {
pub x: f64, pub x: f64,
pub y: f64, pub y: f64,
@ -94,7 +121,7 @@ impl NetInnerState {
let mut buf = Vec::new(); let mut buf = Vec::new();
buf.push(2); buf.push(2);
value.write_opt(&mut buf, key); value.write_opt(&mut buf, key);
let message = tungstenite::Message::Binary(buf); let message = ws_bitcode(&NoitaInbound::RawMessage(buf));
self.try_ws_write(message); self.try_ws_write(message);
} }
} }
@ -440,7 +467,9 @@ impl NetManager {
match msg { match msg {
Ok(msg) => { Ok(msg) => {
if let tungstenite::Message::Binary(msg) = 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)) Err(tungstenite::Error::Io(io_err))
@ -610,7 +639,7 @@ impl NetManager {
let progress = settings.progress.join(","); let progress = settings.progress.join(",");
state.try_ws_write_option("progress", progress.as_str()); 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 // TODO? those are currently ignored by mod
for id in self.peer.iter_peer_ids() { for id in self.peer.iter_peer_ids() {
state.try_ws_write(ws_encode_proxy("join", id.as_hex())); state.try_ws_write(ws_encode_proxy("join", id.as_hex()));
@ -618,31 +647,35 @@ impl NetManager {
info!("Settings sent") info!("Settings sent")
} }
fn handle_mod_message_2(&self, msg: Vec<u8>, state: &mut NetInnerState) { fn handle_mod_message_2(&self, msg: NoitaOutbound, state: &mut NetInnerState) {
match msg[0] & 0b11 { match msg {
// Message to proxy NoitaOutbound::Raw(raw_msg) => {
1 => { match raw_msg[0] & 0b11 {
self.handle_message_to_proxy(&msg[1..], state); // Message to proxy
} 1 => {
// Broadcast self.handle_message_to_proxy(&raw_msg[1..], state);
2 => { }
let msg_to_send = NetMsg::ModRaw { // Broadcast
data: msg[1..].to_owned(), 2 => {
}; let msg_to_send = NetMsg::ModRaw {
let reliable = msg[0] & 4 > 0; data: raw_msg[1..].to_owned(),
self.broadcast( };
&msg_to_send, let reliable = raw_msg[0] & 4 > 0;
if reliable { self.broadcast(
Reliability::Reliable &msg_to_send,
} else { if reliable {
Reliability::Unreliable Reliability::Reliable
}, } else {
); Reliability::Unreliable
} },
// Binary message to proxy );
3 => self.handle_bin_message_to_proxy(&msg[1..], state), }
msg_variant => { // Binary message to proxy
error!("Unknown msg variant from mod: {}", msg_variant) 3 => self.handle_bin_message_to_proxy(&raw_msg[1..], state),
msg_variant => {
error!("Unknown msg variant from mod: {}", msg_variant)
}
}
} }
} }
} }

View file

@ -7,6 +7,18 @@ use tangled::{PeerId, Reliability};
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Decode, Encode)] #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Decode, Encode)]
pub struct OmniPeerId(pub u64); pub struct OmniPeerId(pub u64);
impl From<shared::PeerId> for OmniPeerId {
fn from(value: shared::PeerId) -> Self {
OmniPeerId(value.0)
}
}
impl From<OmniPeerId> for shared::PeerId {
fn from(value: OmniPeerId) -> Self {
shared::PeerId(value.0)
}
}
impl From<PeerId> for OmniPeerId { impl From<PeerId> for OmniPeerId {
fn from(value: PeerId) -> Self { fn from(value: PeerId) -> Self {
Self(value.0.into()) Self(value.0.into())

View file

@ -1,21 +1,14 @@
local bitser = dofile_once("mods/quant.ew/files/lib/bitser.lua") 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 hex_table = dofile_once("mods/quant.ew/files/lib/hex.lua")
local ctx = dofile_once("mods/quant.ew/files/core/ctx.lua") local ctx = dofile_once("mods/quant.ew/files/core/ctx.lua")
local util = dofile_once("mods/quant.ew/files/core/util.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 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_handling = dofile_once("mods/quant.ew/files/core/net_handling.lua")
local net = {} local net = {}
net.net_handling = net_handling net.net_handling = net_handling
function net.update()
reactor:update()
end
local string_split = util.string_split local string_split = util.string_split
net._rpc_inner = { net._rpc_inner = {
@ -111,81 +104,76 @@ function net.new_rpc_namespace_with_id(id)
return ret return ret
end end
function net.init() local function handle_message(msg)
local ready = false local msg_decoded
local addr = os.getenv("NP_NOITA_ADDR") or "127.0.0.1:21251" if string.byte(msg, 1, 1) == 2 then
net.sock = pollnet.open_ws("ws://"..addr) local msg_l = string.sub(msg, 2)
reactor:run(function() local res = string_split(msg_l, " ")
local sock = net.sock if res[1] == "ready" then
while true do ready = true
local msg_decoded else
local msg = sock:await() msg_decoded = {
if string.byte(msg, 1, 1) == 2 then kind = "proxy",
local msg_l = string.sub(msg, 2) key = res[1],
local res = string_split(msg_l, " ") value = res[2],
if res[1] == "ready" then value2 = res[3],
ready = true }
else end
msg_decoded = { elseif string.byte(msg, 1, 1) == 1 then
kind = "proxy", local peer_id_b = {string.byte(msg, 2, 2+8-1)}
key = res[1], local peer_id = ""
value = res[2], for _, b in ipairs(peer_id_b) do
value2 = res[3], peer_id = hex_table[b+1] .. peer_id
}
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...")
end 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 end
local DEST_PROXY = 1 local DEST_PROXY = 1
@ -198,7 +186,7 @@ function net.send_internal(msg, dest, reliable)
if reliable then if reliable then
dest = dest + MOD_RELIABLE dest = dest + MOD_RELIABLE
end end
net.sock:send_binary(string.char(dest)..msg) ewext.netmanager_send(string.char(dest)..msg)
end end
function net.send(key, value, reliable) function net.send(key, value, reliable)
@ -226,7 +214,7 @@ function net.proxy_send(key, value)
end end
function net.proxy_bin_send(key, value) 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 end
function net.proxy_notify_game_over() function net.proxy_notify_game_over()

7
shared/Cargo.toml Normal file
View file

@ -0,0 +1,7 @@
[package]
name = "shared"
version = "0.1.0"
edition = "2021"
[dependencies]
bitcode = "0.6.3"

37
shared/src/lib.rs Normal file
View file

@ -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<u8>,
}
#[derive(Encode, Decode)]
pub struct ModMessage {
pub peer: PeerId,
pub value: Vec<u8>,
}
#[derive(Encode, Decode)]
pub enum NoitaInbound {
// ProxyKV(ProxyKV),
// ProxyKVBin(ProxyKVBin),
// ModMessage(ModMessage),
RawMessage(Vec<u8>),
Ready,
}
#[derive(Encode, Decode)]
pub enum NoitaOutbound {
// ModMessage(ModMessage),
Raw(Vec<u8>),
}