Use a new folder layout for updates and improve a lot of code (read desc)
Switched from a layout like this:
downloads/
1.4.0.zip (temp)
updates/
1.4.0/
lncvrt-games-launcher
to
.version
bin/
lncvrt-games-launcher
This commit is contained in:
@@ -9,11 +9,13 @@
|
|||||||
"core:default",
|
"core:default",
|
||||||
"core:window:allow-start-dragging",
|
"core:window:allow-start-dragging",
|
||||||
"fs:default",
|
"fs:default",
|
||||||
"fs:allow-applocaldata-read",
|
|
||||||
"fs:allow-applocaldata-write",
|
|
||||||
"fs:allow-exists",
|
|
||||||
"os:default",
|
"os:default",
|
||||||
"opener:default",
|
"opener:default",
|
||||||
"dialog:default"
|
"dialog:default",
|
||||||
|
"fs:allow-write-text-file",
|
||||||
|
{
|
||||||
|
"identifier": "fs:scope",
|
||||||
|
"allow": [{ "path": "$APPLOCALDATA/.version" }]
|
||||||
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
use sha2::{Digest, Sha512};
|
use sha2::{Digest, Sha512};
|
||||||
use std::{
|
use std::{
|
||||||
fs::{File, create_dir_all},
|
fs::{self, remove_dir_all},
|
||||||
io::{BufReader, copy},
|
io::Cursor,
|
||||||
path::PathBuf,
|
path::PathBuf,
|
||||||
process::Command,
|
process::Command,
|
||||||
};
|
};
|
||||||
@@ -9,37 +9,25 @@ use tauri::{AppHandle, Manager};
|
|||||||
use tauri_plugin_os::platform;
|
use tauri_plugin_os::platform;
|
||||||
use zip::ZipArchive;
|
use zip::ZipArchive;
|
||||||
|
|
||||||
#[cfg(target_os = "linux")]
|
fn unzip_to_dir(bytes: &[u8], target: &PathBuf) -> std::io::Result<()> {
|
||||||
use std::{fs, os::unix::fs::PermissionsExt};
|
let reader = Cursor::new(bytes);
|
||||||
|
let mut zip = ZipArchive::new(reader).unwrap();
|
||||||
|
|
||||||
async fn unzip_to_dir(zip_path: PathBuf, out_dir: PathBuf) -> String {
|
for i in 0..zip.len() {
|
||||||
let res = tauri::async_runtime::spawn_blocking(move || {
|
let mut file = zip.by_index(i).unwrap();
|
||||||
let file = File::open(zip_path)?;
|
let outpath = target.join(file.mangled_name());
|
||||||
let mut archive = ZipArchive::new(BufReader::new(file))?;
|
|
||||||
|
|
||||||
for i in 0..archive.len() {
|
if file.name().ends_with('/') {
|
||||||
let mut file = archive.by_index(i)?;
|
fs::create_dir_all(&outpath)?;
|
||||||
let outpath = out_dir.join(file.name());
|
} else {
|
||||||
|
if let Some(p) = outpath.parent() {
|
||||||
if file.is_dir() {
|
fs::create_dir_all(p)?;
|
||||||
create_dir_all(&outpath)?;
|
|
||||||
} else {
|
|
||||||
if let Some(parent) = outpath.parent() {
|
|
||||||
create_dir_all(parent)?;
|
|
||||||
}
|
|
||||||
let mut outfile = File::create(&outpath)?;
|
|
||||||
copy(&mut file, &mut outfile)?;
|
|
||||||
}
|
}
|
||||||
|
let mut outfile = fs::File::create(&outpath)?;
|
||||||
|
std::io::copy(&mut file, &mut outfile)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok::<(), zip::result::ZipError>(())
|
|
||||||
})
|
|
||||||
.await;
|
|
||||||
|
|
||||||
match res {
|
|
||||||
Ok(Ok(())) => "1".into(),
|
|
||||||
_ => "-1".into(),
|
|
||||||
}
|
}
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_sha512_hash(data: &[u8]) -> String {
|
fn get_sha512_hash(data: &[u8]) -> String {
|
||||||
@@ -50,20 +38,7 @@ fn get_sha512_hash(data: &[u8]) -> String {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[tauri::command]
|
#[tauri::command]
|
||||||
async fn check_latest_ver(app: AppHandle, version: String) -> String {
|
async fn download(app: AppHandle, url: String, hash: String) -> String {
|
||||||
let updates_path = app.path().app_local_data_dir().unwrap().join("updates");
|
|
||||||
if updates_path.exists()
|
|
||||||
&& updates_path.is_dir()
|
|
||||||
&& updates_path.join(&version).exists()
|
|
||||||
&& updates_path.join(&version).is_dir()
|
|
||||||
{
|
|
||||||
return "1".to_string();
|
|
||||||
}
|
|
||||||
return "-1".to_string();
|
|
||||||
}
|
|
||||||
|
|
||||||
#[tauri::command]
|
|
||||||
async fn download(app: AppHandle, url: String, name: String, hash: String) -> String {
|
|
||||||
let client = reqwest::Client::new();
|
let client = reqwest::Client::new();
|
||||||
let resp = match client.get(&url).send().await {
|
let resp = match client.get(&url).send().await {
|
||||||
Ok(r) => r,
|
Ok(r) => r,
|
||||||
@@ -79,108 +54,88 @@ async fn download(app: AppHandle, url: String, name: String, hash: String) -> St
|
|||||||
return "-2".to_string();
|
return "-2".to_string();
|
||||||
}
|
}
|
||||||
|
|
||||||
let downloads_path = app.path().app_local_data_dir().unwrap().join("downloads");
|
let bin_path = app.path().app_local_data_dir().unwrap().join("bin");
|
||||||
let updates_path = app.path().app_local_data_dir().unwrap().join("updates");
|
let _ = tokio::fs::create_dir_all(&bin_path).await;
|
||||||
|
if let Err(_) = unzip_to_dir(&bytes, &bin_path) {
|
||||||
let download_part_path = downloads_path.join(format!("{}.part", name));
|
return "-3".to_string();
|
||||||
let download_zip_path = downloads_path.join(format!("{}.zip", name));
|
|
||||||
|
|
||||||
let _ = tokio::fs::create_dir_all(&downloads_path).await;
|
|
||||||
if let Ok(true) = tokio::fs::try_exists(&updates_path.join(name.clone())).await {
|
|
||||||
let _ = tokio::fs::remove_dir_all(&updates_path.join(name.clone())).await;
|
|
||||||
}
|
|
||||||
if updates_path.exists() {
|
|
||||||
if let Ok(mut entries) = tokio::fs::read_dir(&updates_path).await {
|
|
||||||
while let Ok(Some(entry)) = entries.next_entry().await {
|
|
||||||
let _ = tokio::fs::remove_dir_all(entry.path()).await;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
let _ = tokio::fs::create_dir_all(updates_path.join(&name)).await;
|
|
||||||
}
|
|
||||||
if download_part_path.exists() {
|
|
||||||
let _ = tokio::fs::remove_file(&download_part_path).await;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if tokio::fs::write(&download_part_path, bytes).await.is_err() {
|
#[cfg(any(target_os = "linux", target_os = "macos"))]
|
||||||
return "-1".to_string();
|
|
||||||
}
|
|
||||||
|
|
||||||
if tokio::fs::rename(&download_part_path, &download_zip_path)
|
|
||||||
.await
|
|
||||||
.is_err()
|
|
||||||
{
|
{
|
||||||
return "-1".to_string();
|
use std::{fs, os::unix::fs::PermissionsExt};
|
||||||
}
|
|
||||||
|
|
||||||
let unzip_res = unzip_to_dir(download_zip_path.clone(), updates_path.join(&name)).await;
|
let executable_path = if cfg!(target_os = "linux") {
|
||||||
tokio::fs::remove_file(download_zip_path.clone())
|
bin_path.join("lncvrt-games-launcher")
|
||||||
.await
|
} else {
|
||||||
.unwrap();
|
bin_path
|
||||||
if unzip_res == "-1" {
|
.join("Lncvrt Games Launcher.app")
|
||||||
return "-1".to_string();
|
.join("Contents")
|
||||||
}
|
.join("MacOS")
|
||||||
|
.join("lncvrt-games-launcher")
|
||||||
|
};
|
||||||
|
|
||||||
#[cfg(target_os = "linux")]
|
|
||||||
{
|
|
||||||
let executable_path = updates_path.join(&name).join("lncvrt-games-launcher");
|
|
||||||
let mut perms = fs::metadata(&executable_path).unwrap().permissions();
|
let mut perms = fs::metadata(&executable_path).unwrap().permissions();
|
||||||
perms.set_mode(0o755);
|
perms.set_mode(0o755);
|
||||||
fs::set_permissions(executable_path, perms).unwrap();
|
fs::set_permissions(&executable_path, perms).unwrap();
|
||||||
}
|
}
|
||||||
#[cfg(target_os = "macos")]
|
|
||||||
{
|
|
||||||
use std::fs;
|
|
||||||
use std::os::unix::fs::PermissionsExt;
|
|
||||||
|
|
||||||
let macos_app_path = updates_path
|
|
||||||
.join(&name)
|
|
||||||
.join("Lncvrt Games Launcher.app")
|
|
||||||
.join("Contents")
|
|
||||||
.join("MacOS")
|
|
||||||
.join("lncvrt-games-launcher");
|
|
||||||
|
|
||||||
let mut perms = fs::metadata(&macos_app_path).unwrap().permissions();
|
|
||||||
perms.set_mode(0o755);
|
|
||||||
fs::set_permissions(&macos_app_path, perms).unwrap();
|
|
||||||
}
|
|
||||||
return "1".to_string();
|
return "1".to_string();
|
||||||
}
|
}
|
||||||
|
|
||||||
#[allow(unused_variables)]
|
#[allow(unused_variables)]
|
||||||
#[tauri::command]
|
#[tauri::command]
|
||||||
fn load(app: AppHandle, name: String) {
|
fn load(app: AppHandle) {
|
||||||
let update_path = app
|
let bin_path = app.path().app_local_data_dir().unwrap().join("bin");
|
||||||
.path()
|
if !bin_path.exists() {
|
||||||
.app_local_data_dir()
|
|
||||||
.unwrap()
|
|
||||||
.join("updates")
|
|
||||||
.join(&name);
|
|
||||||
if !update_path.exists() {
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if platform() == "macos" {
|
if platform() == "macos" {
|
||||||
Command::new("open")
|
Command::new("open")
|
||||||
.arg("Lncvrt Games Launcher.app")
|
.arg("Lncvrt Games Launcher.app")
|
||||||
.current_dir(&update_path)
|
.current_dir(&bin_path)
|
||||||
.spawn()
|
.spawn()
|
||||||
.unwrap();
|
.unwrap();
|
||||||
} else if platform() == "linux" {
|
} else if platform() == "linux" {
|
||||||
Command::new("./lncvrt-games-launcher")
|
Command::new("./lncvrt-games-launcher")
|
||||||
.current_dir(&update_path)
|
.current_dir(&bin_path)
|
||||||
.spawn()
|
.spawn()
|
||||||
.unwrap();
|
.unwrap();
|
||||||
} else if platform() == "windows" {
|
} else if platform() == "windows" {
|
||||||
Command::new(&update_path.join("lncvrt-games-launcher.exe"))
|
Command::new(&bin_path.join("lncvrt-games-launcher.exe"))
|
||||||
.current_dir(&update_path)
|
.current_dir(&bin_path)
|
||||||
.spawn()
|
.spawn()
|
||||||
.unwrap();
|
.unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
app.exit(0);
|
app.exit(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg_attr(mobile, tauri::mobile_entry_point)]
|
#[cfg_attr(mobile, tauri::mobile_entry_point)]
|
||||||
pub fn run() {
|
pub fn run() {
|
||||||
tauri::Builder::default()
|
tauri::Builder::default()
|
||||||
|
.setup(|app| {
|
||||||
|
let app_local_data_dir = app.path().app_local_data_dir().unwrap();
|
||||||
|
let downloads_dir = app_local_data_dir.join("downloads");
|
||||||
|
let updates_dir = app_local_data_dir.join("updates");
|
||||||
|
let bin_dir = app_local_data_dir.join("bin");
|
||||||
|
let version_file = app_local_data_dir.join(".version");
|
||||||
|
|
||||||
|
if downloads_dir.exists() {
|
||||||
|
let _ = remove_dir_all(downloads_dir);
|
||||||
|
}
|
||||||
|
if updates_dir.exists() {
|
||||||
|
let _ = remove_dir_all(&updates_dir);
|
||||||
|
}
|
||||||
|
if bin_dir.exists() && bin_dir.is_file() {
|
||||||
|
let _ = remove_dir_all(&bin_dir);
|
||||||
|
}
|
||||||
|
if version_file.exists() && !bin_dir.is_file() {
|
||||||
|
let _ = remove_dir_all(&version_file);
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
})
|
||||||
.plugin(tauri_plugin_single_instance::init(|app, _args, _cwd| {
|
.plugin(tauri_plugin_single_instance::init(|app, _args, _cwd| {
|
||||||
let _ = app
|
let _ = app
|
||||||
.get_webview_window("main")
|
.get_webview_window("main")
|
||||||
@@ -191,7 +146,7 @@ pub fn run() {
|
|||||||
.plugin(tauri_plugin_opener::init())
|
.plugin(tauri_plugin_opener::init())
|
||||||
.plugin(tauri_plugin_os::init())
|
.plugin(tauri_plugin_os::init())
|
||||||
.plugin(tauri_plugin_fs::init())
|
.plugin(tauri_plugin_fs::init())
|
||||||
.invoke_handler(tauri::generate_handler![check_latest_ver, download, load])
|
.invoke_handler(tauri::generate_handler![download, load])
|
||||||
.run(tauri::generate_context!())
|
.run(tauri::generate_context!())
|
||||||
.expect("error while running tauri application");
|
.expect("error while running tauri application");
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,6 +9,13 @@ import { invoke } from '@tauri-apps/api/core'
|
|||||||
import { LauncherUpdate } from './types/LauncherUpdate'
|
import { LauncherUpdate } from './types/LauncherUpdate'
|
||||||
import { openUrl } from '@tauri-apps/plugin-opener'
|
import { openUrl } from '@tauri-apps/plugin-opener'
|
||||||
import { arch, platform } from '@tauri-apps/plugin-os'
|
import { arch, platform } from '@tauri-apps/plugin-os'
|
||||||
|
import {
|
||||||
|
BaseDirectory,
|
||||||
|
create,
|
||||||
|
exists,
|
||||||
|
readTextFile,
|
||||||
|
writeTextFile
|
||||||
|
} from '@tauri-apps/plugin-fs'
|
||||||
|
|
||||||
export default function Home () {
|
export default function Home () {
|
||||||
const [state, setState] = useState<string>('Loading...')
|
const [state, setState] = useState<string>('Loading...')
|
||||||
@@ -47,12 +54,17 @@ export default function Home () {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
const isLatest = await invoke('check_latest_ver', {
|
const options = {
|
||||||
version: launcherLatestRequest.data
|
baseDir: BaseDirectory.AppLocalData
|
||||||
})
|
}
|
||||||
if (isLatest == '1') {
|
|
||||||
setState('Starting...')
|
let latest = false
|
||||||
} else {
|
if (await exists('.version', options))
|
||||||
|
latest =
|
||||||
|
(await readTextFile('.version', options)) ==
|
||||||
|
launcherLatestRequest.data
|
||||||
|
|
||||||
|
if (!latest) {
|
||||||
setState('Downloading new update...')
|
setState('Downloading new update...')
|
||||||
try {
|
try {
|
||||||
const launcherUpdateRequest = await axios.get(
|
const launcherUpdateRequest = await axios.get(
|
||||||
@@ -63,10 +75,12 @@ export default function Home () {
|
|||||||
setState('Failed. Check internet connection.')
|
setState('Failed. Check internet connection.')
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if (!launcherUpdateData) return
|
if (!launcherUpdateData) {
|
||||||
|
setState('Failed. Check internet connection.')
|
||||||
|
return
|
||||||
|
}
|
||||||
const downloadResult = await invoke('download', {
|
const downloadResult = await invoke('download', {
|
||||||
url: launcherUpdateData.downloadUrl,
|
url: launcherUpdateData.downloadUrl,
|
||||||
name: launcherLatestRequest.data,
|
|
||||||
hash: launcherUpdateData.sha512sum
|
hash: launcherUpdateData.sha512sum
|
||||||
})
|
})
|
||||||
if (downloadResult == '-1') {
|
if (downloadResult == '-1') {
|
||||||
@@ -75,13 +89,16 @@ export default function Home () {
|
|||||||
} else if (downloadResult == '-2') {
|
} else if (downloadResult == '-2') {
|
||||||
setState('File integrity check failed.')
|
setState('File integrity check failed.')
|
||||||
return
|
return
|
||||||
|
} else if (downloadResult == '-3') {
|
||||||
|
setState('Failed to unzip update.')
|
||||||
|
return
|
||||||
}
|
}
|
||||||
setState('Starting...')
|
if (await exists('.version', options)) await create('.version', options)
|
||||||
|
await writeTextFile('.version', launcherLatestRequest.data, options)
|
||||||
}
|
}
|
||||||
|
|
||||||
invoke('load', {
|
setState('Starting...')
|
||||||
name: launcherLatestRequest.data
|
invoke('load')
|
||||||
})
|
|
||||||
})()
|
})()
|
||||||
}, [])
|
}, [])
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user