From 0ac751b29f3a55d958992be06e4ee8c90a5e2968 Mon Sep 17 00:00:00 2001 From: xavo95 Date: Tue, 8 Apr 2025 03:59:34 +0700 Subject: [PATCH] Hoyo and Kuro games implementations --- .gitignore | 2 + Cargo.lock | 243 ++++++++++++++++----------------------------------- Cargo.toml | 31 +++++-- src/error.rs | 6 ++ src/kcp.rs | 237 ++++++++++++++++++++++++++++++++++++++++++------- src/lib.rs | 6 +- 6 files changed, 309 insertions(+), 216 deletions(-) create mode 100644 .gitignore diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..ec376bb --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +.idea +target \ No newline at end of file diff --git a/Cargo.lock b/Cargo.lock index b80ef1b..bf8c228 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1,12 +1,12 @@ # This file is automatically @generated by Cargo. # It is not intended for manual editing. -version = 3 +version = 4 [[package]] name = "addr2line" -version = "0.24.1" +version = "0.24.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f5fb1d8e4442bd405fdfd1dacb42792696b0cf9cb15882e5d097b742a676d375" +checksum = "dfbe277e56a376000877090da837660b4427aad530e3028d44e0bffe4f89a1c1" dependencies = [ "gimli", ] @@ -32,17 +32,11 @@ dependencies = [ "windows-targets", ] -[[package]] -name = "byteorder" -version = "1.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" - [[package]] name = "bytes" -version = "1.7.2" +version = "1.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "428d9aa8fbc0670b7b8d6030a7fadd0f86151cae55e4dbbece15f3780a3dfaf3" +checksum = "d71b6127be86fdcfddb610f7182ac57211d4b18a3e9c82eb2d17662f2227ad6a" [[package]] name = "cfg-if" @@ -51,54 +45,37 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" [[package]] -name = "deranged" -version = "0.3.11" +name = "crc32fast" +version = "1.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b42b6fa04a440b495c8b04d0e71b707c585f83cb9cb28cf8cd0d976c315e31b4" -dependencies = [ - "powerfmt", -] - -[[package]] -name = "getrandom" -version = "0.2.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c4567c8db10ae91089c99af84c68c38da3ec2f087c3f82960bcdbf3656b6f4d7" +checksum = "a97769d94ddab943e4510d138150169a2758b5ef3eb191a9ee688de3e23ef7b3" dependencies = [ "cfg-if", - "libc", - "wasi", ] [[package]] name = "gimli" -version = "0.31.0" +version = "0.31.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "32085ea23f3234fc7846555e85283ba4de91e21016dc0455a16286d87a292d64" +checksum = "07e28edb80900c19c28f1072f2e8aeca7fa06b23cd4169cefe1af5aa3260783f" [[package]] name = "kcp" version = "0.1.0" dependencies = [ "bytes", - "log", - "rand", + "crc32fast", "thiserror", - "time", "tokio", + "tracing", + "xxhash-rust", ] [[package]] name = "libc" -version = "0.2.159" +version = "0.2.171" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "561d97a539a36e26a9a5fad1ea11a3039a67714694aaa379433e580854bc3dc5" - -[[package]] -name = "log" -version = "0.4.22" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a7a70ba024b9dc04c27ea2f0c0548feb474ec5c54bba33a7f72f873a39d07b24" +checksum = "c19937216e9d3aa9956d9bb8dfc0b0c8beb6058fc4f7a4dc4d850edf86a237d6" [[package]] name = "memchr" @@ -108,128 +85,63 @@ checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" [[package]] name = "miniz_oxide" -version = "0.8.0" +version = "0.8.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2d80299ef12ff69b16a84bb182e3b9df68b5a91574d3d4fa6e41b65deec4df1" +checksum = "ff70ce3e48ae43fa075863cef62e8b43b71a4f2382229920e0df362592919430" dependencies = [ "adler2", ] -[[package]] -name = "num-conv" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "51d515d32fb182ee37cda2ccdcb92950d6a3c2893aa280e540671c2cd0f3b1d9" - [[package]] name = "object" -version = "0.36.4" +version = "0.36.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "084f1a5821ac4c651660a94a7153d27ac9d8a53736203f58b31945ded098070a" +checksum = "62948e14d923ea95ea2c7c86c71013138b66525b86bdc08d2dcc262bdb497b87" dependencies = [ "memchr", ] +[[package]] +name = "once_cell" +version = "1.21.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d" + [[package]] name = "pin-project-lite" -version = "0.2.14" +version = "0.2.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bda66fc9667c18cb2758a2ac84d1167245054bcf85d5d1aaa6923f45801bdd02" - -[[package]] -name = "powerfmt" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391" - -[[package]] -name = "ppv-lite86" -version = "0.2.20" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "77957b295656769bb8ad2b6a6b09d897d94f05c41b069aede1fcdaa675eaea04" -dependencies = [ - "zerocopy", -] +checksum = "3b3cff922bd51709b605d9ead9aa71031d81447142d828eb4a6eba76fe619f9b" [[package]] name = "proc-macro2" -version = "1.0.86" +version = "1.0.94" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5e719e8df665df0d1c8fbfd238015744736151d4445ec0836b8e628aae103b77" +checksum = "a31971752e70b8b2686d7e46ec17fb38dad4051d94024c88df49b667caea9c84" dependencies = [ "unicode-ident", ] [[package]] name = "quote" -version = "1.0.37" +version = "1.0.40" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b5b9d34b8991d19d98081b46eacdd8eb58c6f2b201139f7c5f643cc155a633af" +checksum = "1885c039570dc00dcb4ff087a89e185fd56bae234ddc7f056a945bf36467248d" 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" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "719b953e2095829ee67db738b3bfa9fa368c94900df327b3f07fe6e794d2fe1f" -[[package]] -name = "serde" -version = "1.0.210" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c8e3592472072e6e22e0a54d5904d9febf8508f65fb8552499a1abc7d1078c3a" -dependencies = [ - "serde_derive", -] - -[[package]] -name = "serde_derive" -version = "1.0.210" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "243902eda00fad750862fc144cea25caca5e20d615af0a81bee94ca738f1df1f" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - [[package]] name = "syn" -version = "2.0.77" +version = "2.0.100" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9f35bcdf61fd8e7be6caf75f429fdca8beb3ed76584befb503b1569faee373ed" +checksum = "b09a44accad81e1ba1cd74a32461ba89dee89095ba17b32f5d03683b1b1fc2a0" dependencies = [ "proc-macro2", "quote", @@ -238,48 +150,29 @@ dependencies = [ [[package]] name = "thiserror" -version = "1.0.64" +version = "2.0.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d50af8abc119fb8bb6dbabcfa89656f46f84aa0ac7688088608076ad2b459a84" +checksum = "567b8a2dae586314f7be2a752ec7474332959c6460e02bde30d702a66d488708" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" -version = "1.0.64" +version = "2.0.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "08904e7672f5eb876eaaf87e0ce17857500934f4981c4a0ab2b4aa98baac7fc3" +checksum = "7f7cf42b4507d8ea322120659672cf1b9dbb93f8f2d4ecfd6e51350ff5b17a1d" dependencies = [ "proc-macro2", "quote", "syn", ] -[[package]] -name = "time" -version = "0.3.36" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5dfd88e563464686c916c7e46e623e520ddc6d79fa6641390f2e3fa86e83e885" -dependencies = [ - "deranged", - "num-conv", - "powerfmt", - "serde", - "time-core", -] - -[[package]] -name = "time-core" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ef927ca75afb808a4d64dd374f00a2adf8d0fcff8e7b184af886c3c87ec4a3f3" - [[package]] name = "tokio" -version = "1.40.0" +version = "1.44.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2b070231665d27ad9ec9b8df639893f46727666c6767db40317fbe920a5d998" +checksum = "e6b88822cbe49de4185e3a4cbf8321dd487cf5fe0c5c65695fef6346371e9c48" dependencies = [ "backtrace", "bytes", @@ -287,16 +180,41 @@ dependencies = [ ] [[package]] -name = "unicode-ident" -version = "1.0.13" +name = "tracing" +version = "0.1.41" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e91b56cd4cadaeb79bbf1a5645f6b4f8dc5bde8834ad5894a8db35fda9efa1fe" +checksum = "784e0ac535deb450455cbfa28a6f0df145ea1bb7ae51b821cf5e7927fdcfbdd0" +dependencies = [ + "pin-project-lite", + "tracing-attributes", + "tracing-core", +] [[package]] -name = "wasi" -version = "0.11.0+wasi-snapshot-preview1" +name = "tracing-attributes" +version = "0.1.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" +checksum = "395ae124c09f9e6918a2310af6038fba074bcf474ac352496d5910dd59a2226d" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "tracing-core" +version = "0.1.33" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e672c95779cf947c5311f83787af4fa8fffd12fb27e4993211a84bdfd9610f9c" +dependencies = [ + "once_cell", +] + +[[package]] +name = "unicode-ident" +version = "1.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a5f39404a5da50712a4c1eecf25e90dd62b613502b7e925fd4e4d19b5c96512" [[package]] name = "windows-targets" @@ -363,22 +281,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" [[package]] -name = "zerocopy" -version = "0.7.35" +name = "xxhash-rust" +version = "0.8.15" 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", -] +checksum = "fdd20c5420375476fbd4394763288da7eb0cc0b8c11deed431a91562af7335d3" diff --git a/Cargo.toml b/Cargo.toml index 0c5d3b0..3f6d468 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,18 +1,31 @@ [package] name = "kcp" -edition = "2021" +edition = "2024" version = "0.1.0" [features] +# General features fastack-conserve = [] tokio = ["dep:tokio"] -[dependencies] -bytes = "1.7.2" -log = "0.4.22" -thiserror = "1.0.64" -tokio = { version = "1.40.0", optional = true, features = ["io-util"] } +# Internal features +hoyo-token = [] +hoyo-crc = ["dep:xxhash-rust"] +kuro-crc = ["dep:crc32fast"] -[dev-dependencies] -time = "0.3.36" -rand = "0.8.5" \ No newline at end of file +# Game based features +genshin-impact-old = ["hoyo-token"] +genshin-impact = ["hoyo-token", "hoyo-crc"] +honkai-star-rail = ["hoyo-token"] +zenless-zone-zero = ["hoyo-token"] +wuthering-waves-old = [] +wuthering-waves = ["kuro-crc"] + + +[dependencies] +bytes = "1.10.1" +crc32fast = { version = "1.4.2", optional = true } +thiserror = "2.0.12" +tokio = { version = "1.44.2", optional = true, features = ["io-util"] } +tracing = "0.1.41" +xxhash-rust = { version = "0.8.15", optional = true, features = ["xxh3"] } \ No newline at end of file diff --git a/src/error.rs b/src/error.rs index 88c4918..4877ae4 100644 --- a/src/error.rs +++ b/src/error.rs @@ -32,6 +32,12 @@ pub enum Error { UserBufTooBig, #[error("user's recv buffer is too small")] UserBufTooSmall, + #[cfg(feature = "hoyo-token")] + #[error("token mismatch, expected {0}, found {1}")] + TokenMismatch(u32, u32), + #[cfg(any(feature = "hoyo-crc", feature = "kuro-crc"))] + #[error("crc mismatch, expected {0}, actual {1}")] + CrcMismatch(u32, u32), } fn make_io_error(kind: ErrorKind, msg: T) -> io::Error diff --git a/src/kcp.rs b/src/kcp.rs index d8c5473..f58f615 100644 --- a/src/kcp.rs +++ b/src/kcp.rs @@ -1,5 +1,7 @@ //! KCP +use bytes::{Buf, BufMut, BytesMut}; +use std::io::{Seek, SeekFrom}; #[cfg(feature = "tokio")] use std::pin::Pin; #[cfg(feature = "tokio")] @@ -11,14 +13,15 @@ use std::{ fmt::{self, Debug}, io::{self, Cursor, Read, Write}, }; - -use bytes::{Buf, BufMut, BytesMut}; #[cfg(feature = "tokio")] use tokio::io::{AsyncWrite, AsyncWriteExt}; +use tracing::{debug, trace}; + +#[cfg(feature = "hoyo-crc")] +use xxhash_rust::xxh3::xxh3_64; use crate::{error::Error, KcpResult}; - const KCP_RTO_NDL: u32 = 20; // no delay min rto const KCP_RTO_MIN: u32 = 100; // normal min rto const KCP_RTO_DEF: u32 = 200; @@ -40,7 +43,29 @@ const KCP_MTU_DEF: usize = 1400; const KCP_INTERVAL: u32 = 100; /// KCP Header size -pub const KCP_OVERHEAD: usize = 24; +pub const KCP_OVERHEAD: usize = { + #[cfg(any(feature = "hoyo-token", feature = "hoyo-crc", feature = "kuro-crc"))] + { + let mut overhead: usize = 24; // Minimal KCP Overhead + #[cfg(any(feature = "hoyo-crc", feature = "kuro-crc"))] + { + overhead += 4; + } + #[cfg(feature = "hoyo-token")] + { + overhead += 4; + } + overhead + } + #[cfg(all( + not(feature = "hoyo-token"), + not(feature = "hoyo-crc"), + not(feature = "kuro-crc") + ))] + { + 24 + } +}; const KCP_DEADLINK: u32 = 20; const KCP_THRESH_INIT: u16 = 2; @@ -57,6 +82,14 @@ pub fn get_conv(mut buf: &[u8]) -> u32 { buf.get_u32_le() } +#[cfg(feature = "hoyo-token")] +/// Read `token` from raw buffer +#[must_use] +pub fn get_token(buf: &[u8]) -> u32 { + assert!(buf.len() >= KCP_OVERHEAD); + u32::from_le_bytes(buf[4..8].try_into().unwrap()) +} + /// Set `conv` to raw buffer pub fn set_conv(mut buf: &mut [u8], conv: u32) { assert!(buf.len() >= KCP_OVERHEAD); @@ -83,6 +116,11 @@ const fn timediff(later: u32, earlier: u32) -> i32 { #[derive(Default, Clone, Debug)] struct KcpSegment { conv: u32, + #[cfg(feature = "hoyo-token")] + token: u32, + #[cfg(any(feature = "hoyo-crc", feature = "kuro-crc"))] + checksum: u32, + use_checksum: bool, cmd: u8, frg: u8, wnd: u16, @@ -97,8 +135,12 @@ struct KcpSegment { } impl KcpSegment { - fn new(data: BytesMut) -> Self { + #[allow(unused_variables)] + fn new(data: BytesMut, use_checksum: bool) -> Self { Self { + #[cfg(feature = "hoyo-crc")] + checksum: (xxh3_64(&data) & 0xFFFFFFFF) as u32, + use_checksum, data, ..Default::default() } @@ -113,6 +155,8 @@ impl KcpSegment { ); buf.put_u32_le(self.conv); + #[cfg(feature = "hoyo-token")] + buf.put_u32_le(self.token); buf.put_u8(self.cmd); buf.put_u8(self.frg); buf.put_u16_le(self.wnd); @@ -120,7 +164,14 @@ impl KcpSegment { buf.put_u32_le(self.sn); buf.put_u32_le(self.una); buf.put_u32_le(self.data.len() as u32); + #[cfg(feature = "hoyo-crc")] + buf.put_u32_le(self.checksum); buf.put_slice(&self.data); + #[cfg(feature = "kuro-crc")] + if self.use_checksum { + let crc = crc32fast::hash(buf.iter().as_ref()); + buf.put_u32_le(crc); // Check correct endianness + } } fn encoded_len(&self) -> usize { @@ -184,6 +235,13 @@ pub struct Kcp { mss: usize, /// Connection state state: i32, + #[cfg(feature = "hoyo-token")] + /// User token + token: u32, + /// Use CRC flag + use_crc: bool, + /// Conversation start timestamp + start_ts: u64, /// First unacknowledged packet snd_una: u32, @@ -265,7 +323,9 @@ pub struct Kcp { impl Debug for Kcp { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_struct("Kcp") + // Split declaration so no unused mut is triggered when hoyo-token is disabled + let mut debug_struct = f.debug_struct("Kcp"); + debug_struct .field("conv", &self.conv) .field("mtu", &self.mtu) .field("mss", &self.mss) @@ -303,8 +363,12 @@ impl Debug for Kcp { .field("fastlimit", &self.fastlimit) .field("nocwnd", &self.nocwnd) .field("stream", &self.stream) - .field("input_conv", &self.input_conv) - .finish() + .field("input_conv", &self.input_conv); + + #[cfg(feature = "hoyo-token")] + debug_struct.field("token", &self.token); + + debug_struct.finish() } } @@ -312,11 +376,29 @@ impl Kcp { /// Create a KCP control object. /// /// `conv` represents the conversation, it must be equal in both endpoints given a connection + /// `token` is the token for the conversation. /// `stream` will enable stream mode if set to `true`. /// `output` is the callback object for writing. - pub fn new(conv: u32, stream: bool, output: Output) -> Self { + #[allow(unused_variables)] + pub fn new( + conv: u32, + token: u32, + use_crc: bool, + start_ts: u64, + stream: bool, + output: Output, + ) -> Self { Self { conv, + #[cfg(feature = "hoyo-token")] + token, + #[cfg(feature = "hoyo-crc")] + use_crc: true, + #[cfg(feature = "kuro-crc")] + use_crc: use_crc, + #[cfg(all(not(feature = "hoyo-crc"), not(feature = "kuro-crc")))] + use_crc: false, + start_ts, snd_una: 0, snd_nxt: 0, rcv_nxt: 0, @@ -486,7 +568,7 @@ impl Kcp { let count = if buf.len() <= self.mss { 1 } else { - (buf.len() + self.mss - 1) / self.mss + buf.len().div_ceil(self.mss) }; if count >= KCP_WND_RCV as usize { @@ -501,7 +583,7 @@ impl Kcp { let (lf, rt) = buf.split_at(size); - let mut new_segment = KcpSegment::new(lf.into()); + let mut new_segment = KcpSegment::new(lf.into(), self.use_crc); buf = rt; new_segment.frg = if self.stream { @@ -650,6 +732,19 @@ impl Kcp { self.conv } + #[cfg(feature = "hoyo-token")] + /// Get `token` + #[must_use] + pub const fn token(&self) -> u32 { + self.token + } + + /// Get `start_ts` + #[must_use] + pub const fn start_ts(&self) -> u64 { + self.start_ts + } + /// Call this when you received a packet from raw connection pub fn input(&mut self, buf: &[u8]) -> KcpResult { let input_size = buf.len(); @@ -669,9 +764,9 @@ impl Kcp { let old_una = self.snd_una; let mut latest_ts = 0; - let mut buf = Cursor::new(buf); - while buf.remaining() >= KCP_OVERHEAD { - let conv = buf.get_u32_le(); + let mut cursor = Cursor::new(buf); + while cursor.remaining() >= KCP_OVERHEAD { + let conv = cursor.get_u32_le(); if conv != self.conv { // This allows getting conv from this call, which allows us to allocate // conv from the server side. @@ -685,20 +780,40 @@ impl Kcp { } } - let cmd = buf.get_u8(); - let frg = buf.get_u8(); - let wnd = buf.get_u16_le(); - let ts = buf.get_u32_le(); - let sn = buf.get_u32_le(); - let una = buf.get_u32_le(); - let len = buf.get_u32_le() as usize; + #[cfg(feature = "hoyo-token")] + let token = cursor.get_u32_le(); + let cmd = cursor.get_u8(); + let frg = cursor.get_u8(); + let wnd = cursor.get_u16_le(); + let ts = cursor.get_u32_le(); + let sn = cursor.get_u32_le(); + let una = cursor.get_u32_le(); + let len = cursor.get_u32_le() as usize; + #[cfg(feature = "hoyo-crc")] + let checksum = cursor.get_u32_le(); - if buf.remaining() < len { + #[cfg(not(feature = "kuro-crc"))] + let not_enough = cursor.remaining() < len; + #[cfg(feature = "kuro-crc")] + let not_enough = if self.use_crc { + cursor.remaining() < (len + 4) + } else { + cursor.remaining() < len + }; + + if not_enough { debug!( "input bufsize={input_size} payload length={len} remaining={} not match", - buf.remaining() + cursor.remaining() ); - return Err(Error::InvalidSegmentDataSize(len, buf.remaining())); + return Err(Error::InvalidSegmentDataSize(len, cursor.remaining())); + } + + #[cfg(feature = "kuro-crc")] + if self.use_crc { + if let Err(err) = self.kuro_crc_verification(&mut cursor, len as u32) { + return Err(err); + } } match cmd { @@ -709,6 +824,11 @@ impl Kcp { } } + #[cfg(feature = "hoyo-token")] + if token != self.token { + return Err(Error::TokenMismatch(token, self.token)); + } + self.rmt_wnd = wnd; self.parse_una(una); @@ -758,12 +878,23 @@ impl Kcp { unsafe { sbuf.set_len(len); } - buf.read_exact(&mut sbuf).unwrap(); + cursor.read_exact(&mut sbuf)?; + #[cfg(feature = "hoyo-crc")] + { + let expected = (xxh3_64(sbuf.iter().as_ref()) & 0xFFFFFFFF) as u32; + if expected != checksum { + return Err(Error::CrcMismatch(expected, checksum)); + } + } has_read_data = true; - let mut segment = KcpSegment::new(sbuf); + let mut segment = KcpSegment::new(sbuf, self.use_crc); segment.conv = conv; + #[cfg(feature = "hoyo-token")] + { + segment.token = token + }; segment.cmd = cmd; segment.frg = frg; segment.wnd = wnd; @@ -790,8 +921,14 @@ impl Kcp { // Force skip unread data if !has_read_data { - let next_pos = buf.position() + len as u64; - buf.set_position(next_pos); + #[cfg(feature = "kuro-crc")] + if self.use_crc { + cursor.seek(SeekFrom::Current(len as i64 + 4))?; + } else { + cursor.seek(SeekFrom::Current(len as i64))?; + } + #[cfg(not(feature = "kuro-crc"))] + cursor.seek(SeekFrom::Current(len as i64))?; } } @@ -820,7 +957,27 @@ impl Kcp { } } - Ok(buf.position() as usize) + Ok(cursor.position() as usize) + } + + #[cfg(feature = "kuro-crc")] + fn kuro_crc_verification(&self, cursor: &mut Cursor<&[u8]>, len: u32) -> KcpResult<()> { + let backup = cursor.position(); + cursor.seek(SeekFrom::Current(-(KCP_OVERHEAD as i64 - 4)))?; + let full_payload_length = KCP_OVERHEAD + len as usize - 4; + let mut sbuf = BytesMut::with_capacity(full_payload_length); + unsafe { + sbuf.set_len(full_payload_length); + } + cursor.read_exact(&mut sbuf)?; + let checksum = cursor.get_u32_le(); + cursor.set_position(backup); + let expected = crc32fast::hash(&sbuf).to_be(); + if checksum == expected { + Ok(()) + } else { + Err(Error::CrcMismatch(expected, checksum)) + } } #[must_use] @@ -868,7 +1025,7 @@ impl Kcp { } let mut ts_flush = self.ts_flush; - let mut tm_packet = u32::max_value(); + let mut tm_packet = u32::MAX; if timediff(current, ts_flush) >= 10000 || timediff(current, ts_flush) < -10000 { ts_flush = current; @@ -1084,6 +1241,8 @@ impl Kcp { let mut segment = KcpSegment { conv: self.conv, + #[cfg(feature = "hoyo-token")] + token: self.token, cmd: KCP_CMD_ACK, wnd: self.wnd_unused(), una: self.rcv_nxt, @@ -1102,6 +1261,8 @@ impl Kcp { let mut segment = KcpSegment { conv: self.conv, + #[cfg(feature = "hoyo-token")] + token: self.token, cmd: KCP_CMD_ACK, wnd: self.wnd_unused(), una: self.rcv_nxt, @@ -1123,6 +1284,10 @@ impl Kcp { match self.snd_queue.pop_front() { Some(mut new_segment) => { new_segment.conv = self.conv; + #[cfg(feature = "hoyo-token")] + { + new_segment.token = self.token; + } new_segment.cmd = KCP_CMD_PUSH; new_segment.wnd = segment.wnd; new_segment.ts = self.current; @@ -1143,7 +1308,7 @@ impl Kcp { let resent = if self.fastresend > 0 { self.fastresend } else { - u32::max_value() + u32::MAX }; let rtomin = if !self.nodelay { self.rx_rto >> 3 } else { 0 }; @@ -1323,6 +1488,8 @@ impl Kcp { let mut segment = KcpSegment { conv: self.conv, + #[cfg(feature = "hoyo-token")] + token: self.token, cmd: KCP_CMD_ACK, wnd: self.wnd_unused(), una: self.rcv_nxt, @@ -1341,6 +1508,8 @@ impl Kcp { let mut segment = KcpSegment { conv: self.conv, + #[cfg(feature = "hoyo-token")] + token: self.token, cmd: KCP_CMD_ACK, wnd: self.wnd_unused(), una: self.rcv_nxt, @@ -1362,6 +1531,8 @@ impl Kcp { match self.snd_queue.pop_front() { Some(mut new_segment) => { new_segment.conv = self.conv; + #[cfg(feature = "hoyo-token")] + new_segment.token = self.token; new_segment.cmd = KCP_CMD_PUSH; new_segment.wnd = segment.wnd; new_segment.ts = self.current; @@ -1382,7 +1553,7 @@ impl Kcp { let resent = if self.fastresend > 0 { self.fastresend } else { - u32::max_value() + u32::MAX }; let rtomin = if !self.nodelay { self.rx_rto >> 3 } else { 0 }; @@ -1502,4 +1673,4 @@ impl Kcp { Ok(()) } -} \ No newline at end of file +} diff --git a/src/lib.rs b/src/lib.rs index 01ccfa5..e3132ce 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,7 +1,3 @@ -extern crate bytes; -#[macro_use] -extern crate log; - mod error; mod kcp; @@ -12,6 +8,8 @@ pub mod prelude { pub use error::Error; pub use kcp::{get_conv, get_sn, set_conv, Kcp, KCP_OVERHEAD}; +#[cfg(feature = "hoyo-token")] +pub use kcp::get_token; /// KCP result pub type KcpResult = Result; \ No newline at end of file