mirror of
https://github.com/LCE-Hub/LCE-Emerald-Launcher.git
synced 2026-10-22 17:22:06 +00:00
Some checks are pending
Nix / build (push) Waiting to run
Co-authored-by: str1k3r <115313679+S1l3ntStr1ke87@users.noreply.github.com> Co-authored-by: ByteBukkit <codeddev21@gmail.com>
215 lines
7 KiB
Rust
215 lines
7 KiB
Rust
use tauri::State;
|
|
use tokio::io::{AsyncReadExt, AsyncWriteExt};
|
|
use tokio::net::{TcpListener, TcpStream};
|
|
use tokio_util::sync::CancellationToken;
|
|
use crate::state::ProxyGuard;
|
|
const PROXY_ADDR: &str = "proxy.mclegacyedition.xyz:2052"; //neo: yeah bro im hardcoding it
|
|
async fn read_line(stream: &mut TcpStream) -> Result<String, String> {
|
|
let mut buf = Vec::new();
|
|
let mut byte = [0u8; 1];
|
|
loop {
|
|
stream.read_exact(&mut byte).await.map_err(|e| e.to_string())?;
|
|
if byte[0] == b'\n' { break; }
|
|
if byte[0] != b'\r' { buf.push(byte[0]); }
|
|
}
|
|
String::from_utf8(buf).map_err(|e| e.to_string())
|
|
}
|
|
|
|
async fn write_line(stream: &mut TcpStream, line: &str) -> Result<(), String> {
|
|
let data = format!("{}\n", line);
|
|
stream.write_all(data.as_bytes()).await.map_err(|e| e.to_string())
|
|
}
|
|
|
|
async fn run_host_relay(
|
|
_proxy_state: &ProxyGuard,
|
|
proxy_addr: &str,
|
|
auth_token: &str,
|
|
game_port: u16,
|
|
cancel: CancellationToken,
|
|
) -> Result<(), String> {
|
|
let mut host_conn = TcpStream::connect(proxy_addr)
|
|
.await
|
|
.map_err(|e| format!("Proxy connect failed: {}", e))?;
|
|
|
|
write_line(&mut host_conn, &format!("HOST {} 0", auth_token)).await?;
|
|
let mut game_stream = tokio::time::timeout(
|
|
std::time::Duration::from_secs(30),
|
|
async {
|
|
loop {
|
|
match TcpStream::connect(format!("127.0.0.1:{}", game_port)).await {
|
|
Ok(s) => return Ok::<_, String>(s),
|
|
Err(_) => {
|
|
tokio::select! {
|
|
_ = cancel.cancelled() => return Err("Cancelled".into()),
|
|
_ = tokio::time::sleep(std::time::Duration::from_secs(1)) => {}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
).await
|
|
.map_err(|_| "Timed out waiting for game to start".to_string())??;
|
|
let client_line = read_line(&mut host_conn).await?;
|
|
let client_parts: Vec<&str> = client_line.split_whitespace().collect();
|
|
if client_parts.len() < 2 || client_parts[0] != "CLIENT" {
|
|
return Err(format!("Expected CLIENT, got: {}", client_line));
|
|
}
|
|
let joiner_id = client_parts[1];
|
|
let mut accept_conn = TcpStream::connect(proxy_addr)
|
|
.await
|
|
.map_err(|e| format!("Proxy connect failed: {}", e))?;
|
|
write_line(&mut accept_conn, &format!("ACCEPT {} 0 {}", auth_token, joiner_id)).await?;
|
|
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() => {},
|
|
}
|
|
|
|
Ok(())
|
|
}
|
|
|
|
async fn run_relay_proxy(
|
|
proxy_state: &ProxyGuard,
|
|
proxy_addr: &str,
|
|
auth_token: &str,
|
|
target_session: &str,
|
|
cancel: CancellationToken,
|
|
) -> Result<u16, String> {
|
|
let mut stream = TcpStream::connect(proxy_addr)
|
|
.await
|
|
.map_err(|e| format!("Proxy connect failed: {}", e))?;
|
|
|
|
write_line(&mut stream, &format!("JOIN {} 0 {}", auth_token, target_session)).await?;
|
|
let listener = TcpListener::bind("0.0.0.0:61000")
|
|
.await
|
|
.map_err(|e| format!("Bind failed: {}", e))?;
|
|
let local_port = listener.local_addr().map_err(|e| e.to_string())?.port();
|
|
{
|
|
let mut port = proxy_state.local_port.lock().await;
|
|
*port = Some(local_port);
|
|
}
|
|
|
|
let (mut local_stream, _) = tokio::select! {
|
|
r = listener.accept() => r.map_err(|e| format!("Accept failed: {}", e))?,
|
|
_ = cancel.cancelled() => return Err("Cancelled".into()),
|
|
};
|
|
|
|
tokio::select! {
|
|
res = tokio::io::copy_bidirectional(&mut local_stream, &mut stream) => {
|
|
if let Err(e) = res {
|
|
eprintln!("Relay Proxy error: {}", e);
|
|
}
|
|
},
|
|
_ = cancel.cancelled() => {},
|
|
}
|
|
|
|
Ok(local_port)
|
|
}
|
|
|
|
#[tauri::command]
|
|
pub async fn start_host_relay(
|
|
proxy_state: State<'_, ProxyGuard>,
|
|
auth_token: String,
|
|
game_port: u16,
|
|
) -> Result<(), String> {
|
|
let addr = PROXY_ADDR;
|
|
let cancel = CancellationToken::new();
|
|
let session_id = "__host__".to_string();
|
|
{
|
|
let mut tokens = proxy_state.cancel_tokens.lock().await;
|
|
if let Some(old) = tokens.insert(session_id.clone(), cancel.clone()) {
|
|
old.cancel();
|
|
}
|
|
}
|
|
|
|
let result = run_host_relay(&proxy_state, &addr, &auth_token, game_port, cancel).await;
|
|
{
|
|
let mut tokens = proxy_state.cancel_tokens.lock().await;
|
|
tokens.remove(&session_id);
|
|
}
|
|
|
|
result
|
|
}
|
|
|
|
#[tauri::command]
|
|
pub async fn start_relay_proxy(
|
|
proxy_state: State<'_, ProxyGuard>,
|
|
auth_token: String,
|
|
target_session: String,
|
|
) -> Result<u16, String> {
|
|
let addr = PROXY_ADDR;
|
|
let cancel = CancellationToken::new();
|
|
let session_id = target_session.clone();
|
|
{
|
|
let mut tokens = proxy_state.cancel_tokens.lock().await;
|
|
tokens.insert(session_id.clone(), cancel.clone());
|
|
}
|
|
|
|
let local_port = run_relay_proxy(&proxy_state, &addr, &auth_token, &target_session, cancel).await?;
|
|
{
|
|
let mut tokens = proxy_state.cancel_tokens.lock().await;
|
|
tokens.remove(&session_id);
|
|
}
|
|
|
|
Ok(local_port)
|
|
}
|
|
|
|
#[tauri::command]
|
|
pub async fn stop_proxy(proxy_state: State<'_, ProxyGuard>, session_id: String) -> Result<(), String> {
|
|
let mut tokens = proxy_state.cancel_tokens.lock().await;
|
|
let found = tokens.remove(&session_id);
|
|
if let Some(token) = found {
|
|
token.cancel();
|
|
let mut port = proxy_state.local_port.lock().await;
|
|
*port = None;
|
|
}
|
|
Ok(())
|
|
}
|
|
|
|
#[tauri::command]
|
|
pub async fn stop_all_proxies(proxy_state: State<'_, ProxyGuard>) -> Result<(), String> {
|
|
let mut tokens = proxy_state.cancel_tokens.lock().await;
|
|
for (_, token) in tokens.drain() {
|
|
token.cancel();
|
|
}
|
|
let mut port = proxy_state.local_port.lock().await;
|
|
*port = None;
|
|
Ok(())
|
|
}
|
|
|
|
#[tauri::command]
|
|
pub async fn join_game(
|
|
app: tauri::AppHandle,
|
|
game_state: State<'_, crate::state::GameState>,
|
|
_proxy_state: State<'_, ProxyGuard>,
|
|
_api_base_url: String,
|
|
_auth_token: String,
|
|
host_ip: String,
|
|
host_port: u16,
|
|
_target_session: String,
|
|
instance_id: String,
|
|
) -> Result<(), String> {
|
|
let server = crate::types::McServer {
|
|
name: host_ip.clone(),
|
|
ip: host_ip,
|
|
port: host_port,
|
|
};
|
|
#[cfg(target_os = "windows")]
|
|
{
|
|
let deadline = tokio::time::Instant::now() + tokio::time::Duration::from_secs(10);
|
|
loop {
|
|
if tokio::net::TcpStream::connect(format!("127.0.0.1:{}", host_port)).await.is_ok() {
|
|
break;
|
|
}
|
|
if tokio::time::Instant::now() >= deadline {
|
|
return Err("Timed out waiting for relay proxy".into());
|
|
}
|
|
tokio::time::sleep(std::time::Duration::from_millis(200)).await;
|
|
}
|
|
} //neo: workaround for Windows having a race condition where the game is launched before the relay proxy
|
|
crate::commands::game::launch_game(app, game_state, instance_id, vec![server], vec![]).await
|
|
}
|