From 7b8177b27b771acfc0b177ca837dff6ebc231fc2 Mon Sep 17 00:00:00 2001 From: Joshua Goins Date: Fri, 27 Jun 2025 17:58:14 -0400 Subject: [PATCH] Allow building for WebAssembly This really only allows you to access the packet parsing bits, starting the servers isn't possible for a variety of reasons. --- Cargo.toml | 45 ++++++++++++++------------ src/lib.rs | 3 ++ src/packet/mod.rs | 8 ++++- src/packet/parsing.rs | 56 -------------------------------- src/packet/send_helpers.rs | 66 ++++++++++++++++++++++++++++++++++++++ 5 files changed, 100 insertions(+), 78 deletions(-) create mode 100644 src/packet/send_helpers.rs diff --git a/Cargo.toml b/Cargo.toml index 201465e..b5db974 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -51,27 +51,13 @@ serde = { version = "1.0", features = ["derive"], default-features = false } serde_json = { version = "1.0", features = ["std"], default-features = false } [dependencies] -# Used for the web servers -axum = { version = "0.8", features = ["json", "tokio", "http1", "form", "query", "multipart"], default-features = false } -axum-extra = { version = "0.10", features = ["cookie"], default-features = false } - # Serialization used in almost every server serde = { version = "1.0", features = ["derive"], default-features = false } -serde_json = { version = "1.0", default-features = false } +serde_json = { version = "1.0", features = ["std"], default-features = false } serde_yaml_ng = { version = "0.10", default-features = false } -# Async runtime -tokio = { version = "1.45", features = ["macros", "rt", "rt-multi-thread", "io-util"], default-features = false } - # Logging tracing = { version = "0.1", default-features = false } -tracing-subscriber = { version = "0.3", features = ["fmt"], default-features = false } - -# Used currently for SID generation -fastrand = { version = "2.3", features = ["std"], default-features = false } - -# HTML templates used in the web servers -minijinja = { version = "2.10", features = ["serde", "loader", "multi_template"], default-features = false } # Serialization of packets binrw = { version = "0.15", features = ["std"], default-features = false } @@ -82,12 +68,32 @@ md5 = { version = "0.7", default-features = false } # Used to access game data physis = { git = "https://github.com/redstrate/physis", default-features = false } -# Used for data persistence -rusqlite = { version = "0.36", features = ["bundled"], default-features = false } - # needed for c-style bitflags bitflags = { version = "2.9", default-features = false } +# excel sheet data +icarus = { git = "https://github.com/redstrate/Icarus", branch = "ver/2025.04.16.0000.0000", features = ["Warp", "Tribe", "ClassJob", "World", "TerritoryType", "Race", "Aetheryte", "EquipSlotCategory", "Action", "WeatherRate", "PlaceName"], default-features = false } + +[target.'cfg(not(target_family = "wasm"))'.dependencies] +# Used for the web servers +axum = { version = "0.8", features = ["json", "tokio", "http1", "form", "query", "multipart"], default-features = false } +axum-extra = { version = "0.10", features = ["cookie"], default-features = false } + +# Async runtime +tokio = { version = "1.45", features = ["macros", "rt", "rt-multi-thread", "io-util"], default-features = false } + +# Logging +tracing-subscriber = { version = "0.3", features = ["fmt"], default-features = false } + +# Used currently for SID generation +fastrand = { version = "2.3", features = ["std"], default-features = false } + +# HTML templates used in the web servers +minijinja = { version = "2.10", features = ["serde", "loader", "multi_template"], default-features = false } + +# Used for data persistence +rusqlite = { version = "0.36", features = ["bundled"], default-features = false } + # For server-side scripting mlua = { version = "0.10", features = ["lua54", "vendored", "send", "async", "serialize"], default-features = false } @@ -102,6 +108,3 @@ rkon = { version = "0.1" } # For serving static files on the website tower-http = { version = "0.6", features = ["fs", "cors"] } - -# excel sheet data -icarus = { git = "https://github.com/redstrate/Icarus", branch = "ver/2025.04.16.0000.0000", features = ["Warp", "Tribe", "ClassJob", "World", "TerritoryType", "Race", "Aetheryte", "EquipSlotCategory", "Action", "WeatherRate", "PlaceName"], default-features = false } diff --git a/src/lib.rs b/src/lib.rs index ec44d71..df1a1b4 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -16,15 +16,18 @@ pub mod common; pub mod config; /// Lobby server-specific code. +#[cfg(not(target_family = "wasm"))] pub mod lobby; /// World server-specific code. +#[cfg(not(target_family = "wasm"))] pub mod world; /// Everything packet parsing related. pub mod packet; /// Logic server-specific code. +#[cfg(not(target_family = "wasm"))] pub mod login; /// Patch server-specific code. diff --git a/src/packet/mod.rs b/src/packet/mod.rs index 9a0bf3b..ba81a31 100644 --- a/src/packet/mod.rs +++ b/src/packet/mod.rs @@ -1,7 +1,7 @@ mod parsing; pub use parsing::{ ConnectionType, PacketHeader, PacketSegment, PacketState, SegmentData, SegmentType, - parse_packet, send_keep_alive, send_packet, + parse_packet, }; mod compression; @@ -15,3 +15,9 @@ pub use ipc::{IpcSegment, ReadWriteIpcSegment}; /// Bindings for Oodle network compression. pub mod oodle; + +/// Send packet helpers. +#[cfg(not(target_family = "wasm"))] +mod send_helpers; +#[cfg(not(target_family = "wasm"))] +pub use send_helpers::{send_keep_alive, send_packet}; diff --git a/src/packet/parsing.rs b/src/packet/parsing.rs index a30b136..f498c77 100644 --- a/src/packet/parsing.rs +++ b/src/packet/parsing.rs @@ -1,7 +1,6 @@ use std::{fs::write, io::Cursor}; use binrw::{BinRead, BinWrite, binrw}; -use tokio::{io::AsyncWriteExt, net::TcpStream}; use crate::{ common::{read_string, timestamp_msecs, write_string}, @@ -181,39 +180,6 @@ fn dump(msg: &str, data: &[u8]) { tracing::warn!("{msg} Dumped to packet.bin."); } -pub async fn send_packet( - socket: &mut TcpStream, - state: &mut PacketState, - connection_type: ConnectionType, - compression_type: CompressionType, - segments: &[PacketSegment], -) { - let (data, uncompressed_size) = compress(state, &compression_type, segments); - let size = std::mem::size_of::() + data.len(); - - let header = PacketHeader { - prefix: [0; 16], - timestamp: timestamp_msecs(), - size: size as u32, - connection_type, - segment_count: segments.len() as u16, - version: 0, - compression_type, - unk4: 0, - uncompressed_size: uncompressed_size as u32, - }; - - let mut cursor = Cursor::new(Vec::new()); - header.write_le(&mut cursor).unwrap(); - std::io::Write::write_all(&mut cursor, &data).unwrap(); - - let buffer = cursor.into_inner(); - - if let Err(e) = socket.write_all(&buffer).await { - tracing::warn!("Failed to send packet: {e}"); - } -} - // temporary /// State needed for each connection between the client & server, containing various things like the compressor and encryption keys. pub struct PacketState { @@ -249,28 +215,6 @@ pub fn parse_packet( } } -pub async fn send_keep_alive( - socket: &mut TcpStream, - state: &mut PacketState, - connection_type: ConnectionType, - id: u32, - timestamp: u32, -) { - let response_packet: PacketSegment = PacketSegment { - segment_type: SegmentType::KeepAliveResponse, - data: SegmentData::KeepAliveResponse { id, timestamp }, - ..Default::default() - }; - send_packet( - socket, - state, - connection_type, - CompressionType::Uncompressed, - &[response_packet], - ) - .await; -} - #[cfg(test)] mod tests { use crate::packet::IpcSegment; diff --git a/src/packet/send_helpers.rs b/src/packet/send_helpers.rs new file mode 100644 index 0000000..4082052 --- /dev/null +++ b/src/packet/send_helpers.rs @@ -0,0 +1,66 @@ +use std::io::Cursor; + +use binrw::BinWrite; +use tokio::{io::AsyncWriteExt, net::TcpStream}; + +use crate::common::timestamp_msecs; + +use super::{ + CompressionType, ConnectionType, PacketHeader, PacketSegment, PacketState, ReadWriteIpcSegment, + SegmentData, SegmentType, compression::compress, +}; + +pub async fn send_packet( + socket: &mut TcpStream, + state: &mut PacketState, + connection_type: ConnectionType, + compression_type: CompressionType, + segments: &[PacketSegment], +) { + let (data, uncompressed_size) = compress(state, &compression_type, segments); + let size = std::mem::size_of::() + data.len(); + + let header = PacketHeader { + prefix: [0; 16], + timestamp: timestamp_msecs(), + size: size as u32, + connection_type, + segment_count: segments.len() as u16, + version: 0, + compression_type, + unk4: 0, + uncompressed_size: uncompressed_size as u32, + }; + + let mut cursor = Cursor::new(Vec::new()); + header.write_le(&mut cursor).unwrap(); + std::io::Write::write_all(&mut cursor, &data).unwrap(); + + let buffer = cursor.into_inner(); + + if let Err(e) = socket.write_all(&buffer).await { + tracing::warn!("Failed to send packet: {e}"); + } +} + +pub async fn send_keep_alive( + socket: &mut TcpStream, + state: &mut PacketState, + connection_type: ConnectionType, + id: u32, + timestamp: u32, +) { + let response_packet: PacketSegment = PacketSegment { + segment_type: SegmentType::KeepAliveResponse, + data: SegmentData::KeepAliveResponse { id, timestamp }, + ..Default::default() + }; + send_packet( + socket, + state, + connection_type, + CompressionType::Uncompressed, + &[response_packet], + ) + .await; +}