mirror of
https://github.com/LCE-Hub/LCE-Emerald-Launcher.git
synced 2026-09-24 17:11:00 +00:00
feat(lceonline): GET, rewrite relay proxy
This commit is contained in:
@@ -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)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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>) => {
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|||||||
Reference in New Issue
Block a user