diff --git a/Cargo.lock b/Cargo.lock index a7d01ea..f645bfa 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -774,7 +774,26 @@ dependencies = [ ] [[package]] -name = "grebuloff" +name = "grebuloff-hlrt-native" +version = "0.1.0" +dependencies = [ + "anyhow", + "neon", + "tokio", +] + +[[package]] +name = "grebuloff-injector" +version = "0.1.0" +dependencies = [ + "clap", + "dll-syringe", + "sysinfo", + "windows", +] + +[[package]] +name = "grebuloff-llrt" version = "0.1.0" dependencies = [ "anyhow", @@ -786,6 +805,7 @@ dependencies = [ "ffxiv_client_structs", "ffxiv_client_structs_macros", "grebuloff-macros", + "grebuloff-rpc", "inventory", "itertools 0.10.5", "log", @@ -802,12 +822,10 @@ dependencies = [ ] [[package]] -name = "grebuloff-injector" +name = "grebuloff-loader" version = "0.1.0" dependencies = [ - "clap", - "dll-syringe", - "sysinfo", + "plthook", "windows", ] @@ -821,6 +839,21 @@ dependencies = [ "walkdir", ] +[[package]] +name = "grebuloff-rpc" +version = "0.1.0" +dependencies = [ + "anyhow", + "async-trait", + "bytes", + "log", + "rmp", + "rmp-serde", + "serde", + "serde_json", + "tokio", +] + [[package]] name = "gtk" version = "0.15.5" @@ -1018,6 +1051,16 @@ version = "0.2.147" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b4668fb0ea861c1df094127ac5f1da3409a82116a4ba74fca2e58ef927159bb3" +[[package]] +name = "libloading" +version = "0.6.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "351a32417a12d5f7e82c368a66781e307834dae04c6ce0cd4456d52989229883" +dependencies = [ + "cfg-if", + "winapi", +] + [[package]] name = "libudis86-sys" version = "0.2.1" @@ -1043,14 +1086,6 @@ dependencies = [ "proc-macro2", ] -[[package]] -name = "loader" -version = "0.1.0" -dependencies = [ - "plthook", - "windows", -] - [[package]] name = "lock_api" version = "0.4.10" @@ -1144,6 +1179,47 @@ dependencies = [ "winapi", ] +[[package]] +name = "neon" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "28e15415261d880aed48122e917a45e87bb82cf0260bb6db48bbab44b7464373" +dependencies = [ + "neon-build", + "neon-macros", + "neon-runtime", + "semver 0.9.0", + "smallvec", +] + +[[package]] +name = "neon-build" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8bac98a702e71804af3dacfde41edde4a16076a7bbe889ae61e56e18c5b1c811" + +[[package]] +name = "neon-macros" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b7288eac8b54af7913c60e0eb0e2a7683020dffa342ab3fd15e28f035ba897cf" +dependencies = [ + "quote", + "syn 1.0.109", + "syn-mid", +] + +[[package]] +name = "neon-runtime" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4676720fa8bb32c64c3d9f49c47a47289239ec46b4bdb66d0913cc512cb0daca" +dependencies = [ + "cfg-if", + "libloading", + "smallvec", +] + [[package]] name = "ntapi" version = "0.4.1" @@ -1504,7 +1580,7 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bfa0f585226d2e68097d4f95d113b15b83a82e819ab25717ec0590d9584ef366" dependencies = [ - "semver", + "semver 1.0.17", ] [[package]] @@ -1541,12 +1617,27 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" +[[package]] +name = "semver" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d7eb9ef2c18661902cc47e535f9bc51b78acd254da71d375c2f6720d9a40403" +dependencies = [ + "semver-parser", +] + [[package]] name = "semver" version = "1.0.17" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bebd363326d05ec3e2f532ab7660680f3b02130d780c299bca73469d521bc0ed" +[[package]] +name = "semver-parser" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3" + [[package]] name = "serde" version = "1.0.171" @@ -1674,6 +1765,17 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "syn-mid" +version = "0.5.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fea305d57546cc8cd04feb14b62ec84bf17f50e3f7b12560d7bfa9265f39d9ed" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + [[package]] name = "sysinfo" version = "0.29.4" diff --git a/Cargo.toml b/Cargo.toml index 420b896..f5b55b6 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -2,7 +2,7 @@ # LLRT # ######## [package] -name = "grebuloff" +name = "grebuloff-llrt" version = "0.1.0" edition = "2021" build = "build.rs" @@ -17,16 +17,17 @@ chrono = { workspace = true } tokio = { workspace = true } windows = { workspace = true } anyhow = { workspace = true } +rmp = { workspace = true } +rmp-serde = { workspace = true } +serde = { workspace = true } +serde_json = { workspace = true } +bytes = { workspace = true } dll-syringe = { workspace = true, features = ["payload-utils"] } - grebuloff-macros = { path = "macros" } +grebuloff-rpc = { path = "rpc" } ffxiv_client_structs = { path = "deps/FFXIVClientStructs/rust/lib", features = ["async-resolution"] } ffxiv_client_structs_macros = { path = "deps/FFXIVClientStructs/rust/macros" } msgbox = "0.7.0" -rmp = "0.8.11" -rmp-serde = "1.1.1" -serde = { version = "1.0.155", features = ["derive"] } -serde_json = "1.0.96" # deno_core = "0.191.0" # deno_ast = { version = "0.27.1", features = ["transpiling"] } inventory = "0.3.6" @@ -34,7 +35,6 @@ itertools = "0.10.5" rustc-hash = "1.1.0" retour = { version = "0.3.0", features = ["static-detour"] } uuid = { version = "1.4.0", features = ["v4", "fast-rng"] } -bytes = "1.4.0" async-trait = "0.1.71" [build-dependencies] @@ -44,8 +44,8 @@ chrono = { workspace = true } # Workspace # ############# [workspace] -members = [".", "macros", "injector", "loader"] -default-members = [".", "injector", "loader"] +members = [".", "macros", "injector", "loader", "rpc", "hlrt"] +default-members = [".", "injector", "loader", "rpc", "hlrt"] [workspace.dependencies] dll-syringe = { version = "0.15.2", default-features = false } @@ -54,6 +54,11 @@ anyhow = { version = "1.0.71" } log = { version = "0.4" } fern = { version = "0.6.2" } chrono = { version = "0.4.26" } +rmp = "0.8.11" +rmp-serde = "1.1.1" +serde = { version = "1.0.155", features = ["derive"] } +serde_json = "1.0.96" +bytes = "1.4.0" [workspace.dependencies.windows] version = "0.48.0" diff --git a/build.rs b/build.rs index 7bfeab8..a68ccd0 100644 --- a/build.rs +++ b/build.rs @@ -38,10 +38,10 @@ impl Build { Command::new("cmd") .arg("/C") .arg("pnpm") - .arg("build-if-changed") + .arg("maybe-build:js") .current_dir("hlrt") .spawn() - .expect("failed to run `pnpm build-if-changed` for HLRT"); + .expect("failed to run `pnpm maybe-build:js` for HLRT"); } } diff --git a/hlrt/.gitignore b/hlrt/.gitignore index 9415f56..a84dd46 100644 --- a/hlrt/.gitignore +++ b/hlrt/.gitignore @@ -4,3 +4,5 @@ out *.log* .bic_cache electron.vite.config.*.mjs +target +*.node diff --git a/hlrt/Cargo.toml b/hlrt/Cargo.toml new file mode 100644 index 0000000..da468ef --- /dev/null +++ b/hlrt/Cargo.toml @@ -0,0 +1,18 @@ +[package] +name = "grebuloff-hlrt-native" +version = "0.1.0" +edition = "2021" +exclude = ["*.node"] + +[lib] +crate-type = ["cdylib"] +path = "src/main/native/lib.rs" + +[dependencies] +anyhow = { workspace = true } +tokio = { workspace = true } + +[dependencies.neon] +version = "0.10" +default-features = false +features = ["napi-6", "promise-api", "task-api", "channel-api"] diff --git a/hlrt/package.json b/hlrt/package.json index 1fd99f1..881fe6b 100644 --- a/hlrt/package.json +++ b/hlrt/package.json @@ -15,8 +15,10 @@ "start": "electron-vite preview", "dev": "electron-vite dev --sourcemap", "dev:renderer": "cross-env LLRT_PIPE_ID=dev electron-vite dev --sourcemap --rendererOnly", + "build:all": "pnpm run build:native && pnpm run build:js", "build": "pnpm run typecheck && electron-vite build && electron-packager . --platform=win32 --out=../dist --executable-name=grebuloff-hlrt --overwrite", - "build-if-changed": "build-if-changed" + "build:native": "cargo-cp-artifact -ac grebuloff-hlrt-native out/main/native/native.node -- cargo build --message-format=json-render-diagnostics", + "maybe-build:js": "build-if-changed" }, "dependencies": { "@electron-toolkit/utils": "^1.0.2", @@ -46,11 +48,15 @@ "eslint-plugin-react": "^7.32.2", "prettier": "^2.8.8", "typescript": "^5.1.6", - "vite": "^4.3.9" + "vite": "^4.3.9", + "cargo-cp-artifact": "^0.1" }, "pnpm": { "patchedDependencies": { "build-if-changed@1.5.5": "patches/build-if-changed@1.5.5.patch" } - } + }, + "bic": [ + "src" + ] } diff --git a/hlrt/pnpm-lock.yaml b/hlrt/pnpm-lock.yaml index d1f91ad..0148a77 100644 --- a/hlrt/pnpm-lock.yaml +++ b/hlrt/pnpm-lock.yaml @@ -57,6 +57,9 @@ devDependencies: build-if-changed: specifier: ^1.5.5 version: 1.5.5(patch_hash=ae6edl3wnffr2fktryzoqxouge) + cargo-cp-artifact: + specifier: ^0.1 + version: 0.1.0 cross-env: specifier: ^7.0.3 version: 7.0.3 @@ -1338,6 +1341,11 @@ packages: resolution: {integrity: sha512-ENcIpYBmwAAOm/V2cXgM7rZUrKKaqisZl4ZAI520FIkqGXUxJjmaIssbRW5HVVR5tyV6ygTLIm15aU8LUmQSaQ==} dev: true + /cargo-cp-artifact@0.1.0: + resolution: {integrity: sha512-29+tOXvPWghDESqMZMFGIEtmNCn1lbkLyUPIBLXs0qO4eYFcVnN73t4hqQ9Btye4aUQjabZQQPsUtxHE6AqzUg==} + hasBin: true + dev: true + /chalk@2.4.2: resolution: {integrity: sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==} engines: {node: '>=4'} diff --git a/hlrt/src/main/native/index.ts b/hlrt/src/main/native/index.ts new file mode 100644 index 0000000..b94bb8c --- /dev/null +++ b/hlrt/src/main/native/index.ts @@ -0,0 +1 @@ +import native from './native.node'; diff --git a/hlrt/src/main/native/lib.rs b/hlrt/src/main/native/lib.rs new file mode 100644 index 0000000..9fd52d6 --- /dev/null +++ b/hlrt/src/main/native/lib.rs @@ -0,0 +1,11 @@ +use neon::prelude::*; + +fn hello(mut cx: FunctionContext) -> JsResult { + Ok(cx.string("hello node")) +} + +#[neon::main] +fn main(mut cx: ModuleContext) -> NeonResult<()> { + cx.export_function("hello", hello)?; + Ok(()) +} diff --git a/hlrt/tsconfig.node.json b/hlrt/tsconfig.node.json index 2e7fad1..f5af1da 100644 --- a/hlrt/tsconfig.node.json +++ b/hlrt/tsconfig.node.json @@ -3,7 +3,7 @@ "include": [ "electron.vite.config.*", "src/main/**/*.{ts,tsx}", - "src/preload/**/*.{ts,tsx}" + "src/preload/**/*.{ts,tsx}", ], "compilerOptions": { "composite": true, @@ -12,4 +12,4 @@ "electron-vite/node" ], } -} \ No newline at end of file +} diff --git a/loader/Cargo.toml b/loader/Cargo.toml index 2eb4a0f..4b50cba 100644 --- a/loader/Cargo.toml +++ b/loader/Cargo.toml @@ -1,5 +1,5 @@ [package] -name = "loader" +name = "grebuloff-loader" version = "0.1.0" edition = "2021" build = "build.rs" diff --git a/loader/src/lib.rs b/loader/src/lib.rs index 782862c..43d0619 100644 --- a/loader/src/lib.rs +++ b/loader/src/lib.rs @@ -2,10 +2,15 @@ use plthook::{ObjectFile, Replacement}; use std::{ ffi::{c_void, CString}, mem::ManuallyDrop, + os::windows::prelude::OsStringExt, }; use windows::{ - core::PCSTR, - Win32::{Foundation::HANDLE, System::LibraryLoader::LoadLibraryA}, + core::{ComInterface, PCSTR}, + Win32::{ + Foundation::HANDLE, + Graphics::Dxgi::IDXGIFactory2, + System::LibraryLoader::{GetModuleFileNameW, LoadLibraryA}, + }, }; use windows::{ core::{HRESULT, HSTRING}, @@ -19,10 +24,13 @@ use windows::{ }, }; +const ROOT_ENV_VAR: &'static str = "GREBULOFF_ROOT"; +const ROOT_FILE: &'static str = "grebuloff_root.txt"; + const ERROR_NO_ROOT: &'static str = r#"Could not find the Grebuloff root directory! We checked the following locations: -1. "GREBULOFF_PATH" environment variable passed to the game executable +1. "GREBULOFF_ROOT" environment variable passed to the game executable 2. "grebuloff_root.txt" in the same directory as the game executable 3. The default installation directory: %AppData%\Grebuloff @@ -101,7 +109,7 @@ The game will now exit."#, } unsafe extern "system" fn create_dxgi_factory_wrapper( - riid: *const (), + _riid: *const (), pp_factory: *mut *mut c_void, ) -> HRESULT { // remove the IAT hook now that we've been called @@ -116,17 +124,18 @@ unsafe extern "system" fn create_dxgi_factory_wrapper( load_grebuloff(); // call CreateDXGIFactory1 from dxgi.dll - // we use CreateDXGIFactory1 instead of CreateDXGIFactory, to create a DXGI 1.1 factory, - // as opposed to the DXGI 1.0 factory that the game creates - // this shouldn't break anything, but it does allow us to use surface sharing in the future, - // for high-performance UI rendering + // we use CreateDXGIFactory1 instead of CreateDXGIFactory, passing in IDXGIFactory2 as the riid, + // to create a DXGI 1.2 factory, as opposed to the DXGI 1.0 factory that the game creates + // this shouldn't break anything, but it does allow us to use surface sharing from Chromium + // (once we implement that), for high-performance UI rendering let dxgi_dll = LoadLibraryA(PCSTR::from_raw(b"dxgi.dll\0".as_ptr())).unwrap(); - let original_func: Option HRESULT> = + let original_func: Option HRESULT> = GetProcAddress(dxgi_dll, PCSTR::from_raw(b"CreateDXGIFactory1\0".as_ptr())) .map(|func| std::mem::transmute(func)); + // CreateDXGIFactory1() match original_func { - Some(original_func) => original_func(riid, pp_factory), + Some(original_func) => original_func(&IDXGIFactory2::IID, pp_factory), None => { display_error("...huh? failed to find CreateDXGIFactory1 in dxgi.dll..."); std::process::exit(4); @@ -173,8 +182,27 @@ fn get_grebuloff_root() -> GrebuloffRoot { // 2. `grebuloff_root.txt` in the same directory as the game's EXE // 3. default to %AppData%\Grebuloff // if none of these exist, we can't continue - display an error message and exit - std::env::var("GREBULOFF_ROOT") - .or_else(|_| std::fs::read_to_string("grebuloff_root.txt").map(|s| s.trim().to_owned())) + std::env::var(ROOT_ENV_VAR) + .or_else(|_| { + // usually we'll be in the game directory, but we might not be + // ensure we search for grebuloff_root.txt in the game directory + let game_dir = unsafe { + let mut exe_path = [0u16; 1024]; + let exe_path_len = GetModuleFileNameW(None, &mut exe_path); + + std::path::PathBuf::from(std::ffi::OsString::from_wide( + &exe_path[..exe_path_len as usize], + )) + }; + + std::fs::read_to_string( + game_dir + .parent() + .map(|p| p.join(ROOT_FILE)) + .unwrap_or(ROOT_FILE.into()), + ) + .map(|s| s.trim().to_owned()) + }) .or_else(|_| { if let Ok(appdata) = std::env::var("APPDATA") { let mut path = std::path::PathBuf::from(appdata); diff --git a/rpc/Cargo.toml b/rpc/Cargo.toml new file mode 100644 index 0000000..69f37ad --- /dev/null +++ b/rpc/Cargo.toml @@ -0,0 +1,15 @@ +[package] +name = "grebuloff-rpc" +version = "0.1.0" +edition = "2021" + +[dependencies] +anyhow = { workspace = true } +log = { workspace = true } +tokio = { workspace = true } +rmp = { workspace = true } +rmp-serde = { workspace = true } +serde = { workspace = true } +serde_json = { workspace = true } +bytes = { workspace = true } +async-trait = "0.1.71" diff --git a/rpc/src/lib.rs b/rpc/src/lib.rs new file mode 100644 index 0000000..9d74085 --- /dev/null +++ b/rpc/src/lib.rs @@ -0,0 +1,25 @@ +use serde::{Deserialize, Serialize}; + +pub mod ui; + +#[derive(Debug, PartialEq, Deserialize, Serialize)] +#[serde(untagged)] +pub enum RpcMessageDirection { + /// Serverbound (client-to-server) communication. + #[serde(skip_serializing)] + Serverbound(RpcServerboundMessage), + + /// Clientbound (server-to-client) communication. + #[serde(skip_deserializing)] + Clientbound(RpcClientboundMessage), +} + +#[derive(Debug, PartialEq, Deserialize)] +pub enum RpcServerboundMessage { + Ui(ui::UiRpcServerboundMessage), +} + +#[derive(Debug, PartialEq, Serialize)] +pub enum RpcClientboundMessage { + Ui(ui::UiRpcClientboundMessage), +} diff --git a/rpc/src/ui.rs b/rpc/src/ui.rs new file mode 100644 index 0000000..b5a78d5 --- /dev/null +++ b/rpc/src/ui.rs @@ -0,0 +1,89 @@ +use super::{RpcClientboundMessage, RpcServerboundMessage}; +use anyhow::{bail, Result}; +use bytes::{Buf, Bytes, BytesMut}; +use serde::Deserialize; +use serde::Serialize; + +#[derive(Debug, PartialEq, Deserialize)] +pub enum UiRpcServerboundMessage {} + +impl TryFrom for UiRpcServerboundMessage { + type Error = (); + + fn try_from(msg: RpcServerboundMessage) -> Result { + match msg { + RpcServerboundMessage::Ui(msg) => Ok(msg), + _ => Err(()), + } + } +} + +impl From for RpcClientboundMessage { + fn from(msg: UiRpcClientboundMessage) -> Self { + RpcClientboundMessage::Ui(msg) + } +} + +// note to future self: use actual structs instead of enum variant values +// since rmp-serde doesn't properly (how we want it to, anyways) support +// variant values +#[derive(Debug, PartialEq, Serialize)] +pub enum UiRpcClientboundMessage { + /// Sent when the game window is resized. + /// Triggers a resize of the UI. + Resize(UiRpcClientboundResize), +} + +#[derive(Debug, PartialEq)] +pub struct UiRpcServerboundPaint { + pub width: u16, + pub height: u16, + pub format: ImageFormat, + pub data: Bytes, +} + +impl UiRpcServerboundPaint { + pub fn from_raw(mut buf: BytesMut) -> Result { + let data = buf.split_off(5).freeze(); + + // image format is first, so we don't overlap 0x80..=0x8F | 0xDE..=0xDF (msgpack map) + let format = match buf.get_u8() { + 0 => ImageFormat::BGRA8, + _ => bail!("invalid image format"), + }; + let width = buf.get_u16_le(); + let height = buf.get_u16_le(); + + Ok(Self { + width, + height, + format, + data, + }) + } +} + +#[derive(Debug, PartialEq, Serialize)] +pub struct UiRpcClientboundResize { + pub width: u32, + pub height: u32, +} + +/// Represents supported image formats. +#[derive(Debug, PartialEq, Deserialize, Serialize)] +#[repr(u8)] +pub enum ImageFormat { + BGRA8, +} + +impl ImageFormat { + pub fn byte_size_of(&self, width: usize, height: usize) -> usize { + width * height * self.bytes_per_pixel() as usize + } + + pub fn bytes_per_pixel(&self) -> u32 { + match self { + ImageFormat::BGRA8 => 4, + } + } +} diff --git a/src/rpc/mod.rs b/src/rpc/mod.rs index 4afb58c..980172e 100644 --- a/src/rpc/mod.rs +++ b/src/rpc/mod.rs @@ -1,6 +1,7 @@ use anyhow::{anyhow, bail, Result}; use async_trait::async_trait; use bytes::{Buf, BytesMut}; +use grebuloff_rpc::{RpcClientboundMessage, RpcMessageDirection, RpcServerboundMessage}; use log::{error, info}; use rustc_hash::FxHashMap; use serde::{Deserialize, Serialize}; @@ -16,28 +17,6 @@ pub mod ui; static mut CLIENT_STATE: OnceLock>>> = OnceLock::new(); -#[derive(Debug, PartialEq, Deserialize, Serialize)] -#[serde(untagged)] -pub enum RpcMessageDirection { - /// Serverbound (client-to-server) communication. - #[serde(skip_serializing)] - Serverbound(RpcServerboundMessage), - - /// Clientbound (server-to-client) communication. - #[serde(skip_deserializing)] - Clientbound(RpcClientboundMessage), -} - -#[derive(Debug, PartialEq, Deserialize)] -pub enum RpcServerboundMessage { - Ui(ui::UiRpcServerboundMessage), -} - -#[derive(Debug, PartialEq, Serialize)] -pub enum RpcClientboundMessage { - Ui(ui::UiRpcClientboundMessage), -} - pub struct RpcServerOptions { pub pipe_name: Cow<'static, str>, pub buffer_size: usize, diff --git a/src/rpc/ui.rs b/src/rpc/ui.rs index 3184048..ce30ef0 100644 --- a/src/rpc/ui.rs +++ b/src/rpc/ui.rs @@ -1,7 +1,8 @@ -use super::{RpcClientboundMessage, RpcServer, RpcServerOptions, RpcServerboundMessage}; +use super::{RpcServer, RpcServerOptions}; use crate::{get_execution_id, get_tokio_rt}; -use anyhow::{bail, Result}; -use bytes::{Buf, Bytes, BytesMut}; +use anyhow::Result; +use bytes::BytesMut; +use grebuloff_rpc::ui::*; use log::debug; use std::sync::OnceLock; use tokio::sync::mpsc; @@ -10,90 +11,6 @@ use tokio::sync::mpsc; // TODO: make this configurable, or automatically sized based on the game window size const PIPE_BUFFER_SIZE: usize = 32 * 1024 * 1024; -#[derive(Debug, PartialEq, Deserialize)] -pub enum UiRpcServerboundMessage {} - -impl TryFrom for UiRpcServerboundMessage { - type Error = (); - - fn try_from(msg: RpcServerboundMessage) -> Result { - match msg { - RpcServerboundMessage::Ui(msg) => Ok(msg), - _ => Err(()), - } - } -} - -impl From for RpcClientboundMessage { - fn from(msg: UiRpcClientboundMessage) -> Self { - RpcClientboundMessage::Ui(msg) - } -} - -// note to future self: use actual structs instead of enum variant values -// since rmp-serde doesn't properly (how we want it to, anyways) support -// variant values -#[derive(Debug, PartialEq, Serialize)] -pub enum UiRpcClientboundMessage { - /// Sent when the game window is resized. - /// Triggers a resize of the UI. - Resize(UiRpcClientboundResize), -} - -#[derive(Debug, PartialEq)] -pub struct UiRpcServerboundPaint { - pub width: u16, - pub height: u16, - pub format: ImageFormat, - pub data: Bytes, -} - -impl UiRpcServerboundPaint { - fn from_raw(mut buf: BytesMut) -> Result { - let data = buf.split_off(5).freeze(); - - // image format is first, so we don't overlap 0x80..=0x8F | 0xDE..=0xDF (msgpack map) - let format = match buf.get_u8() { - 0 => ImageFormat::BGRA8, - _ => bail!("invalid image format"), - }; - let width = buf.get_u16_le(); - let height = buf.get_u16_le(); - - Ok(Self { - width, - height, - format, - data, - }) - } -} - -#[derive(Debug, PartialEq, Serialize)] -pub struct UiRpcClientboundResize { - pub width: u32, - pub height: u32, -} - -/// Represents supported image formats. -#[derive(Debug, PartialEq, Deserialize, Serialize)] -#[repr(u8)] -pub enum ImageFormat { - BGRA8, -} - -impl ImageFormat { - pub fn byte_size_of(&self, width: usize, height: usize) -> usize { - width * height * self.bytes_per_pixel() as usize - } - - pub fn bytes_per_pixel(&self) -> u32 { - match self { - ImageFormat::BGRA8 => 4, - } - } -} - pub struct UiRpcServer { options: RpcServerOptions, } diff --git a/src/ui.rs b/src/ui.rs index 3ae8613..e878ea5 100644 --- a/src/ui.rs +++ b/src/ui.rs @@ -1,9 +1,7 @@ -use crate::{ - get_execution_id, - rpc::ui::{ImageFormat, UiRpcServerboundPaint}, -}; +use crate::get_execution_id; use anyhow::{bail, Result}; use bytes::Bytes; +use grebuloff_rpc::ui::{ImageFormat, UiRpcServerboundPaint}; use log::{error, info, warn}; use std::{ path::{Path, PathBuf}, @@ -14,7 +12,7 @@ use std::{ }, }; use tokio::{ - io::{AsyncBufReadExt, AsyncReadExt, BufReader}, + io::{AsyncBufReadExt, BufReader}, process::Command, };