1.6.0 drop 1 (#132)

This commit is contained in:
/home/neo
2026-07-12 20:07:52 +03:00
committed by GitHub
parent 0594d330be
commit 0a32333019
14 changed files with 207 additions and 144 deletions
+85 -27
View File
@@ -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);
}
}
+1
View File
@@ -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,
+2
View File
@@ -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()
+37 -14
View File
@@ -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
}
+1
View File
@@ -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>>,