mirror of
https://github.com/LCE-Hub/LCE-Emerald-Launcher.git
synced 2026-09-23 08:32:51 +00:00
1.6.0 drop 1 (#132)
This commit is contained in:
@@ -42,7 +42,6 @@ pub async fn workshop_install(app: AppHandle, request: WorkshopInstallRequest) -
|
||||
}
|
||||
let bytes = response.bytes().await.map_err(|e| e.to_string())?;
|
||||
fs::write(&zip_tmp, &bytes).map_err(|e| e.to_string())?;
|
||||
|
||||
let dest_dir = if placeholder.is_empty() {
|
||||
instance_dir.clone()
|
||||
} else {
|
||||
@@ -55,35 +54,96 @@ pub async fn workshop_install(app: AppHandle, request: WorkshopInstallRequest) -
|
||||
};
|
||||
|
||||
fs::create_dir_all(&dest_dir).map_err(|e| e.to_string())?;
|
||||
let (extracted_files, extract_ok) = if cfg!(target_os = "linux") {
|
||||
let bsdtar_list = std::process::Command::new("bsdtar")
|
||||
.args(["-tf", zip_tmp.to_str().unwrap()])
|
||||
.output();
|
||||
if let Ok(out) = bsdtar_list {
|
||||
if out.status.success() {
|
||||
let listing = String::from_utf8_lossy(&out.stdout);
|
||||
let files: Vec<String> = listing.lines()
|
||||
.map(|l| l.trim())
|
||||
.filter(|l| !l.is_empty() && !l.ends_with('/'))
|
||||
.map(|l| dest_dir.join(l).to_string_lossy().to_string())
|
||||
.collect();
|
||||
let st = std::process::Command::new("bsdtar")
|
||||
.args(["-xf", zip_tmp.to_str().unwrap(), "-C", dest_dir.to_str().unwrap()])
|
||||
.status()
|
||||
.map_err(|e| e.to_string())?;
|
||||
(files, st.success())
|
||||
} else {
|
||||
(Vec::new(), false)
|
||||
}
|
||||
} else {
|
||||
(Vec::new(), false)
|
||||
}
|
||||
} else {
|
||||
(Vec::new(), false)
|
||||
};
|
||||
|
||||
#[cfg(target_os = "linux")]
|
||||
{
|
||||
let status = std::process::Command::new("bsdtar")
|
||||
.args(["-xf", zip_tmp.to_str().unwrap(), "-C", dest_dir.to_str().unwrap()])
|
||||
let (extracted_files, extract_ok) = if !extract_ok {
|
||||
let unzip_list = std::process::Command::new("unzip")
|
||||
.args(["-l", zip_tmp.to_str().unwrap()])
|
||||
.output()
|
||||
.map_err(|e| e.to_string())?;
|
||||
if !unzip_list.status.success() {
|
||||
let _ = fs::remove_dir_all(&tmp_dir);
|
||||
return Err(format!("Failed to list contents of {}", zip_name));
|
||||
}
|
||||
let listing = String::from_utf8_lossy(&unzip_list.stdout);
|
||||
let files: Vec<String> = listing.lines()
|
||||
.filter_map(|l| {
|
||||
let mut parts = l.trim().split_whitespace();
|
||||
let size_str = parts.next()?;
|
||||
size_str.parse::<u64>().ok()?;
|
||||
parts.next()?;
|
||||
parts.next()?;
|
||||
Some(parts.collect::<Vec<&str>>().join(" "))
|
||||
})
|
||||
.filter(|l| !l.ends_with('/') && !l.contains('*'))
|
||||
.map(|l| dest_dir.join(l).to_string_lossy().to_string())
|
||||
.collect();
|
||||
let st = std::process::Command::new("unzip")
|
||||
.args(["-o", zip_tmp.to_str().unwrap(), "-d", dest_dir.to_str().unwrap()])
|
||||
.status()
|
||||
.map_err(|e| e.to_string())?;
|
||||
if !status.success() {
|
||||
let _ = fs::remove_dir_all(&tmp_dir);
|
||||
return Err(format!("Extraction failed for {}", zip_name));
|
||||
}
|
||||
}
|
||||
(files, st.success())
|
||||
} else {
|
||||
(extracted_files, extract_ok)
|
||||
};
|
||||
|
||||
#[cfg(not(target_os = "linux"))]
|
||||
{
|
||||
let status = std::process::Command::new("tar")
|
||||
let (extracted_files, extract_ok) = {
|
||||
let st = std::process::Command::new("tar")
|
||||
.args(["-xf", zip_tmp.to_str().unwrap(), "-C", dest_dir.to_str().unwrap()])
|
||||
.status()
|
||||
.output()
|
||||
.map_err(|e| e.to_string())?;
|
||||
if !status.success() {
|
||||
let _ = fs::remove_dir_all(&tmp_dir);
|
||||
return Err(format!("Extraction failed for {}", zip_name));
|
||||
}
|
||||
let listing = std::process::Command::new("tar")
|
||||
.args(["-tf", zip_tmp.to_str().unwrap()])
|
||||
.output()
|
||||
.map_err(|e| e.to_string())?;
|
||||
let listing_str = String::from_utf8_lossy(&listing.stdout);
|
||||
let files: Vec<String> = listing_str.lines()
|
||||
.map(|l| l.trim())
|
||||
.filter(|l| !l.is_empty() && !l.ends_with('/'))
|
||||
.map(|l| dest_dir.join(l).to_string_lossy().to_string())
|
||||
.collect();
|
||||
(files, st.status.success())
|
||||
};
|
||||
|
||||
if !extract_ok {
|
||||
let _ = fs::remove_dir_all(&tmp_dir);
|
||||
return Err(format!("Extraction failed for {}", zip_name));
|
||||
}
|
||||
|
||||
let dest_str = dest_dir.to_string_lossy().to_string();
|
||||
if !workshop_files.contains(&dest_str) {
|
||||
workshop_files.push(dest_str.clone());
|
||||
}
|
||||
if !pkg_dirs.contains(&dest_str) {
|
||||
pkg_dirs.push(dest_str);
|
||||
for f in &extracted_files {
|
||||
if !workshop_files.contains(f) {
|
||||
workshop_files.push(f.clone());
|
||||
}
|
||||
if !pkg_dirs.contains(f) {
|
||||
pkg_dirs.push(f.clone());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -116,11 +176,9 @@ pub async fn workshop_uninstall(app: AppHandle, instance_id: String, package_id:
|
||||
.unwrap_or_default();
|
||||
|
||||
if let Some(pkg) = packages.iter().find(|p| p.id == package_id) {
|
||||
for dir in &pkg.dirs {
|
||||
let path = PathBuf::from(dir);
|
||||
if path.is_dir() {
|
||||
let _ = fs::remove_dir_all(&path);
|
||||
} else if path.is_file() {
|
||||
for file in &pkg.dirs {
|
||||
let path = PathBuf::from(file);
|
||||
if path.is_file() {
|
||||
let _ = fs::remove_file(&path);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -31,6 +31,7 @@ pub fn load_config_raw(app: AppHandle) -> AppConfig {
|
||||
music_vol: Some(50),
|
||||
sfx_vol: Some(100),
|
||||
legacy_mode: Some(false),
|
||||
skip_intro: Some(false),
|
||||
mangohud_enabled: None,
|
||||
saved_servers: None,
|
||||
extra_launch_args: None,
|
||||
|
||||
@@ -26,10 +26,12 @@ use commands::workshop;
|
||||
use networking::relay;
|
||||
use networking::stun;
|
||||
use state::{DownloadState, GameState, ProxyGuard};
|
||||
fn webview_deep_link_interceptor() -> impl tauri::plugin::Plugin<tauri::Wry> { tauri::plugin::Builder::<tauri::Wry>::new("emerald-deep-link-interceptor").on_navigation(|webview, url| { if url.scheme() == "emerald" || url.scheme() == "emeraldlauncher" { let _ = webview.app_handle().emit("deep-link", vec![url.to_string()]); false } else { true }}).build()}
|
||||
#[cfg_attr(mobile, tauri::mobile_entry_point)]
|
||||
pub fn run() {
|
||||
tauri::Builder::default()
|
||||
.plugin(tauri_plugin_deep_link::init())
|
||||
.plugin(webview_deep_link_interceptor())
|
||||
.plugin(tauri_plugin_single_instance::init(|app, args, _cwd| {
|
||||
let urls: Vec<String> = args
|
||||
.iter()
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
use tauri::State;
|
||||
use tauri::webview::cookie::time::Duration;
|
||||
use tokio::io::{AsyncReadExt, AsyncWriteExt};
|
||||
use tokio::net::{TcpListener, TcpStream};
|
||||
use tokio::time::sleep;
|
||||
use tokio_util::sync::CancellationToken;
|
||||
use crate::state::ProxyGuard;
|
||||
const PROXY_ADDR: &str = "proxy.mclegacyedition.xyz:2052"; //neo: yeah bro im hardcoding it
|
||||
@@ -10,7 +12,7 @@ async fn read_line(stream: &mut TcpStream) -> Result<String, String> {
|
||||
loop {
|
||||
stream.read_exact(&mut byte).await.map_err(|e| e.to_string())?;
|
||||
if byte[0] == b'\n' { break; }
|
||||
buf.push(byte[0]);
|
||||
if byte[0] != b'\r' { buf.push(byte[0]); }
|
||||
}
|
||||
String::from_utf8(buf).map_err(|e| e.to_string())
|
||||
}
|
||||
@@ -32,18 +34,23 @@ 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 = loop {
|
||||
match TcpStream::connect(format!("127.0.0.1:{}", game_port)).await {
|
||||
Ok(s) => break s,
|
||||
Err(_) => {
|
||||
tokio::select! {
|
||||
_ = cancel.cancelled() => return Err("Cancelled".into()),
|
||||
_ = tokio::time::sleep(std::time::Duration::from_secs(1)) => {}
|
||||
let 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" {
|
||||
@@ -167,7 +174,9 @@ pub async fn start_host_relay(
|
||||
let session_id = "__host__".to_string();
|
||||
{
|
||||
let mut tokens = proxy_state.cancel_tokens.lock().await;
|
||||
tokens.insert(session_id.clone(), cancel.clone());
|
||||
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;
|
||||
@@ -205,11 +214,12 @@ pub async fn start_relay_proxy(
|
||||
#[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;
|
||||
if let Some(token) = tokens.remove(&session_id) {
|
||||
let found = tokens.remove(&session_id);
|
||||
if let Some(token) = found {
|
||||
token.cancel();
|
||||
let mut port = proxy_state.local_port.lock().await;
|
||||
*port = None;
|
||||
}
|
||||
let mut port = proxy_state.local_port.lock().await;
|
||||
*port = None;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -241,5 +251,18 @@ pub async fn join_game(
|
||||
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
|
||||
}
|
||||
|
||||
@@ -53,6 +53,7 @@ pub struct AppConfig {
|
||||
pub music_vol: Option<u32>,
|
||||
pub sfx_vol: Option<u32>,
|
||||
pub legacy_mode: Option<bool>,
|
||||
pub skip_intro: Option<bool>,
|
||||
pub mangohud_enabled: Option<bool>,
|
||||
pub saved_servers: Option<Vec<McServer>>,
|
||||
pub extra_launch_args: Option<Vec<String>>,
|
||||
|
||||
Reference in New Issue
Block a user