From effc859146d4572e3aacb53ad5ad9c292f74b0e9 Mon Sep 17 00:00:00 2001 From: neoapps-dev Date: Thu, 27 Aug 2026 17:49:24 +0300 Subject: [PATCH] feat(lceonline): GET, rewrite relay proxy --- src-tauri/src/networking/relay.rs | 84 +++++--------------------- src/components/views/LceOnlineView.tsx | 7 ++- src/services/LceOnlineService.ts | 24 +++++--- 3 files changed, 37 insertions(+), 78 deletions(-) diff --git a/src-tauri/src/networking/relay.rs b/src-tauri/src/networking/relay.rs index 1046cf0..0f308c8 100644 --- a/src-tauri/src/networking/relay.rs +++ b/src-tauri/src/networking/relay.rs @@ -32,7 +32,7 @@ async fn run_host_relay( .map_err(|e| format!("Proxy connect failed: {}", e))?; write_line(&mut host_conn, &format!("HOST {} 0", auth_token)).await?; - let game_stream = tokio::time::timeout( + let mut game_stream = tokio::time::timeout( std::time::Duration::from_secs(30), async { loop { @@ -59,41 +59,15 @@ async fn run_host_relay( .await .map_err(|e| format!("Proxy connect failed: {}", e))?; write_line(&mut accept_conn, &format!("ACCEPT {} 0 {}", auth_token, joiner_id)).await?; - let (mut g_read, mut g_write) = game_stream.into_split(); - let (mut a_read, mut a_write) = accept_conn.into_split(); - let c1 = cancel.clone(); - let c2 = cancel.clone(); - let t1 = tokio::spawn(async move { - let mut buf = [0u8; 65536]; - loop { - tokio::select! { - r = g_read.read(&mut buf) => { - match r { - Ok(0) | Err(_) => break, - Ok(n) => { if a_write.write_all(&buf[..n]).await.is_err() { break; } } - } - } - _ = c1.cancelled() => break, + tokio::select! { + res = tokio::io::copy_bidirectional(&mut game_stream, &mut accept_conn) => { + if let Err(e) = res { + eprintln!("Host Relay error: {}", e); } - } - }); + }, + _ = cancel.cancelled() => {}, + } - let t2 = tokio::spawn(async move { - let mut buf = [0u8; 65536]; - loop { - tokio::select! { - r = a_read.read(&mut buf) => { - match r { - Ok(0) | Err(_) => break, - Ok(n) => { if g_write.write_all(&buf[..n]).await.is_err() { break; } } - } - } - _ = c2.cancelled() => break, - } - } - }); - - let _ = tokio::join!(t1, t2); Ok(()) } @@ -118,46 +92,20 @@ async fn run_relay_proxy( *port = Some(local_port); } - let (local_stream, _) = tokio::select! { + let (mut local_stream, _) = tokio::select! { r = listener.accept() => r.map_err(|e| format!("Accept failed: {}", e))?, _ = cancel.cancelled() => return Err("Cancelled".into()), }; - let (mut l_read, mut l_write) = local_stream.into_split(); - let (mut s_read, mut s_write) = stream.into_split(); - let c1 = cancel.clone(); - let c2 = cancel.clone(); - let t1 = tokio::spawn(async move { - let mut buf = [0u8; 65536]; - loop { - tokio::select! { - r = l_read.read(&mut buf) => { - match r { - Ok(0) | Err(_) => break, - Ok(n) => { if s_write.write_all(&buf[..n]).await.is_err() { break; } } - } - } - _ = c1.cancelled() => break, + tokio::select! { + res = tokio::io::copy_bidirectional(&mut local_stream, &mut stream) => { + if let Err(e) = res { + eprintln!("Relay Proxy error: {}", e); } - } - }); + }, + _ = cancel.cancelled() => {}, + } - let t2 = tokio::spawn(async move { - let mut buf = [0u8; 65536]; - loop { - tokio::select! { - r = s_read.read(&mut buf) => { - match r { - Ok(0) | Err(_) => break, - Ok(n) => { if l_write.write_all(&buf[..n]).await.is_err() { break; } } - } - } - _ = c2.cancelled() => break, - } - } - }); - - let _ = tokio::join!(t1, t2); Ok(local_port) } diff --git a/src/components/views/LceOnlineView.tsx b/src/components/views/LceOnlineView.tsx index 4c25415..7c381ef 100644 --- a/src/components/views/LceOnlineView.tsx +++ b/src/components/views/LceOnlineView.tsx @@ -41,7 +41,7 @@ const LceOnlineView = memo(function LceOnlineView({ const [incomingReqs, setIncomingReqs] = useState([]); const [outgoingReqs, setOutgoingReqs] = useState([]); const invites = invitesProp ?? []; - const [isHosting, setIsHosting] = useState(false); + const [isHosting, setIsHosting] = useState(lceOnlineService.isHosting); const [isAddingFriend, setIsAddingFriend] = useState(false); const [addFriendUsername, setAddFriendUsername] = useState(""); const addFriendInputRef = useRef(null); @@ -74,6 +74,7 @@ const LceOnlineView = memo(function LceOnlineView({ useEffect(() => { return lceOnlineService.onSessionChange(() => { setIsSignedIn(lceOnlineService.signedIn); + setIsHosting(lceOnlineService.isHosting); }); }, []); @@ -137,7 +138,7 @@ const LceOnlineView = memo(function LceOnlineView({ const token = lceOnlineService.accessToken ?? ""; if (!token) return; TauriService.startHostRelay(token, 25565).catch(() => {}); - setIsHosting(true); + lceOnlineService.isHosting = true; } catch (e: unknown) { setErrorModal(e instanceof Error ? e.message : "Failed to start hosting"); } @@ -150,7 +151,7 @@ const LceOnlineView = memo(function LceOnlineView({ } catch (e: unknown) { console.warn("Stop hosting failed", e); } - setIsHosting(false); + lceOnlineService.isHosting = false; }; const handleAction = async (action: () => Promise) => { diff --git a/src/services/LceOnlineService.ts b/src/services/LceOnlineService.ts index a91d367..3decf9a 100644 --- a/src/services/LceOnlineService.ts +++ b/src/services/LceOnlineService.ts @@ -34,6 +34,7 @@ export class LceOnlineService { private _session: SessionData | null = null; private baseUrl: string = SOCIAL_BASE_URL; private _listeners: Array<() => void> = []; + private _isHosting: boolean = false; constructor() { this.loadSession(); } @@ -57,6 +58,15 @@ export class LceOnlineService { return this._session?.account || null; } + get isHosting(): boolean { + return this._isHosting; + } + + set isHosting(value: boolean) { + this._isHosting = value; + this._notify(); + } + get displayUsername(): string { if (!this._session) return "Not signed in"; return ( @@ -220,49 +230,49 @@ export class LceOnlineService { } async sendFriendRequest(target: string): Promise { - const res = await this.request("POST", "/sendrequest", target); + const res = await this.request("GET", `/sendrequest?target=${encodeURIComponent(target)}`); if (typeof res === "string" && res !== "Successfully Sent Friend Request") { throw new Error(res); } } async acceptFriendRequest(from: string): Promise { - const res = await this.request("POST", "/acceptrequest", from); + const res = await this.request("GET", `/acceptrequest?from=${encodeURIComponent(from)}`); if (typeof res === "string" && res !== "1") { throw new Error(res); } } async declineFriendRequest(from: string): Promise { - const res = await this.request("POST", "/declinerequest", from); + const res = await this.request("GET", `/declinerequest?from=${encodeURIComponent(from)}`); if (typeof res === "string" && res !== "1") { throw new Error(res); } } async removeFriend(from: string): Promise { - const res = await this.request("POST", "/removefriend", from); + const res = await this.request("GET", `/removefriend?from=${encodeURIComponent(from)}`); if (typeof res === "string") { throw new Error(res); } } async sendInvite(target: string): Promise { - const res = await this.request("POST", "/invite", target); + const res = await this.request("GET", `/invite?target=${encodeURIComponent(target)}`); if (typeof res === "string" && res !== "Successfully Sent Invite") { throw new Error(res); } } async acceptInvite(from: string): Promise { - const res = await this.request("POST", "/acceptinvite", from); + const res = await this.request("GET", `/acceptinvite?from=${encodeURIComponent(from)}`); if (typeof res !== "string") throw new Error("Failed to accept invite"); return res; } async declineInvite(from: string): Promise { try { - await this.request("POST", "/declineinvite", from); + await this.request("GET", `/declineinvite?from=${encodeURIComponent(from)}`); } catch (e: unknown) { const msg = e instanceof Error ? e.message : ""; if (msg !== "Successfully Declined Invite") throw e;