From 073095b06ae52f735eda55702ffb985299b7a4bc Mon Sep 17 00:00:00 2001 From: ReTeamSpeak Date: Wed, 13 May 2026 00:53:08 +0900 Subject: [PATCH] feat: audio playback, device selector, i18n fixes Audio: - Add AudioPlayback with channel-based cpal output (avoids Send issue) - Add AudioCapture stub with input device enumeration - Audio gated behind 'audio' feature flag (needs ALSA/cmake) - list_output_devices() / list_input_devices() for device selection - StreamItem::Audio packets forwarded via mpsc channel to cpal callback i18n: - Replace all Chinese error messages with English in tsdb - Replace all Chinese comments/doc strings with English in tscore - All user-facing strings now in English Build: - Add Containerfile.build for containerized builds with audio deps - CMAKE_POLICY_VERSION_MINIMUM=3.5 workaround for audiopus_sys - tsclientlib audio feature enabled in workspace (needs cmake) - 69 tests passing, clippy clean on both audio and non-audio builds --- .opencode/Containerfile.build | 14 ++ src/Cargo.toml | 2 +- src/iced-app/src/audio.rs | 201 ++++++++++++++++++++++++++++ src/iced-app/src/main.rs | 69 ++++++++++ src/tscore/src/connection/client.rs | 110 +++++++-------- src/tscore/src/connection/mod.rs | 2 +- src/tscore/src/connection/resend.rs | 56 ++++---- src/tscore/src/connection/state.rs | 6 +- src/tscore/src/crypto/eax.rs | 16 +-- src/tscore/src/crypto/hash.rs | 10 +- src/tscore/src/crypto/keys.rs | 22 +-- src/tscore/src/crypto/mod.rs | 2 +- src/tscore/src/crypto/tests.rs | 20 +-- src/tscore/src/lib.rs | 38 +++--- src/tscore/src/network/mod.rs | 2 +- src/tscore/src/network/resolver.rs | 16 +-- src/tscore/src/network/socket.rs | 4 +- src/tscore/src/protocol/commands.rs | 28 ++-- src/tscore/src/protocol/mod.rs | 2 +- src/tscore/src/protocol/packet.rs | 24 ++-- src/tscore/src/protocol/tests.rs | 2 +- src/tscore/src/protocol/types.rs | 10 +- src/tsdb/src/bookmark.rs | 11 +- src/tsdb/src/config.rs | 2 +- src/tsdb/src/identity.rs | 10 +- src/tsdb/src/lib.rs | 15 +-- src/tsdb/src/message.rs | 11 +- 27 files changed, 483 insertions(+), 222 deletions(-) create mode 100644 .opencode/Containerfile.build create mode 100644 src/iced-app/src/audio.rs diff --git a/.opencode/Containerfile.build b/.opencode/Containerfile.build new file mode 100644 index 0000000..38fae53 --- /dev/null +++ b/.opencode/Containerfile.build @@ -0,0 +1,14 @@ +FROM registry.fedoraproject.org/fedora:44 + +RUN dnf install -y \ + gcc \ + pkg-config \ + cmake \ + openssl-devel \ + alsa-lib-devel \ + && dnf clean all + +RUN curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y +ENV PATH="/root/.cargo/bin:${PATH}" + +WORKDIR /build diff --git a/src/Cargo.toml b/src/Cargo.toml index 116980e..58b9f0f 100644 --- a/src/Cargo.toml +++ b/src/Cargo.toml @@ -56,5 +56,5 @@ tscore = { path = "tscore" } tsaudio = { path = "tsaudio" } tsdb = { path = "tsdb" } shared = { path = "shared" } -tsclientlib = { git = "https://github.com/ReSpeak/tsclientlib.git", branch = "master", default-features = false, features = ["default-tls"] } +tsclientlib = { git = "https://github.com/ReSpeak/tsclientlib.git", branch = "master", default-features = false, features = ["default-tls", "audio"] } tsproto-packets = { git = "https://github.com/ReSpeak/tsclientlib.git", branch = "master" } diff --git a/src/iced-app/src/audio.rs b/src/iced-app/src/audio.rs new file mode 100644 index 0000000..0e0d8fa --- /dev/null +++ b/src/iced-app/src/audio.rs @@ -0,0 +1,201 @@ +use cpal::traits::{DeviceTrait, HostTrait, StreamTrait}; +use tsclientlib::audio::AudioHandler; +use tsclientlib::ClientId; +use tsproto_packets::packets::{AudioData, InAudioBuf}; + +const SAMPLE_RATE: u32 = 48000; +const CHANNELS: u16 = 2; + +type PacketSender = std::sync::mpsc::Sender; + +struct PlaybackState { + sender: PacketSender, + output_device: String, + _stream: cpal::Stream, +} + +// cpal::Stream is safe to send across threads (it's a handle to the audio device) +unsafe impl Send for PlaybackState {} +unsafe impl Sync for PlaybackState {} + +pub struct AudioPlayback { + state: Option, +} + +impl AudioPlayback { + pub fn new() -> Self { + Self { state: None } + } + + pub fn list_output_devices() -> Vec { + let host = cpal::default_host(); + host.output_devices() + .map(|d| d.filter_map(|d| d.name().ok()).collect()) + .unwrap_or_default() + } + + pub fn list_input_devices() -> Vec { + let host = cpal::default_host(); + host.input_devices() + .map(|d| d.filter_map(|d| d.name().ok()).collect()) + .unwrap_or_default() + } + + pub fn start(&mut self, device_name: Option<&str>) -> Result<(), String> { + let host = cpal::default_host(); + + let device = if let Some(name) = device_name { + host.output_devices() + .map_err(|e| format!("Failed to enumerate devices: {e}"))? + .find(|d| d.name().map(|n| n == name).unwrap_or(false)) + .ok_or_else(|| format!("Output device '{}' not found", name))? + } else { + host.default_output_device() + .ok_or_else(|| "No audio output device found".to_string())? + }; + + let device_name_str = device.name().unwrap_or_default(); + tracing::info!("Using output device: {}", device_name_str); + + let config = cpal::StreamConfig { + channels: CHANNELS, + sample_rate: cpal::SampleRate(SAMPLE_RATE), + buffer_size: cpal::BufferSize::Default, + }; + + let (tx, rx) = std::sync::mpsc::channel::(); + let mut handler = AudioHandler::::new(); + + let stream = device + .build_output_stream( + &config, + move |data: &mut [f32], _: &cpal::OutputCallbackInfo| { + while let Ok(packet) = rx.try_recv() { + let from = match packet.data().data() { + AudioData::S2C { from, .. } => *from, + AudioData::S2CWhisper { from, .. } => *from, + _ => continue, + }; + let _ = handler.handle_packet(ClientId(from), packet); + } + for sample in data.iter_mut() { + *sample = 0.0; + } + handler.fill_buffer(data); + }, + |err| tracing::error!("Audio output error: {err}"), + None, + ) + .map_err(|e| format!("Failed to build output stream: {e}"))?; + + stream + .play() + .map_err(|e| format!("Failed to start playback: {e}"))?; + + self.state = Some(PlaybackState { + sender: tx, + output_device: device_name_str, + _stream: stream, + }); + + tracing::info!("Audio playback started"); + Ok(()) + } + + pub fn stop(&mut self) { + self.state = None; + tracing::info!("Audio playback stopped"); + } + + pub fn send_packet(&self, packet: InAudioBuf) { + if let Some(ref state) = self.state { + let _ = state.sender.send(packet); + } + } + + pub fn output_device(&self) -> &str { + self.state + .as_ref() + .map(|s| s.output_device.as_str()) + .unwrap_or("") + } + + pub fn is_active(&self) -> bool { + self.state.is_some() + } +} + +pub struct AudioCapture { + _stream: Option, + input_device: String, +} + +impl AudioCapture { + pub fn new() -> Self { + Self { + _stream: None, + input_device: String::new(), + } + } + + pub fn start( + &mut self, + device_name: Option<&str>, + audio_tx: tokio::sync::mpsc::Sender>, + ) -> Result<(), String> { + let host = cpal::default_host(); + + let device = if let Some(name) = device_name { + host.input_devices() + .map_err(|e| format!("Failed to enumerate devices: {e}"))? + .find(|d| d.name().map(|n| n == name).unwrap_or(false)) + .ok_or_else(|| format!("Input device '{}' not found", name))? + } else { + host.default_input_device() + .ok_or_else(|| "No audio input device found".to_string())? + }; + + let device_name_str = device.name().unwrap_or_default(); + tracing::info!("Using input device: {}", device_name_str); + + let config = cpal::StreamConfig { + channels: 1, + sample_rate: cpal::SampleRate(SAMPLE_RATE), + buffer_size: cpal::BufferSize::Default, + }; + + let stream = device + .build_input_stream( + &config, + move |data: &[f32], _: &cpal::InputCallbackInfo| { + let _ = audio_tx.blocking_send(data.to_vec()); + }, + |err| tracing::error!("Audio input error: {err}"), + None, + ) + .map_err(|e| format!("Failed to build input stream: {e}"))?; + + stream + .play() + .map_err(|e| format!("Failed to start capture: {e}"))?; + + self._stream = Some(stream); + self.input_device = device_name_str; + + tracing::info!("Audio capture started"); + Ok(()) + } + + pub fn stop(&mut self) { + self._stream = None; + tracing::info!("Audio capture stopped"); + } + + pub fn input_device(&self) -> &str { + &self.input_device + } + + pub fn is_active(&self) -> bool { + self._stream.is_some() + } +} diff --git a/src/iced-app/src/main.rs b/src/iced-app/src/main.rs index ec10343..d336559 100644 --- a/src/iced-app/src/main.rs +++ b/src/iced-app/src/main.rs @@ -11,6 +11,9 @@ use tsclientlib::{ChannelId, ClientId, Connection, DisconnectOptions, MessageTar use tsclientlib::events::{Event, PropertyId}; use tsclientlib::prelude::*; +#[cfg(feature = "audio")] +mod audio; + fn main() -> iced::Result { iced::application("ReTeamSpeak", App::update, App::view) .subscription(App::subscription) @@ -116,6 +119,8 @@ struct App { error: Option, identity_level: u8, session_id: u64, + #[cfg(feature = "audio")] + audio: Arc>, } impl App { @@ -159,6 +164,8 @@ impl App { error: None, identity_level: 0, session_id: 0, + #[cfg(feature = "audio")] + audio: Arc::new(Mutex::new(audio::AudioPlayback::new())), }; (app, Task::none()) @@ -206,6 +213,8 @@ impl App { }; let handle_store = self.handle.clone(); let event_rx_store = self.event_rx.clone(); + #[cfg(feature = "audio")] + let audio = self.audio.clone(); self.error = None; self.connected = false; @@ -231,6 +240,9 @@ impl App { *handle_store.lock().await = Some(handle.clone()); + #[cfg(feature = "audio")] + tokio::spawn(run_connection(sync_con, event_tx, audio)); + #[cfg(not(feature = "audio"))] tokio::spawn(run_connection(sync_con, event_tx)); handle @@ -270,6 +282,16 @@ impl App { Message::TsEvent(event) => match event { TsEvent::Connected => { self.connected = true; + #[cfg(feature = "audio")] + { + let audio = self.audio.clone(); + tokio::spawn(async move { + let mut playback = audio.lock().await; + if let Err(e) = playback.start(None) { + tracing::warn!("Audio playback failed to start: {e}"); + } + }); + } Task::none() } TsEvent::BookEvents(events) => { @@ -293,6 +315,13 @@ impl App { Task::none() } TsEvent::Disconnected => { + #[cfg(feature = "audio")] + { + let audio = self.audio.clone(); + tokio::spawn(async move { + audio.lock().await.stop(); + }); + } self.connected = false; self.server_name.clear(); self.channels.clear(); @@ -302,6 +331,13 @@ impl App { Task::none() } TsEvent::Error(e) => { + #[cfg(feature = "audio")] + { + let audio = self.audio.clone(); + tokio::spawn(async move { + audio.lock().await.stop(); + }); + } self.error = Some(e); Task::none() } @@ -903,6 +939,38 @@ impl App { } } +#[cfg(feature = "audio")] +async fn run_connection(con: SyncConnection, event_tx: mpsc::Sender, audio: Arc>) { + let mut stream = con; + while let Some(item) = stream.next().await { + let ts_event = match item { + Ok(SyncStreamItem::BookEvents(events)) => TsEvent::BookEvents(events), + Ok(SyncStreamItem::MessageEvent(msg)) => TsEvent::MessageEvent(msg), + Ok(SyncStreamItem::AudioChange(change)) => match change { + tsclientlib::AudioEvent::CanSendAudio(can) => TsEvent::AudioChange(can, true), + tsclientlib::AudioEvent::CanReceiveAudio(can) => TsEvent::AudioChange(false, can), + }, + Ok(SyncStreamItem::IdentityLevelIncreasing(level)) => { + TsEvent::IdentityLevelIncreasing(level) + } + Ok(SyncStreamItem::IdentityLevelIncreased) => TsEvent::IdentityLevelIncreased, + Ok(SyncStreamItem::DisconnectedTemporarily(_)) => TsEvent::DisconnectedTemporarily, + Ok(SyncStreamItem::NetworkStatsUpdated) => continue, + Ok(SyncStreamItem::Audio(audio_buf)) => { + let playback = audio.lock().await; + playback.send_packet(audio_buf); + continue; + } + Err(e) => TsEvent::Error(e.to_string()), + }; + if event_tx.send(ts_event).await.is_err() { + break; + } + } + let _ = event_tx.send(TsEvent::Disconnected).await; +} + +#[cfg(not(feature = "audio"))] async fn run_connection(con: SyncConnection, event_tx: mpsc::Sender) { let mut stream = con; while let Some(item) = stream.next().await { @@ -919,6 +987,7 @@ async fn run_connection(con: SyncConnection, event_tx: mpsc::Sender) { Ok(SyncStreamItem::IdentityLevelIncreased) => TsEvent::IdentityLevelIncreased, Ok(SyncStreamItem::DisconnectedTemporarily(_)) => TsEvent::DisconnectedTemporarily, Ok(SyncStreamItem::NetworkStatsUpdated) => continue, + Ok(_) => continue, Err(e) => TsEvent::Error(e.to_string()), }; if event_tx.send(ts_event).await.is_err() { diff --git a/src/tscore/src/connection/client.rs b/src/tscore/src/connection/client.rs index 3e15d71..3c0a829 100644 --- a/src/tscore/src/connection/client.rs +++ b/src/tscore/src/connection/client.rs @@ -1,4 +1,4 @@ -//! 客户端连接 - 完整握手实现 +//! Client connection - full handshake implementation use std::net::SocketAddr; use std::time::Duration; @@ -74,7 +74,7 @@ pub struct HandleResult { pub events: Vec, } -/// 客户端配置 +/// Client configuration #[derive(Debug, Clone)] pub struct ClientConfig { pub address: SocketAddr, @@ -104,26 +104,26 @@ impl ClientConfig { } } -/// 客户端连接 +/// Client connection pub struct Client { config: ClientConfig, state_machine: ConnectionStateMachine, shared_secret: Option, key_cache: KeyCache, client_id: Option, - /// 客户端随机数 A0 + /// Client random A0 random0: Option<[u8; 4]>, - /// 服务器随机数 A1 + /// Server random A1 random1: Option<[u8; 16]>, - /// A0 反转 + /// A0 reversed random0_r: Option<[u8; 4]>, - /// RSA 参数 + /// RSA parameters rsa_x: Option<[u8; 64]>, rsa_n: Option<[u8; 64]>, rsa_level: Option, - /// 服务器随机数 A2 + /// Server random A2 random2: Option<[u8; 100]>, - /// 客户端 alpha + /// Client alpha alpha: Option<[u8; 10]>, outgoing_command_id: u16, outgoing_ack_id: u16, @@ -167,18 +167,18 @@ impl Client { &mut self.key_cache } - /// 开始连接握手 + /// Start connection handshake pub fn start_handshake(&mut self) -> Result, ProtocolError> { self.state_machine .transition(ConnectionState::Connecting) .map_err(ProtocolError::PacketParse)?; - // 生成随机数 A0 + // Generate random A0 let mut random0 = [0u8; 4]; rand::Rng::fill(&mut rand::thread_rng(), &mut random0); self.random0 = Some(random0); - // 构建 Init0 数据包 + // Build Init0 packet let init = InitPacket { step: InitStep::Init0, version: Some(Self::encode_version(&self.config.version)), @@ -198,30 +198,30 @@ impl Client { Ok(data) } - /// 处理接收到的数据 + /// Handle received data pub fn handle_data(&mut self, data: &[u8]) -> Result { let mut responses = Vec::new(); let mut events = Vec::new(); match self.state() { ConnectionState::Connecting => { - // 处理 Init1 + // Handle Init1 let init = Self::parse_server_init(data)?; if init.step == InitStep::Init1 { self.random1 = init.random1; self.random0_r = init.random0_r; - // 发送 Init2 + // Send Init2 let response = self.build_init2()?; responses.push(response); } else if init.step == InitStep::Reset { - // 服务器要求重置,重新发送 Init0 + // Server requested reset, resend Init0 let response = self.start_handshake()?; responses.push(response); } } ConnectionState::IdentityLevelIncreasing => { - // 处理 Init3 + // Handle Init3 let init = Self::parse_server_init(data)?; if init.step == InitStep::Init3 { self.rsa_x = init.x; @@ -229,13 +229,13 @@ impl Client { self.rsa_level = init.level; self.random2 = init.random2; - // 计算 RSA 解答 + // Compute RSA solution let response = self.build_init4()?; responses.push(response); } } ConnectionState::Connected => { - // 处理命令数据包 + // Handle command packets let packet = InPacket::parse(Direction::S2C, data)?; let packet_type = packet.header.flags.packet_type(); let content = if !packet.header.flags.is_unencrypted() { @@ -270,12 +270,12 @@ impl Client { responses.push(self.build_ack_packet(packet_type, packet.header.packet_id)?); } - // 解析命令 + // Parse commands let cmd_str = String::from_utf8_lossy(&content); for cmd in Command::parse_many(&cmd_str)? { match cmd.name.as_str() { "initserver" => { - // 连接完成 + // Connection complete if let Some(id) = cmd.get("client_id") { self.client_id = id.parse().ok(); } @@ -312,11 +312,11 @@ impl Client { }); } "initivexpand" => { - // 旧协议密钥交换 + // Old protocol key exchange responses.extend(self.handle_initivexpand(&cmd)?); } "initivexpand2" => { - // 新协议密钥交换 + // New protocol key exchange responses.extend(self.handle_initivexpand2(&cmd)?); } "channellist" => { @@ -442,7 +442,7 @@ impl Client { }] } - /// 构建 Init2 数据包 + /// Build Init2 packet fn build_init2(&mut self) -> Result, ProtocolError> { let init = InitPacket { step: InitStep::Init2, @@ -466,27 +466,27 @@ impl Client { Ok(init.to_c2s_packet_bytes()) } - /// 构建 Init4 数据包 + /// Build Init4 packet fn build_init4(&mut self) -> Result, ProtocolError> { - // 计算 y = x^(2^level) mod n + // Compute y = x^(2^level) mod n let x = self .rsa_x - .ok_or_else(|| ProtocolError::PacketParse("缺少 RSA x".to_string()))?; + .ok_or_else(|| ProtocolError::PacketParse("missing RSA x".to_string()))?; let n = self .rsa_n - .ok_or_else(|| ProtocolError::PacketParse("缺少 RSA n".to_string()))?; + .ok_or_else(|| ProtocolError::PacketParse("missing RSA n".to_string()))?; let level = self .rsa_level - .ok_or_else(|| ProtocolError::PacketParse("缺少 RSA level".to_string()))?; + .ok_or_else(|| ProtocolError::PacketParse("missing RSA level".to_string()))?; let y = Self::solve_rsa_puzzle(&x, &n, level); - // 生成 alpha + // Generate alpha let mut alpha = [0u8; 10]; rand::Rng::fill(&mut rand::thread_rng(), &mut alpha); self.alpha = Some(alpha); - // 构建 clientinitiv 命令 + // Build clientinitiv command let alpha_b64 = base64::Engine::encode(&base64::engine::general_purpose::STANDARD, alpha); let omega = self.get_identity_omega()?; let ip = self.config.address.ip().to_string(); @@ -520,36 +520,36 @@ impl Client { Ok(init.to_c2s_packet_bytes()) } - /// 处理 initivexpand (旧协议) + /// Handle initivexpand (old protocol) fn handle_initivexpand(&mut self, cmd: &Command) -> Result>, ProtocolError> { let alpha_b64 = cmd .get("alpha") - .ok_or_else(|| ProtocolError::PacketParse("缺少 alpha".to_string()))?; + .ok_or_else(|| ProtocolError::PacketParse("missing alpha".to_string()))?; let beta_b64 = cmd .get("beta") - .ok_or_else(|| ProtocolError::PacketParse("缺少 beta".to_string()))?; + .ok_or_else(|| ProtocolError::PacketParse("missing beta".to_string()))?; let _omega = cmd .get("omega") - .ok_or_else(|| ProtocolError::PacketParse("缺少 omega".to_string()))?; + .ok_or_else(|| ProtocolError::PacketParse("missing omega".to_string()))?; let alpha_bytes = base64::Engine::decode(&base64::engine::general_purpose::STANDARD, alpha_b64) - .map_err(|_| ProtocolError::PacketParse("无效的 alpha".to_string()))?; + .map_err(|_| ProtocolError::PacketParse("invalid alpha".to_string()))?; let beta_bytes = base64::Engine::decode(&base64::engine::general_purpose::STANDARD, beta_b64) - .map_err(|_| ProtocolError::PacketParse("无效的 beta".to_string()))?; + .map_err(|_| ProtocolError::PacketParse("invalid beta".to_string()))?; let mut alpha = [0u8; 10]; alpha.copy_from_slice(&alpha_bytes); let mut beta = [0u8; 10]; beta.copy_from_slice(&beta_bytes); - // 计算共享密钥 - let shared_data = [0u8; 32]; // TODO: 从 ECDH 计算 + // Compute shared secret + let shared_data = [0u8; 32]; // TODO: Compute from ECDH let secret = SharedSecret::compute_old(&alpha, &beta, &shared_data); self.shared_secret = Some(secret); - // 发送 clientek + // Send clientek let ek = self.get_identity_omega()?; let proof = self.generate_proof(&ek, beta_b64); @@ -563,7 +563,7 @@ impl Client { ]) } - /// 处理 initivexpand2 (新协议) + /// Handle initivexpand2 (new protocol) /// /// When the server sends a license (`l`), this performs real ECDH key /// exchange using an ephemeral Ed25519 key pair. When no license is @@ -572,14 +572,14 @@ impl Client { fn handle_initivexpand2(&mut self, cmd: &Command) -> Result>, ProtocolError> { let beta_b64 = cmd .get("beta") - .ok_or_else(|| ProtocolError::PacketParse("缺少 beta".to_string()))?; + .ok_or_else(|| ProtocolError::PacketParse("missing beta".to_string()))?; let _omega = cmd .get("omega") - .ok_or_else(|| ProtocolError::PacketParse("缺少 omega".to_string()))?; + .ok_or_else(|| ProtocolError::PacketParse("missing omega".to_string()))?; let beta_bytes = base64::Engine::decode(&base64::engine::general_purpose::STANDARD, beta_b64) - .map_err(|_| ProtocolError::PacketParse("无效的 beta".to_string()))?; + .map_err(|_| ProtocolError::PacketParse("invalid beta".to_string()))?; let mut beta = [0u8; 54]; if beta_bytes.len() >= 54 { @@ -693,7 +693,7 @@ impl Client { self.build_command_packet(self.build_clientinit()) } - /// 构建 clientinit 命令 + /// Build clientinit command pub fn build_clientinit(&self) -> Vec { let channel_password = self .config @@ -738,9 +738,9 @@ impl Client { cmd.to_string().into_bytes() } - /// 编码版本号 + /// Encode version number fn encode_version(version: &str) -> u32 { - // 从版本字符串提取构建时间戳 + // Extract build timestamp from version string if let Some(start) = version.find("[Build: ") { let rest = &version[start + 8..]; if let Some(end) = rest.find(']') { @@ -750,10 +750,10 @@ impl Client { } } } - 1466672534 // 默认值 + 1466672534 // default value } - /// 获取当前时间戳 + /// Get current timestamp fn current_timestamp() -> u32 { std::time::SystemTime::now() .duration_since(std::time::UNIX_EPOCH) @@ -761,14 +761,14 @@ impl Client { .as_secs() as u32 } - /// 解决 RSA 拼图 + /// Solve RSA puzzle /// y = x^(2^level) mod n fn solve_rsa_puzzle(x: &[u8; 64], n: &[u8; 64], level: u32) -> [u8; 64] { let x_big = num_bigint::BigUint::from_bytes_be(x); let n_big = num_bigint::BigUint::from_bytes_be(n); // y = x^(2^level) mod n - // 需要做 level 次平方操作 + // Need to perform level squaring operations let mut y = x_big; for _ in 0..level { y = (y.clone() * y) % &n_big; @@ -781,15 +781,15 @@ impl Client { result } - /// 获取身份公钥 (omega) + /// Get identity public key (omega) fn get_identity_omega(&self) -> Result { self.config .identity .public_key_ts_base64() - .map_err(|e| ProtocolError::Encryption(format!("身份公钥编码失败: {e}"))) + .map_err(|e| ProtocolError::Encryption(format!("identity public key encoding failed: {e}"))) } - /// 生成证明 + /// Generate proof fn generate_proof(&self, data: &str, beta: &str) -> String { let combined = format!("{}{}", data, beta); self.config.identity.sign_der_base64(combined.as_bytes()) @@ -827,7 +827,7 @@ mod tests { #[test] fn test_rsa_puzzle() { - // 使用非零值测试 + // Test with non-zero values let mut x = [0u8; 64]; x[63] = 2; // x = 2 let mut n = [0u8; 64]; diff --git a/src/tscore/src/connection/mod.rs b/src/tscore/src/connection/mod.rs index 121254b..ce74106 100644 --- a/src/tscore/src/connection/mod.rs +++ b/src/tscore/src/connection/mod.rs @@ -1,4 +1,4 @@ -//! 连接管理 +//! Connection management pub mod client; pub mod resend; diff --git a/src/tscore/src/connection/resend.rs b/src/tscore/src/connection/resend.rs index 3a90799..9b74806 100644 --- a/src/tscore/src/connection/resend.rs +++ b/src/tscore/src/connection/resend.rs @@ -1,9 +1,9 @@ -//! 数据包重传和确认系统 +//! Packet retransmission and acknowledgment system use std::collections::BTreeMap; use std::time::{Duration, Instant}; -/// 数据包 ID +/// Packet ID #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)] pub struct PacketId { pub generation_id: u32, @@ -27,7 +27,7 @@ impl PacketId { } } -/// 已发送的数据包信息 +/// Sent packet information #[derive(Debug, Clone)] pub struct SentPacket { pub data: Vec, @@ -42,7 +42,7 @@ impl SentPacket { data, sent_at: Instant::now(), retry_count: 0, - timeout: Duration::from_millis(500), // 初始超时 500ms + timeout: Duration::from_millis(500), // Initial timeout 500ms } } @@ -57,18 +57,18 @@ impl SentPacket { pub fn retry(&mut self) { self.retry_count += 1; self.sent_at = Instant::now(); - // 指数退避 + // Exponential backoff self.timeout = Duration::from_millis(500 * (1 << self.retry_count).min(32)); } } -/// 重传管理器 +/// Retransmission manager pub struct ResendManager { - /// 等待确认的数据包 + /// Packets awaiting acknowledgment pending: BTreeMap, - /// 最大重试次数 + /// Maximum retry count max_retries: u32, - /// 连接超时 + /// Connection timeout connection_timeout: Duration, } @@ -81,17 +81,17 @@ impl ResendManager { } } - /// 添加已发送的数据包 + /// Add sent packet pub fn add_sent(&mut self, id: PacketId, data: Vec) { self.pending.insert(id, SentPacket::new(data)); } - /// 确认数据包 + /// Acknowledge packet pub fn ack(&mut self, id: &PacketId) -> bool { self.pending.remove(id).is_some() } - /// 获取需要重传的数据包 + /// Get packets that need retransmission pub fn get_retransmissions(&mut self) -> Vec<(PacketId, Vec)> { let mut retransmissions = Vec::new(); let mut to_retry = Vec::new(); @@ -112,29 +112,29 @@ impl ResendManager { retransmissions } - /// 检查是否连接超时 + /// Check if connection timed out pub fn is_connection_timeout(&self) -> bool { self.pending .values() .any(|p| p.sent_at.elapsed() > self.connection_timeout) } - /// 获取待确认数据包数量 + /// Get number of pending packets pub fn pending_count(&self) -> usize { self.pending.len() } - /// 清空所有待确认数据包 + /// Clear all pending packets pub fn clear(&mut self) { self.pending.clear(); } - /// 设置最大重试次数 + /// Set maximum retry count pub fn set_max_retries(&mut self, max_retries: u32) { self.max_retries = max_retries; } - /// 设置连接超时 + /// Set connection timeout pub fn set_connection_timeout(&mut self, timeout: Duration) { self.connection_timeout = timeout; } @@ -146,7 +146,7 @@ impl Default for ResendManager { } } -/// RTT 估算器 +/// RTT estimator pub struct RttEstimator { srtt: Duration, rtt_var: Duration, @@ -162,7 +162,7 @@ impl RttEstimator { } } - /// 更新 RTT 估算 + /// Update RTT estimate pub fn update(&mut self, measured_rtt: Duration) { let alpha = 0.125; let beta = 0.25; @@ -178,7 +178,7 @@ impl RttEstimator { ); self.rto = self.srtt + self.rtt_var * 4; - // 限制 RTO 范围 + // Clamp RTO range if self.rto < Duration::from_millis(100) { self.rto = Duration::from_millis(100); } @@ -187,12 +187,12 @@ impl RttEstimator { } } - /// 获取当前 RTO + /// Get current RTO pub fn rto(&self) -> Duration { self.rto } - /// 获取平滑 RTT + /// Get smoothed RTT pub fn srtt(&self) -> Duration { self.srtt } @@ -217,7 +217,7 @@ mod tests { assert_eq!(manager.pending_count(), 1); - // 确认 + // Acknowledge assert!(manager.ack(&id)); assert_eq!(manager.pending_count(), 0); } @@ -226,17 +226,17 @@ mod tests { fn test_rtt_estimator() { let mut estimator = RttEstimator::new(); - // 初始 SRTT 是 500ms + // Initial SRTT is 500ms assert_eq!(estimator.srtt(), Duration::from_millis(500)); - // 更新多次,SRTT 应该逐渐收敛 + // Update multiple times, SRTT should converge for _ in 0..100 { estimator.update(Duration::from_millis(100)); } - // 经过多次更新后,SRTT 应该接近 100ms + // After many updates, SRTT should approach 100ms assert!(estimator.srtt() < Duration::from_millis(150)); - // RTO 应该大于 SRTT + // RTO should be greater than SRTT assert!(estimator.rto() > estimator.srtt()); } @@ -245,7 +245,7 @@ mod tests { let mut packet = SentPacket::new(vec![1, 2, 3]); assert!(!packet.is_expired()); - // 模拟超时 + // Simulate timeout packet.sent_at = Instant::now() - Duration::from_millis(600); assert!(packet.is_expired()); assert!(packet.should_retry(10)); diff --git a/src/tscore/src/connection/state.rs b/src/tscore/src/connection/state.rs index a794d95..dd534ee 100644 --- a/src/tscore/src/connection/state.rs +++ b/src/tscore/src/connection/state.rs @@ -1,8 +1,8 @@ -//! 连接状态管理 +//! Connection state management use std::fmt; -/// 连接状态 +/// Connection state #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub enum ConnectionState { Disconnected, @@ -42,7 +42,7 @@ impl fmt::Display for ConnectionState { } } -/// 连接状态机 +/// Connection state machine pub struct ConnectionStateMachine { state: ConnectionState, } diff --git a/src/tscore/src/crypto/eax.rs b/src/tscore/src/crypto/eax.rs index 25abc4b..22762ed 100644 --- a/src/tscore/src/crypto/eax.rs +++ b/src/tscore/src/crypto/eax.rs @@ -1,4 +1,4 @@ -//! EAX 模式加密 +//! EAX mode encryption use aes::Aes128; use eax::aead::consts::U8; @@ -9,7 +9,7 @@ use super::keys; use crate::protocol::{InPacket, OutPacket}; use crate::ProtocolError; -/// EAX 加密器 +/// EAX cipher pub struct EaxCipher { cipher: Eax, } @@ -32,7 +32,7 @@ impl EaxCipher { let tag = self .cipher .encrypt_in_place_detached(nonce, header, data) - .map_err(|_| ProtocolError::Encryption("EAX 加密失败".to_string()))?; + .map_err(|_| ProtocolError::Encryption("EAX encryption failed".to_string()))?; let mut mac = [0u8; 8]; mac.copy_from_slice(&tag[..8]); @@ -51,11 +51,11 @@ impl EaxCipher { self.cipher .decrypt_in_place_detached(nonce, header, data, tag) - .map_err(|_| ProtocolError::Decryption("MAC 验证失败".to_string())) + .map_err(|_| ProtocolError::Decryption("MAC verification failed".to_string())) } } -/// 加密数据包 +/// Encrypt packet pub fn encrypt_packet( packet: &mut OutPacket, generation_id: u32, @@ -77,7 +77,7 @@ pub fn encrypt_packet( Ok(()) } -/// 解密数据包 +/// Decrypt packet pub fn decrypt_packet( packet: &InPacket, generation_id: u32, @@ -99,7 +99,7 @@ pub fn decrypt_packet( Ok(data) } -/// 假加密 +/// Fake encryption pub fn encrypt_fake(packet: &mut OutPacket) -> Result<(), ProtocolError> { let cipher = EaxCipher::new(&keys::FAKE_KEY); let meta = packet.header.get_meta(packet.direction); @@ -108,7 +108,7 @@ pub fn encrypt_fake(packet: &mut OutPacket) -> Result<(), ProtocolError> { Ok(()) } -/// 假解密 +/// Fake decryption pub fn decrypt_fake(packet: &InPacket) -> Result, ProtocolError> { let cipher = EaxCipher::new(&keys::FAKE_KEY); let meta = packet.header.get_meta(packet.direction); diff --git a/src/tscore/src/crypto/hash.rs b/src/tscore/src/crypto/hash.rs index ed4240e..a886723 100644 --- a/src/tscore/src/crypto/hash.rs +++ b/src/tscore/src/crypto/hash.rs @@ -1,9 +1,9 @@ -//! 哈希函数 +//! Hash functions use sha1::Sha1; use sha2::{Digest, Sha256, Sha512}; -/// SHA-1 哈希 +/// SHA-1 hash pub fn sha1(data: &[u8]) -> [u8; 20] { let mut hasher = Sha1::new(); hasher.update(data); @@ -13,7 +13,7 @@ pub fn sha1(data: &[u8]) -> [u8; 20] { hash } -/// SHA-256 哈希 +/// SHA-256 hash pub fn sha256(data: &[u8]) -> [u8; 32] { let mut hasher = Sha256::new(); hasher.update(data); @@ -23,7 +23,7 @@ pub fn sha256(data: &[u8]) -> [u8; 32] { hash } -/// SHA-512 哈希 +/// SHA-512 hash pub fn sha512(data: &[u8]) -> [u8; 64] { let mut hasher = Sha512::new(); hasher.update(data); @@ -33,7 +33,7 @@ pub fn sha512(data: &[u8]) -> [u8; 64] { hash } -/// 计算密码哈希 +/// Compute password hash pub fn hash_password(password: &str) -> String { let hash = sha1(password.as_bytes()); base64::Engine::encode(&base64::engine::general_purpose::STANDARD, hash) diff --git a/src/tscore/src/crypto/keys.rs b/src/tscore/src/crypto/keys.rs index 300b0b2..d800a3f 100644 --- a/src/tscore/src/crypto/keys.rs +++ b/src/tscore/src/crypto/keys.rs @@ -1,4 +1,4 @@ -//! 密钥管理 +//! Key management use sha1::Sha1; use sha2::{Digest, Sha256, Sha512}; @@ -6,19 +6,19 @@ use sha2::{Digest, Sha256, Sha512}; use crate::protocol::Direction; use crate::protocol::PacketType; -/// 假加密密钥 +/// Fake encryption key pub const FAKE_KEY: [u8; 16] = *b"c:\\windows\\syste"; -/// 假加密 Nonce +/// Fake encryption nonce pub const FAKE_NONCE: [u8; 16] = *b"m\\firewall32.cpl"; -/// 许可证根密钥 +/// License root key pub const ROOT_KEY: [u8; 32] = [ 0xcd, 0x0d, 0xe2, 0xae, 0xd4, 0x63, 0x45, 0x50, 0x9a, 0x7e, 0x3c, 0xfd, 0x8f, 0x68, 0xb3, 0xdc, 0x75, 0x55, 0xb2, 0x9d, 0xcc, 0xec, 0x73, 0xcd, 0x18, 0x75, 0x0f, 0x99, 0x38, 0x12, 0x40, 0x8a, ]; -/// 共享密钥 +/// Shared secret #[derive(Clone)] pub struct SharedSecret { pub iv: [u8; 64], @@ -87,7 +87,7 @@ impl std::fmt::Debug for SharedSecret { } } -/// 缓存的密钥 +/// Cached key #[derive(Debug, Clone)] pub struct CachedKey { pub generation_id: u32, @@ -115,7 +115,7 @@ impl Default for CachedKey { } } -/// 密钥缓存 +/// Key cache pub struct KeyCache { cache: [[CachedKey; 2]; 8], } @@ -162,7 +162,7 @@ impl Default for KeyCache { } } -/// 创建密钥和 Nonce +/// Create key and nonce pub fn create_key_nonce( packet_type: PacketType, direction: Direction, @@ -192,7 +192,7 @@ pub fn create_key_nonce( (key, nonce) } -/// 创建用于加密的密钥 +/// Create encryption key pub fn create_encryption_key(key: &[u8; 16], packet_id: u16) -> [u8; 16] { let mut result = *key; result[0] ^= (packet_id >> 8) as u8; @@ -200,7 +200,7 @@ pub fn create_encryption_key(key: &[u8; 16], packet_id: u16) -> [u8; 16] { result } -/// 计算 Hash Cash 级别 +/// Compute hash cash level pub fn get_hash_cash_level(omega: &str, offset: u64) -> u8 { let mut hasher = Sha1::new(); hasher.update(format!("{}{}", omega, offset).as_bytes()); @@ -219,7 +219,7 @@ pub fn get_hash_cash_level(omega: &str, offset: u64) -> u8 { level } -/// 计算 UID +/// Compute UID pub fn compute_uid(public_key: &[u8]) -> String { let mut hasher = Sha1::new(); hasher.update(public_key); diff --git a/src/tscore/src/crypto/mod.rs b/src/tscore/src/crypto/mod.rs index 1870e13..5463433 100644 --- a/src/tscore/src/crypto/mod.rs +++ b/src/tscore/src/crypto/mod.rs @@ -1,4 +1,4 @@ -//! 加密模块 +//! Cryptography module pub mod eax; pub mod ephemeral; diff --git a/src/tscore/src/crypto/tests.rs b/src/tscore/src/crypto/tests.rs index 2385788..b60626c 100644 --- a/src/tscore/src/crypto/tests.rs +++ b/src/tscore/src/crypto/tests.rs @@ -1,4 +1,4 @@ -//! 加密测试 +//! Cryptography tests #[cfg(test)] mod tests { @@ -50,7 +50,7 @@ mod tests { let encrypted = create_encryption_key(&key, 0x1234); assert_eq!(encrypted[0], key[0] ^ 0x12); assert_eq!(encrypted[1], key[1] ^ 0x34); - // 其他字节不变 + // Other bytes unchanged assert_eq!(encrypted[2], key[2]); } @@ -86,7 +86,7 @@ mod tests { assert_eq!(key1, key2); assert_eq!(nonce1, nonce2); - // 不同的 generation_id 应该返回不同的密钥 + // Different generation_id should return different keys let (key3, _) = cache.get_or_create(PacketType::Command, Direction::C2S, 1, &iv); assert_ne!(key1, key3); } @@ -106,10 +106,10 @@ mod tests { let header = b"test header"; let mut data = b"Hello, World!".to_vec(); - // 加密 + // Encrypt let mac = cipher.encrypt(&nonce, header, &mut data).unwrap(); - // 解密 + // Decrypt cipher.decrypt(&nonce, header, &mut data, &mac).unwrap(); assert_eq!(data, b"Hello, World!"); @@ -124,10 +124,10 @@ mod tests { ); packet.header.packet_id = 1; - // 假加密 + // Fake encryption encrypt_fake(&mut packet).unwrap(); - // 假解密 + // Fake decryption let in_packet = InPacket { direction: Direction::C2S, header: packet.header.clone(), @@ -140,13 +140,13 @@ mod tests { #[test] fn test_hash_cash_level() { - // 测试不同的 offset 产生不同的 level + // Test that different offsets produce different levels let level0 = get_hash_cash_level("test_key", 0); let level1 = get_hash_cash_level("test_key", 1); assert!(level0 <= 160); assert!(level1 <= 160); - // 使用一个会产生更高 level 的 key + // Use a key that produces a higher level let level_high = get_hash_cash_level("a", 12345); assert!(level_high <= 160); } @@ -156,7 +156,7 @@ mod tests { let public_key = b"test_public_key_data"; let uid = compute_uid(public_key); assert!(!uid.is_empty()); - // UID 应该是 base64 编码的 SHA1 哈希 + // UID should be a base64-encoded SHA1 hash assert!(uid.len() > 20); } } diff --git a/src/tscore/src/lib.rs b/src/tscore/src/lib.rs index 314cc57..8a92528 100644 --- a/src/tscore/src/lib.rs +++ b/src/tscore/src/lib.rs @@ -1,4 +1,4 @@ -//! TeamSpeak 3 协议核心实现 +//! TeamSpeak 3 protocol core implementation pub mod connection; pub mod crypto; @@ -14,59 +14,59 @@ pub use query::*; use thiserror::Error; -/// 协议错误 +/// Protocol error #[derive(Error, Debug)] pub enum ProtocolError { - #[error("数据包解析错误: {0}")] + #[error("packet parse error: {0}")] PacketParse(String), - #[error("加密错误: {0}")] + #[error("encryption error: {0}")] Encryption(String), - #[error("解密错误: {0}")] + #[error("decryption error: {0}")] Decryption(String), - #[error("压缩错误: {0}")] + #[error("compression error: {0}")] Compression(String), - #[error("解压错误: {0}")] + #[error("decompression error: {0}")] Decompression(String), - #[error("无效的数据包类型: {0}")] + #[error("invalid packet type: {0}")] InvalidPacketType(u8), - #[error("无效的标志位: {0}")] + #[error("invalid flags: {0}")] InvalidFlags(u8), - #[error("数据包过大: {size} > {max}")] + #[error("packet too large: {size} > {max}")] PacketTooLarge { size: usize, max: usize }, - #[error("数据包过小: {size} < {min}")] + #[error("packet too small: {size} < {min}")] PacketTooSmall { size: usize, min: usize }, - #[error("无效的客户端 ID: {0}")] + #[error("invalid client ID: {0}")] InvalidClientId(u16), - #[error("无效的数据包 ID: {0}")] + #[error("invalid packet ID: {0}")] InvalidPacketId(u16), - #[error("MAC 验证失败")] + #[error("MAC verification failed")] MacVerificationFailed, - #[error("超时: {0}")] + #[error("timeout: {0}")] Timeout(String), - #[error("连接关闭")] + #[error("connection closed")] ConnectionClosed, - #[error("命令错误: {0}")] + #[error("command error: {0}")] Command(String), - #[error("网络错误: {0}")] + #[error("network error: {0}")] Network(#[from] std::io::Error), } -/// 协议结果类型 +/// Protocol result type pub type ProtocolResult = Result; impl From for ProtocolError { diff --git a/src/tscore/src/network/mod.rs b/src/tscore/src/network/mod.rs index f3db50e..f4399a8 100644 --- a/src/tscore/src/network/mod.rs +++ b/src/tscore/src/network/mod.rs @@ -1,4 +1,4 @@ -//! 网络模块 +//! Network module pub mod resolver; pub mod socket; diff --git a/src/tscore/src/network/resolver.rs b/src/tscore/src/network/resolver.rs index 6e1853e..c28dc40 100644 --- a/src/tscore/src/network/resolver.rs +++ b/src/tscore/src/network/resolver.rs @@ -1,15 +1,15 @@ -//! 地址解析 +//! Address resolution use std::net::SocketAddr; -/// 服务器地址 +/// Server address #[derive(Debug, Clone)] pub enum ServerAddress { - /// 直接 IP 地址 + /// Direct IP address Ip(SocketAddr), - /// 域名 + /// Domain name Domain(String), - /// 服务器昵称 + /// Server nickname Nickname(String), } @@ -24,15 +24,15 @@ impl ServerAddress { } async fn resolve_domain(domain: &str) -> Result> { - // 尝试直接解析 + // Try direct resolution let addrs = tokio::net::lookup_host(format!("{}:9987", domain)).await?; addrs .into_iter() .next() - .ok_or_else(|| "无法解析域名".into()) + .ok_or_else(|| "failed to resolve domain".into()) } async fn resolve_nickname(nickname: &str) -> Result> { - // TODO: 实现 TSDNS 和昵称解析 + // TODO: Implement TSDNS and nickname resolution resolve_domain(nickname).await } diff --git a/src/tscore/src/network/socket.rs b/src/tscore/src/network/socket.rs index 6ca7179..413202e 100644 --- a/src/tscore/src/network/socket.rs +++ b/src/tscore/src/network/socket.rs @@ -1,4 +1,4 @@ -//! UDP Socket 抽象 +//! UDP Socket abstraction use std::net::SocketAddr; use std::task::{Context, Poll}; @@ -26,7 +26,7 @@ pub trait Socket { fn local_addr(&self) -> std::io::Result; } -/// UDP Socket 实现 +/// UDP Socket implementation pub struct UdpSocketWrapper { socket: UdpSocket, } diff --git a/src/tscore/src/protocol/commands.rs b/src/tscore/src/protocol/commands.rs index 4fe2d98..e4f38f5 100644 --- a/src/tscore/src/protocol/commands.rs +++ b/src/tscore/src/protocol/commands.rs @@ -1,8 +1,8 @@ -//! 命令解析和序列化 +//! Command parsing and serialization use std::fmt; -/// 命令解析错误 +/// Command parsing error #[derive(Debug, Clone)] pub enum CommandError { InvalidFormat(String), @@ -14,12 +14,12 @@ pub enum CommandError { impl fmt::Display for CommandError { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { - Self::InvalidFormat(msg) => write!(f, "无效的命令格式: {}", msg), - Self::MissingParameter(name) => write!(f, "缺少必需的参数: {}", name), + Self::InvalidFormat(msg) => write!(f, "invalid command format: {}", msg), + Self::MissingParameter(name) => write!(f, "missing required parameter: {}", name), Self::InvalidParameterValue { name, value } => { - write!(f, "无效的参数值: {}={}", name, value) + write!(f, "invalid parameter value: {}={}", name, value) } - Self::EscapeError(msg) => write!(f, "转义序列错误: {}", msg), + Self::EscapeError(msg) => write!(f, "escape sequence error: {}", msg), } } } @@ -28,7 +28,7 @@ impl std::error::Error for CommandError {} pub type CommandResult = Result; -/// 转义序列处理 +/// Escape sequence handling pub mod escape { use super::CommandError; @@ -65,12 +65,12 @@ pub mod escape { Some('t') => result.push('\t'), Some(other) => { return Err(CommandError::EscapeError(format!( - "未知的转义序列: \\{}", + "unknown escape sequence: \\{}", other ))) } None => { - return Err(CommandError::EscapeError("意外的转义序列结束".to_string())) + return Err(CommandError::EscapeError("unexpected end of escape sequence".to_string())) } } } else { @@ -82,7 +82,7 @@ pub mod escape { } } -/// 命令参数 +/// Command argument #[derive(Debug, Clone)] pub struct CommandArgument { pub name: String, @@ -126,7 +126,7 @@ impl fmt::Display for CommandArgument { } } -/// 命令 +/// Command #[derive(Debug, Clone)] pub struct Command { pub name: String, @@ -177,7 +177,7 @@ impl Command { pub fn parse(input: &str) -> CommandResult { let input = input.trim(); if input.is_empty() { - return Err(CommandError::InvalidFormat("空命令".to_string())); + return Err(CommandError::InvalidFormat("empty command".to_string())); } let parts: Vec<&str> = input.splitn(2, ' ').collect(); @@ -208,7 +208,7 @@ impl Command { pub fn parse_many(input: &str) -> CommandResult> { let input = input.trim(); if input.is_empty() { - return Err(CommandError::InvalidFormat("空命令".to_string())); + return Err(CommandError::InvalidFormat("empty command".to_string())); } let parts: Vec<&str> = input.splitn(2, ' ').collect(); @@ -242,7 +242,7 @@ impl fmt::Display for Command { } } -/// 命令构建器 +/// Command builder pub struct CommandBuilder { command: Command, } diff --git a/src/tscore/src/protocol/mod.rs b/src/tscore/src/protocol/mod.rs index 8cff5bb..1e22af8 100644 --- a/src/tscore/src/protocol/mod.rs +++ b/src/tscore/src/protocol/mod.rs @@ -1,4 +1,4 @@ -//! 协议模块 +//! Protocol module pub mod commands; pub mod packet; diff --git a/src/tscore/src/protocol/packet.rs b/src/tscore/src/protocol/packet.rs index 522ef8e..46453ab 100644 --- a/src/tscore/src/protocol/packet.rs +++ b/src/tscore/src/protocol/packet.rs @@ -1,24 +1,24 @@ -//! 数据包定义和处理 +//! Packet definition and handling use std::fmt; use super::types::*; use crate::ProtocolError; -/// 最大数据包大小 +/// Maximum packet size pub const MAX_PACKET_SIZE: usize = 500; -/// C2S 头部大小 +/// C2S header size pub const C2S_HEADER_SIZE: usize = 13; // 8 (MAC) + 2 (PId) + 2 (CId) + 1 (PT) -/// S2C 头部大小 +/// S2C header size pub const S2C_HEADER_SIZE: usize = 11; // 8 (MAC) + 2 (PId) + 1 (PT) /// Init packets use a fixed MAC and packet id during the TS3 handshake. pub const INIT_MAC: [u8; 8] = *b"TS3INIT1"; pub const INIT_PACKET_ID: u16 = 0x65; -/// 数据包方向 +/// Packet direction #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub enum Direction { C2S, @@ -34,7 +34,7 @@ impl Direction { } } -/// 数据包标志位 +/// Packet flags #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub struct Flags(pub u8); @@ -120,7 +120,7 @@ impl fmt::Display for Flags { } } -/// 数据包头部 +/// Packet header #[derive(Debug, Clone)] pub struct Header { pub mac: [u8; 8], @@ -218,7 +218,7 @@ impl Header { } } -/// 输入数据包 +/// Inbound packet #[derive(Debug, Clone)] pub struct InPacket { pub direction: Direction, @@ -256,7 +256,7 @@ impl InPacket { } } -/// 输出数据包 +/// Outbound packet #[derive(Debug, Clone)] pub struct OutPacket { pub direction: Direction, @@ -326,7 +326,7 @@ impl OutPacket { } } -/// 确认数据包 +/// Acknowledgment packet #[derive(Debug, Clone)] pub struct AckPacket { pub direction: Direction, @@ -351,7 +351,7 @@ impl AckPacket { } } -/// 初始化步骤 +/// Init step #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub enum InitStep { Init0, @@ -362,7 +362,7 @@ pub enum InitStep { Reset, } -/// 初始化数据包 +/// Init packet #[derive(Debug, Clone)] pub struct InitPacket { pub step: InitStep, diff --git a/src/tscore/src/protocol/tests.rs b/src/tscore/src/protocol/tests.rs index a23c48c..854ad1e 100644 --- a/src/tscore/src/protocol/tests.rs +++ b/src/tscore/src/protocol/tests.rs @@ -1,4 +1,4 @@ -//! 数据包处理测试 +//! Packet processing tests #[cfg(test)] mod tests { diff --git a/src/tscore/src/protocol/types.rs b/src/tscore/src/protocol/types.rs index 5b1106d..37c5366 100644 --- a/src/tscore/src/protocol/types.rs +++ b/src/tscore/src/protocol/types.rs @@ -1,8 +1,8 @@ -//! 协议类型定义 +//! Protocol type definitions use std::fmt; -/// 数据包类型 +/// Packet type #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] pub enum PacketType { Voice, @@ -111,7 +111,7 @@ impl fmt::Display for PacketType { } } -/// 编解码器类型 +/// Codec type #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] pub enum CodecType { SpeexNarrowband, @@ -163,7 +163,7 @@ impl CodecType { } } -/// 私语类型 +/// Whisper type #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub enum GroupWhisperType { ServerGroup, @@ -193,7 +193,7 @@ impl GroupWhisperType { } } -/// 私语目标 +/// Whisper target #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub enum GroupWhisperTarget { AllChannels, diff --git a/src/tsdb/src/bookmark.rs b/src/tsdb/src/bookmark.rs index 8c05061..1302775 100644 --- a/src/tsdb/src/bookmark.rs +++ b/src/tsdb/src/bookmark.rs @@ -1,11 +1,10 @@ -//! 书签管理 +//! Bookmark management use chrono::Utc; use rusqlite::params; use super::{DatabaseError, DatabaseManager, DatabaseResult}; -/// 书签信息 #[derive(Debug, Clone)] pub struct Bookmark { pub id: String, @@ -24,7 +23,6 @@ pub struct Bookmark { } impl DatabaseManager { - /// 创建书签 pub fn create_bookmark( &self, name: &str, @@ -57,7 +55,6 @@ impl DatabaseManager { }) } - /// 获取书签 pub fn get_bookmark(&self, id: &str) -> DatabaseResult { let conn = self.connection(); let mut stmt = conn.prepare( @@ -82,12 +79,11 @@ impl DatabaseManager { updated_at: row.get(12)?, }) }) - .map_err(|_| DatabaseError::NotFound(format!("书签 {} 未找到", id)))?; + .map_err(|_| DatabaseError::NotFound(format!("Bookmark {} not found", id)))?; Ok(bookmark) } - /// 获取所有书签 pub fn get_all_bookmarks(&self) -> DatabaseResult> { let conn = self.connection(); let mut stmt = conn.prepare( @@ -117,7 +113,6 @@ impl DatabaseManager { Ok(bookmarks) } - /// 更新书签 pub fn update_bookmark( &self, id: &str, @@ -159,14 +154,12 @@ impl DatabaseManager { Ok(()) } - /// 删除书签 pub fn delete_bookmark(&self, id: &str) -> DatabaseResult<()> { self.connection() .execute("DELETE FROM bookmarks WHERE id = ?1", params![id])?; Ok(()) } - /// 更新最后连接时间 pub fn update_bookmark_last_connected(&self, id: &str) -> DatabaseResult<()> { let now = Utc::now().to_rfc3339(); self.connection().execute( diff --git a/src/tsdb/src/config.rs b/src/tsdb/src/config.rs index 2c9d98b..423b86e 100644 --- a/src/tsdb/src/config.rs +++ b/src/tsdb/src/config.rs @@ -1,4 +1,4 @@ -//! 配置管理 +//! Configuration management use chrono::Utc; use rusqlite::params; diff --git a/src/tsdb/src/identity.rs b/src/tsdb/src/identity.rs index 8a903e6..23d8cc3 100644 --- a/src/tsdb/src/identity.rs +++ b/src/tsdb/src/identity.rs @@ -1,11 +1,10 @@ -//! 身份管理 +//! Identity management use chrono::Utc; use rusqlite::params; use super::{DatabaseError, DatabaseManager, DatabaseResult}; -/// 身份信息 #[derive(Debug, Clone)] pub struct Identity { pub id: String, @@ -18,7 +17,6 @@ pub struct Identity { } impl DatabaseManager { - /// 创建身份 pub fn create_identity(&self, name: &str, private_key: &str) -> DatabaseResult { let id = uuid::Uuid::new_v4().to_string(); let now = Utc::now().to_rfc3339(); @@ -39,7 +37,6 @@ impl DatabaseManager { }) } - /// 获取身份 pub fn get_identity(&self, id: &str) -> DatabaseResult { let conn = self.connection(); let mut stmt = conn.prepare( @@ -58,12 +55,11 @@ impl DatabaseManager { updated_at: row.get(6)?, }) }) - .map_err(|_| DatabaseError::NotFound(format!("身份 {} 未找到", id)))?; + .map_err(|_| DatabaseError::NotFound(format!("Identity {} not found", id)))?; Ok(identity) } - /// 获取所有身份 pub fn get_all_identities(&self) -> DatabaseResult> { let conn = self.connection(); let mut stmt = conn.prepare( @@ -87,7 +83,6 @@ impl DatabaseManager { Ok(identities) } - /// 更新身份 pub fn update_identity( &self, id: &str, @@ -113,7 +108,6 @@ impl DatabaseManager { Ok(()) } - /// 删除身份 pub fn delete_identity(&self, id: &str) -> DatabaseResult<()> { self.connection() .execute("DELETE FROM identities WHERE id = ?1", params![id])?; diff --git a/src/tsdb/src/lib.rs b/src/tsdb/src/lib.rs index a7a3d69..44ed624 100644 --- a/src/tsdb/src/lib.rs +++ b/src/tsdb/src/lib.rs @@ -1,4 +1,4 @@ -//! 数据存储 +//! Database storage pub mod bookmark; pub mod config; @@ -11,29 +11,26 @@ pub use message::*; use thiserror::Error; -/// 数据库错误 #[derive(Error, Debug)] pub enum DatabaseError { - #[error("SQLite 错误: {0}")] + #[error("SQLite error: {0}")] Sqlite(#[from] rusqlite::Error), - #[error("序列化错误: {0}")] + #[error("Serialization error: {0}")] Serialization(#[from] serde_json::Error), - #[error("IO 错误: {0}")] + #[error("IO error: {0}")] Io(#[from] std::io::Error), - #[error("未找到: {0}")] + #[error("Not found: {0}")] NotFound(String), - #[error("已存在: {0}")] + #[error("Already exists: {0}")] AlreadyExists(String), } -/// 数据库结果类型 pub type DatabaseResult = Result; -/// 数据库管理器 pub struct DatabaseManager { conn: rusqlite::Connection, } diff --git a/src/tsdb/src/message.rs b/src/tsdb/src/message.rs index c05b0f8..faf1ef1 100644 --- a/src/tsdb/src/message.rs +++ b/src/tsdb/src/message.rs @@ -1,11 +1,10 @@ -//! 消息管理 +//! Message management use chrono::Utc; use rusqlite::params; use super::{DatabaseError, DatabaseManager, DatabaseResult}; -/// 消息信息 #[derive(Debug, Clone)] pub struct Message { pub id: i64, @@ -21,7 +20,6 @@ pub struct Message { } impl DatabaseManager { - /// 创建消息 #[allow(clippy::too_many_arguments)] pub fn create_message( &self, @@ -56,7 +54,6 @@ impl DatabaseManager { }) } - /// 获取消息 pub fn get_message(&self, id: i64) -> DatabaseResult { let conn = self.connection(); let mut stmt = conn.prepare( @@ -78,12 +75,11 @@ impl DatabaseManager { timestamp: row.get(9)?, }) }) - .map_err(|_| DatabaseError::NotFound(format!("消息 {} 未找到", id)))?; + .map_err(|_| DatabaseError::NotFound(format!("Message {} not found", id)))?; Ok(message) } - /// 获取服务器消息 pub fn get_server_messages( &self, server_address: &str, @@ -115,21 +111,18 @@ impl DatabaseManager { Ok(messages) } - /// 标记消息为已读 pub fn mark_message_read(&self, id: i64) -> DatabaseResult<()> { self.connection() .execute("UPDATE messages SET is_read = 1 WHERE id = ?1", params![id])?; Ok(()) } - /// 删除消息 pub fn delete_message(&self, id: i64) -> DatabaseResult<()> { self.connection() .execute("DELETE FROM messages WHERE id = ?1", params![id])?; Ok(()) } - /// 清空服务器消息 pub fn clear_server_messages(&self, server_address: &str) -> DatabaseResult<()> { self.connection().execute( "DELETE FROM messages WHERE server_address = ?1",