feat(lceonline): GET, rewrite relay proxy

This commit is contained in:
neoapps-dev
2026-08-27 17:49:24 +03:00
parent c4b87009be
commit effc859146
3 changed files with 37 additions and 78 deletions
+16 -68
View File
@@ -32,7 +32,7 @@ async fn run_host_relay(
.map_err(|e| format!("Proxy connect failed: {}", e))?; .map_err(|e| format!("Proxy connect failed: {}", e))?;
write_line(&mut host_conn, &format!("HOST {} 0", auth_token)).await?; 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), std::time::Duration::from_secs(30),
async { async {
loop { loop {
@@ -59,41 +59,15 @@ async fn run_host_relay(
.await .await
.map_err(|e| format!("Proxy connect failed: {}", e))?; .map_err(|e| format!("Proxy connect failed: {}", e))?;
write_line(&mut accept_conn, &format!("ACCEPT {} 0 {}", auth_token, joiner_id)).await?; write_line(&mut accept_conn, &format!("ACCEPT {} 0 {}", auth_token, joiner_id)).await?;
let (mut g_read, mut g_write) = game_stream.into_split(); tokio::select! {
let (mut a_read, mut a_write) = accept_conn.into_split(); res = tokio::io::copy_bidirectional(&mut game_stream, &mut accept_conn) => {
let c1 = cancel.clone(); if let Err(e) = res {
let c2 = cancel.clone(); eprintln!("Host Relay error: {}", e);
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,
} }
} },
}); _ = 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(()) Ok(())
} }
@@ -118,46 +92,20 @@ async fn run_relay_proxy(
*port = Some(local_port); *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))?, r = listener.accept() => r.map_err(|e| format!("Accept failed: {}", e))?,
_ = cancel.cancelled() => return Err("Cancelled".into()), _ = cancel.cancelled() => return Err("Cancelled".into()),
}; };
let (mut l_read, mut l_write) = local_stream.into_split(); tokio::select! {
let (mut s_read, mut s_write) = stream.into_split(); res = tokio::io::copy_bidirectional(&mut local_stream, &mut stream) => {
let c1 = cancel.clone(); if let Err(e) = res {
let c2 = cancel.clone(); eprintln!("Relay Proxy error: {}", e);
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,
} }
} },
}); _ = 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) Ok(local_port)
} }
+4 -3
View File
@@ -41,7 +41,7 @@ const LceOnlineView = memo(function LceOnlineView({
const [incomingReqs, setIncomingReqs] = useState<SocialEntry[]>([]); const [incomingReqs, setIncomingReqs] = useState<SocialEntry[]>([]);
const [outgoingReqs, setOutgoingReqs] = useState<SocialEntry[]>([]); const [outgoingReqs, setOutgoingReqs] = useState<SocialEntry[]>([]);
const invites = invitesProp ?? []; const invites = invitesProp ?? [];
const [isHosting, setIsHosting] = useState(false); const [isHosting, setIsHosting] = useState(lceOnlineService.isHosting);
const [isAddingFriend, setIsAddingFriend] = useState(false); const [isAddingFriend, setIsAddingFriend] = useState(false);
const [addFriendUsername, setAddFriendUsername] = useState(""); const [addFriendUsername, setAddFriendUsername] = useState("");
const addFriendInputRef = useRef<HTMLInputElement>(null); const addFriendInputRef = useRef<HTMLInputElement>(null);
@@ -74,6 +74,7 @@ const LceOnlineView = memo(function LceOnlineView({
useEffect(() => { useEffect(() => {
return lceOnlineService.onSessionChange(() => { return lceOnlineService.onSessionChange(() => {
setIsSignedIn(lceOnlineService.signedIn); setIsSignedIn(lceOnlineService.signedIn);
setIsHosting(lceOnlineService.isHosting);
}); });
}, []); }, []);
@@ -137,7 +138,7 @@ const LceOnlineView = memo(function LceOnlineView({
const token = lceOnlineService.accessToken ?? ""; const token = lceOnlineService.accessToken ?? "";
if (!token) return; if (!token) return;
TauriService.startHostRelay(token, 25565).catch(() => {}); TauriService.startHostRelay(token, 25565).catch(() => {});
setIsHosting(true); lceOnlineService.isHosting = true;
} catch (e: unknown) { } catch (e: unknown) {
setErrorModal(e instanceof Error ? e.message : "Failed to start hosting"); setErrorModal(e instanceof Error ? e.message : "Failed to start hosting");
} }
@@ -150,7 +151,7 @@ const LceOnlineView = memo(function LceOnlineView({
} catch (e: unknown) { } catch (e: unknown) {
console.warn("Stop hosting failed", e); console.warn("Stop hosting failed", e);
} }
setIsHosting(false); lceOnlineService.isHosting = false;
}; };
const handleAction = async (action: () => Promise<void>) => { const handleAction = async (action: () => Promise<void>) => {
+17 -7
View File
@@ -34,6 +34,7 @@ export class LceOnlineService {
private _session: SessionData | null = null; private _session: SessionData | null = null;
private baseUrl: string = SOCIAL_BASE_URL; private baseUrl: string = SOCIAL_BASE_URL;
private _listeners: Array<() => void> = []; private _listeners: Array<() => void> = [];
private _isHosting: boolean = false;
constructor() { constructor() {
this.loadSession(); this.loadSession();
} }
@@ -57,6 +58,15 @@ export class LceOnlineService {
return this._session?.account || null; return this._session?.account || null;
} }
get isHosting(): boolean {
return this._isHosting;
}
set isHosting(value: boolean) {
this._isHosting = value;
this._notify();
}
get displayUsername(): string { get displayUsername(): string {
if (!this._session) return "Not signed in"; if (!this._session) return "Not signed in";
return ( return (
@@ -220,49 +230,49 @@ export class LceOnlineService {
} }
async sendFriendRequest(target: string): Promise<void> { async sendFriendRequest(target: string): Promise<void> {
const res = await this.request<string>("POST", "/sendrequest", target); const res = await this.request<string>("GET", `/sendrequest?target=${encodeURIComponent(target)}`);
if (typeof res === "string" && res !== "Successfully Sent Friend Request") { if (typeof res === "string" && res !== "Successfully Sent Friend Request") {
throw new Error(res); throw new Error(res);
} }
} }
async acceptFriendRequest(from: string): Promise<void> { async acceptFriendRequest(from: string): Promise<void> {
const res = await this.request<string>("POST", "/acceptrequest", from); const res = await this.request<string>("GET", `/acceptrequest?from=${encodeURIComponent(from)}`);
if (typeof res === "string" && res !== "1") { if (typeof res === "string" && res !== "1") {
throw new Error(res); throw new Error(res);
} }
} }
async declineFriendRequest(from: string): Promise<void> { async declineFriendRequest(from: string): Promise<void> {
const res = await this.request<string>("POST", "/declinerequest", from); const res = await this.request<string>("GET", `/declinerequest?from=${encodeURIComponent(from)}`);
if (typeof res === "string" && res !== "1") { if (typeof res === "string" && res !== "1") {
throw new Error(res); throw new Error(res);
} }
} }
async removeFriend(from: string): Promise<void> { async removeFriend(from: string): Promise<void> {
const res = await this.request<string>("POST", "/removefriend", from); const res = await this.request<string>("GET", `/removefriend?from=${encodeURIComponent(from)}`);
if (typeof res === "string") { if (typeof res === "string") {
throw new Error(res); throw new Error(res);
} }
} }
async sendInvite(target: string): Promise<void> { async sendInvite(target: string): Promise<void> {
const res = await this.request<string>("POST", "/invite", target); const res = await this.request<string>("GET", `/invite?target=${encodeURIComponent(target)}`);
if (typeof res === "string" && res !== "Successfully Sent Invite") { if (typeof res === "string" && res !== "Successfully Sent Invite") {
throw new Error(res); throw new Error(res);
} }
} }
async acceptInvite(from: string): Promise<string> { async acceptInvite(from: string): Promise<string> {
const res = await this.request<string>("POST", "/acceptinvite", from); const res = await this.request<string>("GET", `/acceptinvite?from=${encodeURIComponent(from)}`);
if (typeof res !== "string") throw new Error("Failed to accept invite"); if (typeof res !== "string") throw new Error("Failed to accept invite");
return res; return res;
} }
async declineInvite(from: string): Promise<void> { async declineInvite(from: string): Promise<void> {
try { try {
await this.request("POST", "/declineinvite", from); await this.request("GET", `/declineinvite?from=${encodeURIComponent(from)}`);
} catch (e: unknown) { } catch (e: unknown) {
const msg = e instanceof Error ? e.message : ""; const msg = e instanceof Error ? e.message : "";
if (msg !== "Successfully Declined Invite") throw e; if (msg !== "Successfully Declined Invite") throw e;