enhanced websocket handling

This commit is contained in:
2026-01-25 20:54:56 +08:00
parent acf4b94a1f
commit a48e34adc7
5 changed files with 125 additions and 121 deletions

View File

@@ -1,52 +1,14 @@
use rust_socketio::{ClientBuilder, Event, Payload, RawClient}; use rust_socketio::ClientBuilder;
use serde_json::json;
use tauri::async_runtime; use tauri::async_runtime;
use tracing::{error, info}; use tracing::{error, info};
use crate::{ use crate::{
lock_r, lock_w, lock_r, lock_w,
services::{ services::client_config_manager::AppConfig,
client_config_manager::AppConfig, health_manager::close_health_manager_window,
scene::open_scene_window,
},
state::FDOLL, state::FDOLL,
}; };
use super::WS_EVENT; use super::handlers;
fn emit_initialize(socket: &RawClient) {
if let Err(e) = socket.emit(WS_EVENT::CLIENT_INITIALIZE, json!({})) {
error!("Failed to emit client-initialize: {:?}", e);
}
}
fn on_connected(_payload: Payload, socket: RawClient) {
info!("WebSocket connected. Sending initialization request.");
emit_initialize(&socket);
}
fn on_initialized(payload: Payload, _socket: RawClient) {
match payload {
Payload::Text(values) => {
if let Some(first_value) = values.first() {
info!("Received initialized event: {:?}", first_value);
// Mark WebSocket as initialized and reset backoff timer
let mut guard = lock_w!(FDOLL);
if let Some(clients) = guard.network.clients.as_mut() {
clients.is_ws_initialized = true;
}
// Connection restored: close health manager and reopen scene
close_health_manager_window();
open_scene_window();
} else {
info!("Received initialized event with empty payload");
}
}
_ => error!("Received unexpected payload format for initialized"),
}
}
pub async fn init_ws_client() { pub async fn init_ws_client() {
let app_config = { let app_config = {
@@ -89,62 +51,13 @@ pub async fn build_ws_client(
.ok_or("Missing API base URL")?; .ok_or("Missing API base URL")?;
let client_result = async_runtime::spawn_blocking(move || { let client_result = async_runtime::spawn_blocking(move || {
ClientBuilder::new(api_base_url) let builder = ClientBuilder::new(api_base_url)
.namespace("/") .namespace("/")
.on( .auth(serde_json::json!({ "token": token }));
WS_EVENT::FRIEND_REQUEST_RECEIVED,
super::friend::on_friend_request_received, let builder_with_handlers = handlers::register_event_handlers(builder);
)
.on( builder_with_handlers.connect()
WS_EVENT::FRIEND_REQUEST_ACCEPTED,
super::friend::on_friend_request_accepted,
)
.on(
WS_EVENT::FRIEND_REQUEST_DENIED,
super::friend::on_friend_request_denied,
)
.on(WS_EVENT::UNFRIENDED, super::friend::on_unfriended)
.on(
WS_EVENT::FRIEND_CURSOR_POSITION,
super::friend::on_friend_cursor_position,
)
.on(
WS_EVENT::FRIEND_DISCONNECTED,
super::friend::on_friend_disconnected,
)
.on(
WS_EVENT::FRIEND_DOLL_CREATED,
super::friend::on_friend_doll_created,
)
.on(
WS_EVENT::FRIEND_DOLL_UPDATED,
super::friend::on_friend_doll_updated,
)
.on(
WS_EVENT::FRIEND_DOLL_DELETED,
super::friend::on_friend_doll_deleted,
)
.on(
WS_EVENT::FRIEND_ACTIVE_DOLL_CHANGED,
super::friend::on_friend_active_doll_changed,
)
.on(WS_EVENT::DOLL_CREATED, super::doll::on_doll_created)
.on(WS_EVENT::DOLL_UPDATED, super::doll::on_doll_updated)
.on(WS_EVENT::DOLL_DELETED, super::doll::on_doll_deleted)
.on(WS_EVENT::INITIALIZED, on_initialized)
.on(
WS_EVENT::INTERACTION_RECEIVED,
super::interaction::on_interaction_received,
)
.on(
WS_EVENT::INTERACTION_DELIVERY_FAILED,
super::interaction::on_interaction_delivery_failed,
)
// rust-socketio fires Event::Connect on initial connect AND reconnects
// so we resend initialization there instead of a dedicated reconnect event.
.on(Event::Connect, on_connected)
.auth(json!({ "token": token }))
.connect()
}) })
.await; .await;

View File

@@ -0,0 +1,45 @@
use rust_socketio::{Payload, RawClient};
use tracing::info;
use crate::{
lock_w,
services::health_manager::close_health_manager_window,
services::scene::open_scene_window,
state::FDOLL,
};
use super::WS_EVENT;
fn emit_initialize(socket: &RawClient) {
if let Err(e) = socket.emit(WS_EVENT::CLIENT_INITIALIZE, serde_json::json!({})) {
tracing::error!("Failed to emit client-initialize: {:?}", e);
}
}
pub fn on_connected(_payload: Payload, socket: RawClient) {
info!("WebSocket connected. Sending initialization request.");
emit_initialize(&socket);
}
pub fn on_initialized(payload: Payload, _socket: RawClient) {
match payload {
Payload::Text(values) => {
if let Some(first_value) = values.first() {
info!("Received initialized event: {:?}", first_value);
// Mark WebSocket as initialized and reset backoff timer
let mut guard = lock_w!(FDOLL);
if let Some(clients) = guard.network.clients.as_mut() {
clients.is_ws_initialized = true;
}
// Connection restored: close health manager and reopen scene
close_health_manager_window();
open_scene_window();
} else {
info!("Received initialized event with empty payload");
}
}
_ => tracing::error!("Received unexpected payload format for initialized"),
}
}

View File

@@ -123,43 +123,28 @@ pub fn on_friend_disconnected(payload: Payload, _socket: RawClient) {
} }
pub fn on_friend_doll_created(payload: Payload, _socket: RawClient) { pub fn on_friend_doll_created(payload: Payload, _socket: RawClient) {
match payload { handle_friend_doll_change(WS_EVENT::FRIEND_DOLL_CREATED, payload);
Payload::Text(values) => {
// Log raw JSON for now, as requested
if let Some(first_value) = values.first() {
info!("Received friend-doll-created event: {:?}", first_value);
// Future: Trigger re-fetch or emit to frontend
} else {
info!("Received friend-doll-created event with empty payload");
}
}
_ => error!("Received unexpected payload format for friend-doll-created"),
}
} }
pub fn on_friend_doll_updated(payload: Payload, _socket: RawClient) { pub fn on_friend_doll_updated(payload: Payload, _socket: RawClient) {
match payload { handle_friend_doll_change(WS_EVENT::FRIEND_DOLL_UPDATED, payload);
Payload::Text(values) => {
if let Some(first_value) = values.first() {
info!("Received friend-doll-updated event: {:?}", first_value);
} else {
info!("Received friend-doll-updated event with empty payload");
}
}
_ => error!("Received unexpected payload format for friend-doll-updated"),
}
} }
pub fn on_friend_doll_deleted(payload: Payload, _socket: RawClient) { pub fn on_friend_doll_deleted(payload: Payload, _socket: RawClient) {
handle_friend_doll_change(WS_EVENT::FRIEND_DOLL_DELETED, payload);
}
fn handle_friend_doll_change(event_name: &str, payload: Payload) {
match payload { match payload {
Payload::Text(values) => { Payload::Text(values) => {
if let Some(first_value) = values.first() { if let Some(first_value) = values.first() {
info!("Received friend-doll-deleted event: {:?}", first_value); info!("Received {} event: {:?}", event_name, first_value);
// Future: Trigger re-fetch or emit to frontend
} else { } else {
info!("Received friend-doll-deleted event with empty payload"); info!("Received {} event with empty payload", event_name);
} }
} }
_ => error!("Received unexpected payload format for friend-doll-deleted"), _ => error!("Received unexpected payload format for {}", event_name),
} }
} }

View File

@@ -0,0 +1,59 @@
use rust_socketio::{ClientBuilder, Event};
use crate::services::ws::WS_EVENT;
pub fn register_event_handlers(builder: ClientBuilder) -> ClientBuilder {
builder
.on(
WS_EVENT::FRIEND_REQUEST_RECEIVED,
super::friend::on_friend_request_received,
)
.on(
WS_EVENT::FRIEND_REQUEST_ACCEPTED,
super::friend::on_friend_request_accepted,
)
.on(
WS_EVENT::FRIEND_REQUEST_DENIED,
super::friend::on_friend_request_denied,
)
.on(WS_EVENT::UNFRIENDED, super::friend::on_unfriended)
.on(
WS_EVENT::FRIEND_CURSOR_POSITION,
super::friend::on_friend_cursor_position,
)
.on(
WS_EVENT::FRIEND_DISCONNECTED,
super::friend::on_friend_disconnected,
)
.on(
WS_EVENT::FRIEND_DOLL_CREATED,
super::friend::on_friend_doll_created,
)
.on(
WS_EVENT::FRIEND_DOLL_UPDATED,
super::friend::on_friend_doll_updated,
)
.on(
WS_EVENT::FRIEND_DOLL_DELETED,
super::friend::on_friend_doll_deleted,
)
.on(
WS_EVENT::FRIEND_ACTIVE_DOLL_CHANGED,
super::friend::on_friend_active_doll_changed,
)
.on(WS_EVENT::DOLL_CREATED, super::doll::on_doll_created)
.on(WS_EVENT::DOLL_UPDATED, super::doll::on_doll_updated)
.on(WS_EVENT::DOLL_DELETED, super::doll::on_doll_deleted)
.on(WS_EVENT::INITIALIZED, super::connection::on_initialized)
.on(
WS_EVENT::INTERACTION_RECEIVED,
super::interaction::on_interaction_received,
)
.on(
WS_EVENT::INTERACTION_DELIVERY_FAILED,
super::interaction::on_interaction_delivery_failed,
)
// rust-socketio fires Event::Connect on initial connect AND reconnects
// so we resend initialization there instead of a dedicated reconnect event.
.on(Event::Connect, super::connection::on_connected)
}

View File

@@ -40,9 +40,11 @@ impl WS_EVENT {
} }
mod client; mod client;
mod connection;
mod cursor; mod cursor;
mod doll; mod doll;
mod friend; mod friend;
mod handlers;
mod interaction; mod interaction;
pub use client::init_ws_client; pub use client::init_ws_client;