1
Fork 0
mirror of https://github.com/redstrate/Kawari.git synced 2025-06-30 03:37:45 +00:00

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.
This commit is contained in:
Joshua Goins 2025-06-27 17:58:14 -04:00
parent 05f909497c
commit 7b8177b27b
5 changed files with 100 additions and 78 deletions

View file

@ -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 }

View file

@ -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.

View file

@ -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};

View file

@ -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<T: ReadWriteIpcSegment>(
socket: &mut TcpStream,
state: &mut PacketState,
connection_type: ConnectionType,
compression_type: CompressionType,
segments: &[PacketSegment<T>],
) {
let (data, uncompressed_size) = compress(state, &compression_type, segments);
let size = std::mem::size_of::<PacketHeader>() + 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<T: ReadWriteIpcSegment>(
}
}
pub async fn send_keep_alive<T: ReadWriteIpcSegment>(
socket: &mut TcpStream,
state: &mut PacketState,
connection_type: ConnectionType,
id: u32,
timestamp: u32,
) {
let response_packet: PacketSegment<T> = 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;

View file

@ -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<T: ReadWriteIpcSegment>(
socket: &mut TcpStream,
state: &mut PacketState,
connection_type: ConnectionType,
compression_type: CompressionType,
segments: &[PacketSegment<T>],
) {
let (data, uncompressed_size) = compress(state, &compression_type, segments);
let size = std::mem::size_of::<PacketHeader>() + 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<T: ReadWriteIpcSegment>(
socket: &mut TcpStream,
state: &mut PacketState,
connection_type: ConnectionType,
id: u32,
timestamp: u32,
) {
let response_packet: PacketSegment<T> = PacketSegment {
segment_type: SegmentType::KeepAliveResponse,
data: SegmentData::KeepAliveResponse { id, timestamp },
..Default::default()
};
send_packet(
socket,
state,
connection_type,
CompressionType::Uncompressed,
&[response_packet],
)
.await;
}