Compare commits
No commits in common. "master" and "0.0.3" have entirely different histories.
@ -1,14 +0,0 @@
|
|||||||
{
|
|
||||||
"version": "1.0.3",
|
|
||||||
"notes": "Removed test text",
|
|
||||||
"platforms": {
|
|
||||||
"linux-x86_64": {
|
|
||||||
"signature": "dW50cnVzdGVkIGNvbW1lbnQ6IHNpZ25hdHVyZSBmcm9tIHRhdXJpIHNlY3JldCBrZXkKUlVTZlMrWWVKdURJQTZvNXR1WTFXSSs4bm1KbkpTeUhDMG9aeThZVDZEVGRCTlJSbFRGMXpZeFlOV085ZThFL0xNTmZjUmk5MUNWSGVIcUxaZnM4bzk0RjJnRDhGMk82bXdNPQp0cnVzdGVkIGNvbW1lbnQ6IHRpbWVzdGFtcDoxNzIwODg5ODQ4CWZpbGU6ZmMtbGF1bmNoZXJfMS4wLjNfYW1kNjQuQXBwSW1hZ2UudGFyLmd6CkViSUNFWmNmZVJiQVR1MTBCUHptc3VxeVV4V3daK0tCUnBPc21GdlBUdlVFMXNLWmFSeUJ4V0ZPN2ZoUXdMRjBzZ3NUTjExSFFVV2QrOGxXQmZKSURRPT0K",
|
|
||||||
"url": "https://gitea.piwalker.net/fclauncher/app/linux/fc-launcher_1.0.3_amd64.AppImage.tar.gz"
|
|
||||||
},
|
|
||||||
"windows-x86_64": {
|
|
||||||
"signature": "dW50cnVzdGVkIGNvbW1lbnQ6IHNpZ25hdHVyZSBmcm9tIHRhdXJpIHNlY3JldCBrZXkKUlVTZlMrWWVKdURJQThTaEpZYjNicThPbVFzbXlwYlJQanBWZGhncEtCVEkvc21SLzcrS0NuZFozdDM5d01lOCtYUElyb0dYQmpPelF0WDF5R1IyN3RZVU1BaDFRWkFneFFJPQp0cnVzdGVkIGNvbW1lbnQ6IHRpbWVzdGFtcDoxNzIwODg5NjMwCWZpbGU6RkNMYXVuY2hlcl8xLjAuM194NjRfZW4tVVMubXNpLnppcApTOXQvQlZWV2VYS2ZjYmIwSWJobzIxYjh1TWhyNHlFS21BVU11RVVud3dUQmMzVldUSlh2VURob0s2T3dUM1ZtcHJrZ2VvYXhoZFlZWmJ2OEJmYlpDUT09Cg==",
|
|
||||||
"url": "https://gitea.piwalker.net/fclauncher/app/windows/FCLauncher_1.0.3_x64_en-US.msi.zip"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "fclauncher"
|
name = "fclauncher"
|
||||||
version = "0.0.5"
|
version = "0.0.3"
|
||||||
description = "Launcher for Familycraft"
|
description = "Launcher for Familycraft"
|
||||||
authors = ["Samuel Walker", "Benjamin Walker"]
|
authors = ["Samuel Walker", "Benjamin Walker"]
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
@ -8,10 +8,10 @@ edition = "2021"
|
|||||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||||
|
|
||||||
[build-dependencies]
|
[build-dependencies]
|
||||||
tauri-build = { version = "2", features = [] }
|
tauri-build = { version = "1", features = [] }
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
tauri = { version = "2", features = [] }
|
tauri = { version = "1", features = [ "dialog-ask", "shell-open"] }
|
||||||
serde = { version = "1", features = ["derive"] }
|
serde = { version = "1", features = ["derive"] }
|
||||||
serde_json = "1"
|
serde_json = "1"
|
||||||
suppaftp = { version = "6.0.1", features = ["native-tls"] }
|
suppaftp = { version = "6.0.1", features = ["native-tls"] }
|
||||||
@ -21,19 +21,11 @@ zip-extract = "0.1.3"
|
|||||||
dirs = "5.0.1"
|
dirs = "5.0.1"
|
||||||
gethostname = "0.4.3"
|
gethostname = "0.4.3"
|
||||||
self_update = "0.40.0"
|
self_update = "0.40.0"
|
||||||
parking_lot = "0.12.3"
|
|
||||||
reqwest = { version = "0.12.5", features = ["stream"] }
|
|
||||||
futures-util = "0.3.30"
|
|
||||||
ssh2 = "0.9.4"
|
|
||||||
chrono = "0.4.38"
|
|
||||||
zip = "2.1.3"
|
|
||||||
tauri-plugin-dialog = "2"
|
|
||||||
tauri-plugin-shell = "2"
|
|
||||||
tauri-plugin-process = "2"
|
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
# This feature is used for production builds or when a dev server is not specified, DO NOT REMOVE!!
|
# This feature is used for production builds or when a dev server is not specified, DO NOT REMOVE!!
|
||||||
custom-protocol = ["tauri/custom-protocol"]
|
custom-protocol = ["tauri/custom-protocol"]
|
||||||
|
|
||||||
[target.'cfg(not(any(target_os = "android", target_os = "ios")))'.dependencies]
|
[[bin]]
|
||||||
tauri-plugin-updater = "2"
|
name = "FCLauncher"
|
||||||
|
path = "src/main.rs"
|
||||||
|
@ -1,4 +0,0 @@
|
|||||||
export TAURI_PRIVATE_KEY=$(cat ~/.tauri/fclauncher.key)
|
|
||||||
read -s PASSWORD
|
|
||||||
export TAURI_KEY_PASSWORD=$PASSWORD
|
|
||||||
NO_STRIP=true cargo tauri build
|
|
@ -1,11 +0,0 @@
|
|||||||
{
|
|
||||||
"identifier": "desktop-capability",
|
|
||||||
"platforms": [
|
|
||||||
"macOS",
|
|
||||||
"windows",
|
|
||||||
"linux"
|
|
||||||
],
|
|
||||||
"permissions": [
|
|
||||||
"updater:default"
|
|
||||||
]
|
|
||||||
}
|
|
@ -1,20 +0,0 @@
|
|||||||
{
|
|
||||||
"identifier": "migrated",
|
|
||||||
"description": "permissions that were migrated from v1",
|
|
||||||
"local": true,
|
|
||||||
"windows": [
|
|
||||||
"main"
|
|
||||||
],
|
|
||||||
"permissions": [
|
|
||||||
"core:default",
|
|
||||||
"shell:allow-open",
|
|
||||||
"dialog:allow-open",
|
|
||||||
"dialog:allow-message",
|
|
||||||
"dialog:allow-ask",
|
|
||||||
"process:allow-restart",
|
|
||||||
"process:allow-exit",
|
|
||||||
"dialog:default",
|
|
||||||
"shell:default",
|
|
||||||
"process:default"
|
|
||||||
]
|
|
||||||
}
|
|
@ -1,295 +0,0 @@
|
|||||||
use std::fs::File;
|
|
||||||
use std::io::Cursor;
|
|
||||||
use std::io::Read;
|
|
||||||
use std::io::Seek;
|
|
||||||
use std::io::Write;
|
|
||||||
use std::path::Path;
|
|
||||||
use std::path::PathBuf;
|
|
||||||
|
|
||||||
use crate::modpack;
|
|
||||||
use crate::modpack::get_modpacks;
|
|
||||||
use crate::modpack::get_versions;
|
|
||||||
use crate::modpack::VersionEntry;
|
|
||||||
use crate::sftp;
|
|
||||||
use chrono;
|
|
||||||
use serde::Serialize;
|
|
||||||
use ssh2::Session;
|
|
||||||
use zip::write::SimpleFileOptions;
|
|
||||||
use zip::ZipArchive;
|
|
||||||
use zip::ZipWriter;
|
|
||||||
use tauri::Emitter;
|
|
||||||
|
|
||||||
//static USERNAME: parking_lot::Mutex<String> = parking_lot::const_mutex(String::new());
|
|
||||||
//static PASSWORD: parking_lot::Mutex<String> = parking_lot::const_mutex(String::new());
|
|
||||||
static SESSION: tauri::async_runtime::Mutex<Option<Session>> =
|
|
||||||
tauri::async_runtime::Mutex::const_new(None);
|
|
||||||
|
|
||||||
pub fn emit(event: &str, payload: impl Serialize + Clone, window: tauri::AppHandle) {
|
|
||||||
if !window.emit(event, payload).is_ok() {
|
|
||||||
println!("Failed to emit to window!");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[tauri::command]
|
|
||||||
pub async fn login(username: String, password: String, window: tauri::AppHandle) {
|
|
||||||
let res = sftp::connect(username, password);
|
|
||||||
if res.is_ok() {
|
|
||||||
//*USERNAME.lock() = username;
|
|
||||||
//*PASSWORD.lock() = password;
|
|
||||||
*SESSION.lock().await = Some(res.unwrap());
|
|
||||||
emit("Login_Success", {}, window);
|
|
||||||
} else {
|
|
||||||
emit("Login_Failed", {}, window);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[tauri::command]
|
|
||||||
pub async fn drop_session() {
|
|
||||||
let ref mut session = *SESSION.lock().await;
|
|
||||||
if let Some(session) = session {
|
|
||||||
session.disconnect(None, "disconnecting", None).unwrap();
|
|
||||||
}
|
|
||||||
*session = None;
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn update_modpacks(modpacks: Vec<modpack::ModpackEntry>) -> Result<(), String> {
|
|
||||||
let data = serde_json::to_string(&modpacks).or(Err("Unable to serialize json"))?;
|
|
||||||
let reader = Cursor::new(data.as_bytes());
|
|
||||||
let ref mut session = *SESSION.lock().await;
|
|
||||||
if let Some(session) = session {
|
|
||||||
sftp::uplaod(
|
|
||||||
None,
|
|
||||||
session.clone(),
|
|
||||||
PathBuf::from("/ftp/modpacks.json"),
|
|
||||||
reader,
|
|
||||||
format!("modpacks.json"),
|
|
||||||
data.as_bytes().len(),
|
|
||||||
)
|
|
||||||
.await?;
|
|
||||||
}
|
|
||||||
Err(format!("Session doesnt exist?"))
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn update_versions(id: String, versions: Vec<modpack::VersionEntry>) -> Result<(), String> {
|
|
||||||
let data = serde_json::to_string(&versions).or(Err("Unable to serialize json"))?;
|
|
||||||
let reader = Cursor::new(data.as_bytes());
|
|
||||||
let ref mut session = *SESSION.lock().await;
|
|
||||||
if let Some(session) = session {
|
|
||||||
sftp::uplaod(
|
|
||||||
None,
|
|
||||||
session.clone(),
|
|
||||||
PathBuf::from(format!("/ftp/{}/versions.json", id)),
|
|
||||||
reader,
|
|
||||||
format!("modpacks.json"),
|
|
||||||
data.as_bytes().len(),
|
|
||||||
)
|
|
||||||
.await?;
|
|
||||||
}
|
|
||||||
Err(format!("Session doesnt exist?"))
|
|
||||||
}
|
|
||||||
|
|
||||||
#[tauri::command]
|
|
||||||
pub async fn shift_up(id: String, window: tauri::AppHandle) {
|
|
||||||
let mut modpacks = modpack::get_modpacks().await;
|
|
||||||
let mut index = 0;
|
|
||||||
for pack in modpacks.as_slice() {
|
|
||||||
if pack.id == id {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
index += 1;
|
|
||||||
}
|
|
||||||
if index != 0 {
|
|
||||||
modpacks.swap(index, index - 1);
|
|
||||||
}
|
|
||||||
let res = update_modpacks(modpacks).await;
|
|
||||||
if !res.is_ok() {
|
|
||||||
emit("Error", res.unwrap_err(), window);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[tauri::command]
|
|
||||||
pub async fn shift_down(id: String, window: tauri::AppHandle) {
|
|
||||||
let mut modpacks = modpack::get_modpacks().await;
|
|
||||||
let mut index = 0;
|
|
||||||
for pack in modpacks.as_slice() {
|
|
||||||
if pack.id == id {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
index += 1;
|
|
||||||
}
|
|
||||||
if index != modpacks.len() - 1 {
|
|
||||||
modpacks.swap(index, index + 1);
|
|
||||||
}
|
|
||||||
let res = update_modpacks(modpacks).await;
|
|
||||||
if !res.is_ok() {
|
|
||||||
emit("Error", res.unwrap_err(), window);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[tauri::command]
|
|
||||||
pub async fn add_pack(id: String, name: String, window: tauri::AppHandle) {
|
|
||||||
{
|
|
||||||
let ref mut session = *SESSION.lock().await;
|
|
||||||
if let Some(session) = session {
|
|
||||||
let res = sftp::mkdir(session.clone(), PathBuf::from(format!("/ftp/{}", id))).await;
|
|
||||||
if !res.is_ok() {
|
|
||||||
emit("Error", res.unwrap_err(), window.clone());
|
|
||||||
}
|
|
||||||
let res = sftp::mkdir(
|
|
||||||
session.clone(),
|
|
||||||
PathBuf::from(format!("/ftp/{}/Versions", id)),
|
|
||||||
)
|
|
||||||
.await;
|
|
||||||
if !res.is_ok() {
|
|
||||||
emit("Error", res.unwrap_err(), window.clone());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
let versions: Vec<VersionEntry> = Vec::new();
|
|
||||||
let res = update_versions(id.clone(), versions).await;
|
|
||||||
if !res.is_ok() {
|
|
||||||
emit("Error", res.unwrap_err(), window.clone());
|
|
||||||
}
|
|
||||||
let mut modpacks = get_modpacks().await;
|
|
||||||
modpacks.push(modpack::ModpackEntry {
|
|
||||||
id: id,
|
|
||||||
name: name,
|
|
||||||
last_updated: format!("{:?}", chrono::offset::Utc::now()),
|
|
||||||
});
|
|
||||||
let res = update_modpacks(modpacks).await;
|
|
||||||
if !res.is_ok() {
|
|
||||||
emit("Error", res.unwrap_err(), window);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[tauri::command]
|
|
||||||
pub async fn remove_pack(id: String, window: tauri::AppHandle) {
|
|
||||||
let mut modpacks = get_modpacks().await;
|
|
||||||
let mut index = 0;
|
|
||||||
for pack in modpacks.clone() {
|
|
||||||
if pack.id == id {
|
|
||||||
modpacks.remove(index);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
index += 1;
|
|
||||||
}
|
|
||||||
let res = update_modpacks(modpacks).await;
|
|
||||||
if !res.is_ok() {
|
|
||||||
emit("Error", res.unwrap_err(), window);
|
|
||||||
}
|
|
||||||
{
|
|
||||||
let ref mut session = *SESSION.lock().await;
|
|
||||||
if let Some(session) = session {
|
|
||||||
sftp::rmdir(session.clone(), PathBuf::from(format!("/ftp/{}", id))).await;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[tauri::command]
|
|
||||||
pub async fn update_pack(
|
|
||||||
window: tauri::AppHandle,
|
|
||||||
id: String,
|
|
||||||
path: String,
|
|
||||||
version: String,
|
|
||||||
) -> Result<(), String> {
|
|
||||||
println!(
|
|
||||||
"Update modpack {}, to version {}, from file {}",
|
|
||||||
id, version, path
|
|
||||||
);
|
|
||||||
let file = File::open(Path::new(path.as_str())).or(Err(format!("Unable to open file")))?;
|
|
||||||
let mut archive = ZipArchive::new(file).or(Err(format!("File not a zip archive!")))?;
|
|
||||||
let mut buf = Cursor::new(vec![]);
|
|
||||||
let mut out_archive = ZipWriter::new(&mut buf);
|
|
||||||
|
|
||||||
for i in 0..archive.len() {
|
|
||||||
let mut file = archive
|
|
||||||
.by_index(i)
|
|
||||||
.or(Err(format!("error reading archive")))?;
|
|
||||||
if file.name() == "overrides/version.txt" {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
let res = out_archive.start_file(
|
|
||||||
file.name(),
|
|
||||||
SimpleFileOptions::default().compression_method(zip::CompressionMethod::Deflated),
|
|
||||||
);
|
|
||||||
if !res.is_ok() {
|
|
||||||
emit("Error", format!("Unable to start zip archive"), window);
|
|
||||||
return Err(format!("Unable to start zip archive"));
|
|
||||||
}
|
|
||||||
let res = std::io::copy(&mut file, &mut out_archive);
|
|
||||||
if !res.is_ok() {
|
|
||||||
emit("Error", format!("Unable to copy archive to ram"), window);
|
|
||||||
return Err(format!("Unable to copy archive to ram"));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
let res = out_archive.start_file(
|
|
||||||
"overrides/version.txt",
|
|
||||||
SimpleFileOptions::default().compression_method(zip::CompressionMethod::Deflated),
|
|
||||||
);
|
|
||||||
if !res.is_ok() {
|
|
||||||
emit("Error", format!("Unable to create version file"), window);
|
|
||||||
return Err(format!("Unable to create version file"));
|
|
||||||
}
|
|
||||||
let res = out_archive.write_all(version.as_bytes());
|
|
||||||
if !res.is_ok() {
|
|
||||||
emit("Error", format!("Unable to write to zip"), window);
|
|
||||||
return Err(format!("Unable to write to zip"));
|
|
||||||
}
|
|
||||||
let res = out_archive.finish();
|
|
||||||
if !res.is_ok() {
|
|
||||||
emit("Error", format!("Unable to finish zip"), window);
|
|
||||||
return Err(format!("Unable to finish zip"));
|
|
||||||
}
|
|
||||||
buf.rewind().unwrap();
|
|
||||||
let timestamp = format!("{:?}", chrono::offset::Utc::now());
|
|
||||||
let path = format!("Versions/{}-{}.mrpack", id, timestamp);
|
|
||||||
{
|
|
||||||
let ref mut session = *SESSION.lock().await;
|
|
||||||
if let Some(session) = session {
|
|
||||||
let size = buf.clone().bytes().count();
|
|
||||||
let upload_path = format!("/ftp/{}/{}", id, path.clone());
|
|
||||||
println!("Uploading to {}", upload_path);
|
|
||||||
let res = sftp::uplaod(
|
|
||||||
Some(window.clone()),
|
|
||||||
session.clone(),
|
|
||||||
PathBuf::from(upload_path),
|
|
||||||
&mut buf,
|
|
||||||
path.clone(),
|
|
||||||
size,
|
|
||||||
)
|
|
||||||
.await;
|
|
||||||
if !res.is_ok() {
|
|
||||||
emit("Error", res.clone().unwrap_err(), window.clone());
|
|
||||||
return res;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let mut versions = get_versions(id.clone()).await?;
|
|
||||||
versions.push(VersionEntry {
|
|
||||||
Version: version,
|
|
||||||
Date: timestamp.clone(),
|
|
||||||
File: path,
|
|
||||||
});
|
|
||||||
let res = update_versions(id.clone(), versions).await;
|
|
||||||
if !res.is_ok() {
|
|
||||||
emit("Error", res.clone().unwrap_err(), window.clone());
|
|
||||||
return res;
|
|
||||||
}
|
|
||||||
let mut modpacks = get_modpacks().await;
|
|
||||||
let mut index = 0;
|
|
||||||
for pack in modpacks.as_slice() {
|
|
||||||
if pack.id == id {
|
|
||||||
modpacks[index].last_updated = timestamp;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
index += 1;
|
|
||||||
}
|
|
||||||
let res = update_modpacks(modpacks).await;
|
|
||||||
if !res.is_ok() {
|
|
||||||
emit("Error", res.clone().unwrap_err(), window.clone());
|
|
||||||
return res;
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
@ -12,25 +12,20 @@ fn ftp_connection_anonymous() -> Result<NativeTlsFtpStream, FtpError>{
|
|||||||
ftp_connection("anonymous", "anonymous@")
|
ftp_connection("anonymous", "anonymous@")
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn test_cred(username: &str, password: &str) -> bool{
|
|
||||||
return ftp_connection(username, password).is_ok();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
fn ftp_connection(username: &str, password: &str) -> Result<NativeTlsFtpStream, FtpError>{
|
fn ftp_connection(username: &str, password: &str) -> Result<NativeTlsFtpStream, FtpError>{
|
||||||
let ftp_stream = NativeTlsFtpStream::connect("gitea.piwalker.net:21")?;
|
let ftp_stream = NativeTlsFtpStream::connect("gitea.piwalker.net:21").unwrap_or_else(|err|
|
||||||
|
panic!("{}", err)
|
||||||
|
);
|
||||||
let cert = include_bytes!("../res/vsftpd.crt");
|
let cert = include_bytes!("../res/vsftpd.crt");
|
||||||
let cert = Certificate::from_pem(cert).unwrap();
|
let cert = Certificate::from_pem(cert).unwrap();
|
||||||
let mut ftp_stream = ftp_stream.into_secure(NativeTlsConnector::from(TlsConnector::builder().add_root_certificate(cert).build().unwrap()), "gitea.piwalker.net").unwrap();
|
let mut ftp_stream = ftp_stream.into_secure(NativeTlsConnector::from(TlsConnector::builder().add_root_certificate(cert).build().unwrap()), "gitea.piwalker.net").unwrap();
|
||||||
let result = ftp_stream.login(username, password);
|
ftp_stream.login("anonymous", "anonymous@").map(|_| Ok(ftp_stream)).unwrap()
|
||||||
if result.is_ok() {
|
|
||||||
return Ok(ftp_stream);
|
|
||||||
}
|
|
||||||
Err(result.unwrap_err())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
pub fn ftp_retr(window: Option<tauri::Window>, file: PathBuf , mut writer: impl Write, mut callback: impl FnMut(Option<tauri::Window>, usize, usize)) -> Result<bool, FtpError> {
|
pub fn ftp_retr(window: Option<tauri::Window>, file: PathBuf , mut writer: impl Write, mut callback: impl FnMut(Option<tauri::Window>, usize, usize)) -> Result<bool, FtpError> {
|
||||||
let mut ftp_stream = ftp_connection_anonymous()?;
|
let mut ftp_stream = ftp_connection_anonymous().unwrap();
|
||||||
let file = file.to_str().unwrap().replace("\\", "/");
|
let file = file.to_str().unwrap().replace("\\", "/");
|
||||||
let size = ftp_stream.size(&file)?;
|
let size = ftp_stream.size(&file)?;
|
||||||
let mut total = 0;
|
let mut total = 0;
|
||||||
|
@ -1,67 +0,0 @@
|
|||||||
use futures_util::StreamExt;
|
|
||||||
use serde::Serialize;
|
|
||||||
use std::cmp::min;
|
|
||||||
use std::io::Write;
|
|
||||||
use std::path::PathBuf;
|
|
||||||
use tauri::Emitter;
|
|
||||||
|
|
||||||
#[derive(Clone, Serialize)]
|
|
||||||
pub struct DownloadStatus {
|
|
||||||
pub downloaded: usize,
|
|
||||||
pub total: usize,
|
|
||||||
pub time_elapsed: usize,
|
|
||||||
pub download_name: String,
|
|
||||||
}
|
|
||||||
|
|
||||||
pub async fn download(
|
|
||||||
window: Option<tauri::AppHandle>,
|
|
||||||
url: String,
|
|
||||||
mut writer: impl Write,
|
|
||||||
downloadName: String,
|
|
||||||
) -> Result<(), String> {
|
|
||||||
let client = reqwest::Client::new();
|
|
||||||
let res = client
|
|
||||||
.get(url)
|
|
||||||
.send()
|
|
||||||
.await
|
|
||||||
.or(Err(format!("Failed to fetch from URL!")))?;
|
|
||||||
let total_size = res
|
|
||||||
.content_length()
|
|
||||||
.ok_or(format!("Failed to get content length"))?;
|
|
||||||
let mut downloaded: u64 = 0;
|
|
||||||
let mut stream = res.bytes_stream();
|
|
||||||
while let Some(item) = stream.next().await {
|
|
||||||
let chunk = item.or(Err(format!("Error while downloading file!")))?;
|
|
||||||
writer
|
|
||||||
.write_all(&chunk)
|
|
||||||
.or(Err("Error writing to stream!"))?;
|
|
||||||
let new = min(downloaded + (chunk.len() as u64), total_size);
|
|
||||||
downloaded = new;
|
|
||||||
println!(
|
|
||||||
"Downloading {}: {}MB / {}MB",
|
|
||||||
downloadName.clone(),
|
|
||||||
downloaded / (1024 * 1024),
|
|
||||||
total_size / (1024 * 1024)
|
|
||||||
);
|
|
||||||
if let Some(window) = window.clone() {
|
|
||||||
if downloaded != total_size {
|
|
||||||
window
|
|
||||||
.emit(
|
|
||||||
"download_progress",
|
|
||||||
DownloadStatus {
|
|
||||||
downloaded: downloaded as usize,
|
|
||||||
total: total_size as usize,
|
|
||||||
time_elapsed: 0,
|
|
||||||
download_name: downloadName.clone(),
|
|
||||||
},
|
|
||||||
)
|
|
||||||
.or(Err(format!("Unable to signal window")))?;
|
|
||||||
} else {
|
|
||||||
window
|
|
||||||
.emit("download_finished", true)
|
|
||||||
.or(Err(format!("Unable to signal window!")))?;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return Ok(());
|
|
||||||
}
|
|
@ -1,102 +1,44 @@
|
|||||||
use flate2::read::GzDecoder;
|
|
||||||
use std::env;
|
use std::env;
|
||||||
use std::io::{Cursor, Read, Seek};
|
use std::io::{Cursor, Seek, Read};
|
||||||
use std::path::{Components, Path, PathBuf};
|
use std::path::{Components, Path, PathBuf};
|
||||||
|
use flate2::read::GzDecoder;
|
||||||
use tar::Archive;
|
use tar::Archive;
|
||||||
|
|
||||||
use crate::https;
|
|
||||||
use crate::system_dirs::get_local_data_directory;
|
use crate::system_dirs::get_local_data_directory;
|
||||||
|
use crate::ftp::{self, ftp_get_size};
|
||||||
use crate::util;
|
use crate::util;
|
||||||
|
|
||||||
fn check_java(version: u8) -> bool {
|
fn check_java(version: u8) -> bool {
|
||||||
let dir = get_local_data_directory().join("java").join(format!(
|
let dir = get_local_data_directory().join("java").join(format!("java-{}-{}", version, if env::consts::OS == "windows" { "win" } else {"lin"}));
|
||||||
"java-{}-{}",
|
|
||||||
version,
|
|
||||||
if env::consts::OS == "windows" {
|
|
||||||
"win"
|
|
||||||
} else {
|
|
||||||
"lin"
|
|
||||||
}
|
|
||||||
));
|
|
||||||
dir.exists()
|
dir.exists()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn install_java(version: u8, window: tauri::AppHandle) {
|
pub fn install_java(version: u8, window: tauri::Window) {
|
||||||
if check_java(version) {
|
if check_java(version) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
//let ftp_dir = PathBuf::new().join("java").join(format!("java-{}-{}", version, if env::consts::OS == "windows" { "win.zip" } else {"lin.tar.gz"}));
|
let ftp_dir = PathBuf::new().join("java").join(format!("java-{}-{}", version, if env::consts::OS == "windows" { "win.zip" } else {"lin.tar.gz"}));
|
||||||
let mut buff = Cursor::new(vec![]);
|
let mut buff = Cursor::new(vec![]);
|
||||||
//ftp::ftp_retr(Some(window), ftp_dir, &mut buff, |window, data, size| util::download_callback(format!("Java {}", version), window,data, size)).unwrap();
|
ftp::ftp_retr(Some(window), ftp_dir, &mut buff, |window, data, size| util::download_callback(format!("Java {}", version), window,data, size)).unwrap();
|
||||||
https::download(
|
|
||||||
Some(window.clone()),
|
std::fs::create_dir_all(get_local_data_directory().join("java").join(format!("java-{}-{}", version, if env::consts::OS == "windows" { "win" } else {"lin"}))).unwrap();
|
||||||
format!(
|
|
||||||
"https://gitea.piwalker.net/fclauncher/java/java-{}-{}",
|
|
||||||
version,
|
|
||||||
if env::consts::OS == "windows" {
|
|
||||||
"win.zip"
|
|
||||||
} else {
|
|
||||||
"lin.tar.gz"
|
|
||||||
}
|
|
||||||
),
|
|
||||||
&mut buff,
|
|
||||||
format!("Java {}", version),
|
|
||||||
)
|
|
||||||
.await;
|
|
||||||
std::fs::create_dir_all(get_local_data_directory().join("java").join(format!(
|
|
||||||
"java-{}-{}",
|
|
||||||
version,
|
|
||||||
if env::consts::OS == "windows" {
|
|
||||||
"win"
|
|
||||||
} else {
|
|
||||||
"lin"
|
|
||||||
}
|
|
||||||
)))
|
|
||||||
.unwrap();
|
|
||||||
buff.rewind().unwrap();
|
buff.rewind().unwrap();
|
||||||
if env::consts::OS != "windows" {
|
if env::consts::OS != "windows" {
|
||||||
let tar = GzDecoder::new(buff);
|
let tar = GzDecoder::new(buff);
|
||||||
let mut archive = Archive::new(tar);
|
let mut archive = Archive::new(tar);
|
||||||
if !unpack_archive(
|
if !unpack_archive(archive, get_local_data_directory().join("java").join(format!("java-{}-lin", version))).is_ok() {
|
||||||
archive,
|
std::fs::remove_dir_all(get_local_data_directory().join("java").join(format!("java-{}-lin", version))).unwrap();
|
||||||
get_local_data_directory()
|
|
||||||
.join("java")
|
|
||||||
.join(format!("java-{}-lin", version)),
|
|
||||||
)
|
|
||||||
.is_ok()
|
|
||||||
{
|
|
||||||
std::fs::remove_dir_all(
|
|
||||||
get_local_data_directory()
|
|
||||||
.join("java")
|
|
||||||
.join(format!("java-{}-lin", version)),
|
|
||||||
)
|
|
||||||
.unwrap();
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if !zip_extract::extract(
|
if !zip_extract::extract(buff, get_local_data_directory().join("java").join(format!("java-{}-win", version)).as_path(), true).is_ok() {
|
||||||
buff,
|
std::fs::remove_dir_all(get_local_data_directory().join("java").join(format!("java-{}-win", version))).unwrap();
|
||||||
get_local_data_directory()
|
|
||||||
.join("java")
|
|
||||||
.join(format!("java-{}-win", version))
|
|
||||||
.as_path(),
|
|
||||||
true,
|
|
||||||
)
|
|
||||||
.is_ok()
|
|
||||||
{
|
|
||||||
std::fs::remove_dir_all(
|
|
||||||
get_local_data_directory()
|
|
||||||
.join("java")
|
|
||||||
.join(format!("java-{}-win", version)),
|
|
||||||
)
|
|
||||||
.unwrap();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn unpack_archive<T: Read>(
|
fn unpack_archive<T: Read>(mut archive: Archive<T>, dst: PathBuf) -> Result<(), Box<dyn std::error::Error>> {
|
||||||
mut archive: Archive<T>,
|
|
||||||
dst: PathBuf,
|
|
||||||
) -> Result<(), Box<dyn std::error::Error>> {
|
|
||||||
for file in archive.entries()? {
|
for file in archive.entries()? {
|
||||||
let path = PathBuf::new().join(dst.clone());
|
let path = PathBuf::new().join(dst.clone());
|
||||||
let mut file = file?;
|
let mut file = file?;
|
||||||
|
@ -1,28 +1,25 @@
|
|||||||
// Prevents additional console window on Windows in release, DO NOT REMOVE!!
|
// Prevents additional console window on Windows in release, DO NOT REMOVE!!
|
||||||
#![cfg_attr(not(debug_assertions), windows_subsystem = "windows")]
|
#![cfg_attr(not(debug_assertions), windows_subsystem = "windows")]
|
||||||
|
|
||||||
use self_update::cargo_crate_version;
|
|
||||||
use serde::Deserialize;
|
|
||||||
use serde::Serialize;
|
|
||||||
use serde_json::{Map, Result, Value};
|
|
||||||
use std::collections::{HashMap, HashSet};
|
use std::collections::{HashMap, HashSet};
|
||||||
use std::io::Seek;
|
|
||||||
use std::{io::Cursor, path::PathBuf};
|
use std::{io::Cursor, path::PathBuf};
|
||||||
|
use std::io::Seek;
|
||||||
|
use self_update::cargo_crate_version;
|
||||||
|
use serde_json::{Map, Result, Value};
|
||||||
|
use serde::Serialize;
|
||||||
|
use serde::Deserialize;
|
||||||
|
|
||||||
//mod ftp;
|
mod ftp;
|
||||||
mod admin;
|
|
||||||
mod https;
|
|
||||||
mod java;
|
mod java;
|
||||||
mod modpack;
|
|
||||||
mod prism;
|
mod prism;
|
||||||
mod sftp;
|
|
||||||
mod system_dirs;
|
mod system_dirs;
|
||||||
mod util;
|
mod util;
|
||||||
|
mod modpack;
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize)]
|
#[derive(Serialize, Deserialize)]
|
||||||
struct ModpackEntry {
|
struct ModpackEntry{
|
||||||
name: String,
|
name: String,
|
||||||
id: String,
|
id: String
|
||||||
}
|
}
|
||||||
|
|
||||||
// Learn more about Tauri commands at https://tauri.app/v1/guides/features/command
|
// Learn more about Tauri commands at https://tauri.app/v1/guides/features/command
|
||||||
@ -31,30 +28,15 @@ fn greet(name: &str) -> String {
|
|||||||
format!("Hello, {}! You've been greeted from Rust!", name)
|
format!("Hello, {}! You've been greeted from Rust!", name)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
//modpack::get_modpacks();
|
let status = self_update::backends::gitea::Update::configure().no_confirm(true).with_host("https://gitea.piwalker.net").repo_owner("piwalker").repo_name("FCLauncher").bin_name("FCLauncher").show_download_progress(true).current_version(cargo_crate_version!()).build().unwrap().update().unwrap();
|
||||||
|
println!("update status: `{}`", status.version());
|
||||||
|
println!("This is the updated version!");
|
||||||
|
modpack::get_modpacks();
|
||||||
//prism::install_prism();
|
//prism::install_prism();
|
||||||
tauri::Builder::default()
|
tauri::Builder::default()
|
||||||
.plugin(tauri_plugin_updater::Builder::new().build())
|
.invoke_handler(tauri::generate_handler![greet, modpack::get_modpacks, modpack::launch_modpack, prism::launch_prism, prism::install_prism])
|
||||||
.plugin(tauri_plugin_process::init())
|
|
||||||
.plugin(tauri_plugin_shell::init())
|
|
||||||
.plugin(tauri_plugin_dialog::init())
|
|
||||||
.invoke_handler(tauri::generate_handler![
|
|
||||||
greet,
|
|
||||||
modpack::get_modpacks,
|
|
||||||
modpack::launch_modpack,
|
|
||||||
modpack::get_versions,
|
|
||||||
modpack::get_latest_version,
|
|
||||||
prism::launch_prism,
|
|
||||||
prism::install_prism,
|
|
||||||
admin::login,
|
|
||||||
admin::drop_session,
|
|
||||||
admin::shift_up,
|
|
||||||
admin::shift_down,
|
|
||||||
admin::add_pack,
|
|
||||||
admin::remove_pack,
|
|
||||||
admin::update_pack
|
|
||||||
])
|
|
||||||
.run(tauri::generate_context!())
|
.run(tauri::generate_context!())
|
||||||
.expect("error while running tauri application");
|
.expect("error while running tauri application");
|
||||||
}
|
}
|
||||||
|
@ -1,38 +1,34 @@
|
|||||||
use crate::https;
|
|
||||||
use crate::java;
|
use crate::{ftp, java};
|
||||||
use crate::system_dirs::{
|
use crate::system_dirs::{get_data_directory, get_java_executable, get_local_data_directory, get_prism_executable};
|
||||||
get_data_directory, get_java_executable, get_local_data_directory, get_prism_executable,
|
|
||||||
};
|
|
||||||
use crate::util;
|
|
||||||
use reqwest::IntoUrl;
|
|
||||||
use serde::de::value::Error;
|
|
||||||
use serde::Deserialize;
|
|
||||||
use serde::Serialize;
|
|
||||||
use serde_json::Value;
|
|
||||||
use std::fs;
|
|
||||||
use std::fs::File;
|
|
||||||
use std::io::{Read, Seek, Write};
|
|
||||||
use std::process::Command;
|
|
||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
use std::{env, thread};
|
use std::{env, thread};
|
||||||
|
use std::fs::File;
|
||||||
|
use std::process::Command;
|
||||||
use std::{io::Cursor, path::PathBuf};
|
use std::{io::Cursor, path::PathBuf};
|
||||||
|
use std::io::{Read, Seek, Write};
|
||||||
|
use serde_json::Value;
|
||||||
|
use serde::Serialize;
|
||||||
|
use serde::Deserialize;
|
||||||
|
use crate::util;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, Clone)]
|
#[derive(Serialize, Deserialize, Clone)]
|
||||||
pub struct ModpackEntry {
|
pub struct ModpackEntry{
|
||||||
pub name: String,
|
name: String,
|
||||||
pub id: String,
|
id: String
|
||||||
pub last_updated: String,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, Clone)]
|
#[derive(Clone)]
|
||||||
pub struct VersionEntry {
|
pub struct VersionEntry{
|
||||||
pub Version: String,
|
version: String,
|
||||||
pub File: String,
|
file: String,
|
||||||
pub Date: String,
|
date: String
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn get_modpack_name(id: String) -> String {
|
fn get_modpack_name(id: String) -> String {
|
||||||
let modpacks = get_modpacks().await;
|
let modpacks = get_modpacks();
|
||||||
let mut instance_name = String::new();
|
let mut instance_name = String::new();
|
||||||
for pack in modpacks {
|
for pack in modpacks {
|
||||||
if pack.id == id {
|
if pack.id == id {
|
||||||
@ -42,101 +38,54 @@ async fn get_modpack_name(id: String) -> String {
|
|||||||
return instance_name;
|
return instance_name;
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn check_modpack_needs_update(id: String) -> bool {
|
|
||||||
let mut instance_name = get_modpack_name(id.clone()).await;
|
|
||||||
if !get_local_data_directory()
|
fn check_modpack_needs_update(id: String) -> bool{
|
||||||
.join("prism")
|
let mut instance_name = get_modpack_name(id.clone());
|
||||||
.join("instances")
|
if !get_local_data_directory().join("prism").join("instances").join(&mut instance_name).exists() {
|
||||||
.join(&mut instance_name)
|
|
||||||
.exists()
|
|
||||||
{
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let versions = get_versions(id);
|
||||||
|
let latest = versions[versions.len()-1].version.clone();
|
||||||
|
|
||||||
let versions = get_versions(id).await;
|
let mut file = File::open(get_local_data_directory().join("prism").join("instances").join(instance_name).join(".minecraft").join("version.txt")).unwrap();
|
||||||
if !versions.is_ok() {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
let versions = versions.unwrap();
|
|
||||||
let latest = versions[versions.len() - 1].Version.clone();
|
|
||||||
|
|
||||||
let mut file = File::open(
|
|
||||||
get_local_data_directory()
|
|
||||||
.join("prism")
|
|
||||||
.join("instances")
|
|
||||||
.join(instance_name)
|
|
||||||
.join(".minecraft")
|
|
||||||
.join("version.txt"),
|
|
||||||
)
|
|
||||||
.unwrap();
|
|
||||||
let mut current = String::new();
|
let mut current = String::new();
|
||||||
file.read_to_string(&mut current);
|
file.read_to_string(&mut current);
|
||||||
|
|
||||||
if latest != current {
|
if latest != current {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tauri::command]
|
#[tauri::command]
|
||||||
pub async fn launch_modpack(window: tauri::AppHandle, id: String) {
|
pub async fn launch_modpack(window: tauri::Window, id: String){
|
||||||
if check_modpack_needs_update(id.clone()).await {
|
|
||||||
install_modpack(window, id.clone()).await;
|
if check_modpack_needs_update(id.clone()) {
|
||||||
|
install_modpack(window, id.clone());
|
||||||
}
|
}
|
||||||
// Launch
|
// Launch
|
||||||
let mut child = Command::new(
|
let mut child = Command::new(get_local_data_directory().join("prism").join(get_prism_executable())).arg("-l").arg(get_modpack_name(id)).spawn().unwrap();
|
||||||
get_local_data_directory()
|
|
||||||
.join("prism")
|
|
||||||
.join(get_prism_executable()),
|
|
||||||
)
|
|
||||||
.arg("-l")
|
|
||||||
.arg(get_modpack_name(id).await)
|
|
||||||
.spawn()
|
|
||||||
.unwrap();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn install_modpack(window: tauri::AppHandle, id: String) {
|
fn install_modpack(window: tauri::Window, id: String){
|
||||||
let versions = get_versions(id.clone()).await.unwrap();
|
let versions = get_versions(id.clone());
|
||||||
let path = env::temp_dir().join(format!("{}.mrpack", get_modpack_name(id.clone()).await));
|
let path = env::temp_dir().join(format!("{}.mrpack", get_modpack_name(id.clone())));
|
||||||
let mut file = File::create(path.clone()).unwrap();
|
let mut file = File::create(path.clone()).unwrap();
|
||||||
let ftp_path = PathBuf::new()
|
let ftp_path = PathBuf::new().join(id.clone()).join(versions[versions.len()-1].file.clone().as_str());
|
||||||
.join(id.clone())
|
|
||||||
.join(versions[versions.len() - 1].File.clone().as_str());
|
|
||||||
println!("Downloading file {}", ftp_path.to_str().unwrap());
|
println!("Downloading file {}", ftp_path.to_str().unwrap());
|
||||||
//ftp::ftp_retr(Some(window.clone()), ftp_path, &mut file, |window, data, size| util::download_callback(name.clone(), window, data, size)).unwrap();
|
ftp::ftp_retr(Some(window.clone()), ftp_path, &mut file, |window, data, size| util::download_callback(get_modpack_name(id.clone()), window, data, size)).unwrap();
|
||||||
https::download(
|
let mut child = Command::new(get_local_data_directory().join("prism").join(get_prism_executable())).arg("-I").arg(path).spawn().unwrap();
|
||||||
Some(window.clone()),
|
|
||||||
format!(
|
|
||||||
"https://gitea.piwalker.net/fclauncher/{}/{}",
|
|
||||||
id.clone(),
|
|
||||||
versions[versions.len() - 1].File.clone()
|
|
||||||
),
|
|
||||||
&mut file,
|
|
||||||
get_modpack_name(id.clone()).await,
|
|
||||||
)
|
|
||||||
.await;
|
|
||||||
let mut child = Command::new(
|
|
||||||
get_local_data_directory()
|
|
||||||
.join("prism")
|
|
||||||
.join(get_prism_executable()),
|
|
||||||
)
|
|
||||||
.arg("-I")
|
|
||||||
.arg(path)
|
|
||||||
.spawn()
|
|
||||||
.unwrap();
|
|
||||||
loop {
|
loop {
|
||||||
let version_path = get_local_data_directory()
|
let version_path = get_local_data_directory().join("prism").join("instances").join(get_modpack_name(id.clone())).join(".minecraft").join("version.txt");
|
||||||
.join("prism")
|
|
||||||
.join("instances")
|
|
||||||
.join(get_modpack_name(id.clone()).await)
|
|
||||||
.join(".minecraft")
|
|
||||||
.join("version.txt");
|
|
||||||
if version_path.clone().exists() {
|
if version_path.clone().exists() {
|
||||||
let mut ver_file = File::open(version_path).unwrap();
|
let mut ver_file = File::open(version_path).unwrap();
|
||||||
let mut buf = String::new();
|
let mut buf = String::new();
|
||||||
ver_file.read_to_string(&mut buf).unwrap();
|
ver_file.read_to_string(&mut buf).unwrap();
|
||||||
if buf == versions[versions.len() - 1].Version.clone().as_str() {
|
if buf == versions[versions.len()-1].version.clone().as_str() {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -144,11 +93,7 @@ async fn install_modpack(window: tauri::AppHandle, id: String) {
|
|||||||
}
|
}
|
||||||
thread::sleep(Duration::from_secs(1));
|
thread::sleep(Duration::from_secs(1));
|
||||||
child.kill();
|
child.kill();
|
||||||
let info_path = get_local_data_directory()
|
let info_path = get_local_data_directory().join("prism").join("instances").join(get_modpack_name(id.clone())).join("mmc-pack.json");
|
||||||
.join("prism")
|
|
||||||
.join("instances")
|
|
||||||
.join(get_modpack_name(id.clone()).await)
|
|
||||||
.join("mmc-pack.json");
|
|
||||||
let mut info_file = File::open(info_path.clone()).unwrap();
|
let mut info_file = File::open(info_path.clone()).unwrap();
|
||||||
let info_json: Value = serde_json::from_reader(info_file).unwrap();
|
let info_json: Value = serde_json::from_reader(info_file).unwrap();
|
||||||
let mut mc_version = "0.0";
|
let mut mc_version = "0.0";
|
||||||
@ -158,13 +103,9 @@ async fn install_modpack(window: tauri::AppHandle, id: String) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
let java = get_java_version(mc_version);
|
let java = get_java_version(mc_version);
|
||||||
java::install_java(java, window.clone()).await;
|
java::install_java(java, window.clone());
|
||||||
|
|
||||||
let option_path = get_local_data_directory()
|
let option_path = get_local_data_directory().join("prism").join("instances").join(get_modpack_name(id.clone())).join("instance.cfg");
|
||||||
.join("prism")
|
|
||||||
.join("instances")
|
|
||||||
.join(get_modpack_name(id.clone()).await)
|
|
||||||
.join("instance.cfg");
|
|
||||||
let mut option_file = File::open(option_path.clone()).unwrap();
|
let mut option_file = File::open(option_path.clone()).unwrap();
|
||||||
let mut buf = String::new();
|
let mut buf = String::new();
|
||||||
option_file.read_to_string(&mut buf);
|
option_file.read_to_string(&mut buf);
|
||||||
@ -172,128 +113,51 @@ async fn install_modpack(window: tauri::AppHandle, id: String) {
|
|||||||
let mut set = false;
|
let mut set = false;
|
||||||
for line in buf.lines() {
|
for line in buf.lines() {
|
||||||
if line.starts_with("JavaPath=") {
|
if line.starts_with("JavaPath=") {
|
||||||
option_file.write_all(
|
option_file.write_all(format!("JavaPath={}/java-{}-{}/bin/{}\n", get_local_data_directory().join("java").into_os_string().to_str().unwrap().replace("\\", "/"), java, if env::consts::OS == "windows" {"win"} else {"lin"}, get_java_executable()).as_bytes());
|
||||||
format!(
|
|
||||||
"JavaPath={}/java-{}-{}/bin/{}\n",
|
|
||||||
get_local_data_directory()
|
|
||||||
.join("java")
|
|
||||||
.into_os_string()
|
|
||||||
.to_str()
|
|
||||||
.unwrap()
|
|
||||||
.replace("\\", "/"),
|
|
||||||
java,
|
|
||||||
if env::consts::OS == "windows" {
|
|
||||||
"win"
|
|
||||||
} else {
|
|
||||||
"lin"
|
|
||||||
},
|
|
||||||
get_java_executable()
|
|
||||||
)
|
|
||||||
.as_bytes(),
|
|
||||||
);
|
|
||||||
set = true;
|
set = true;
|
||||||
} else {
|
} else {
|
||||||
option_file.write_all(format!("{}\n", line).as_bytes());
|
option_file.write_all(format!("{}\n",line).as_bytes());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if !set {
|
if !set {
|
||||||
option_file.write_all(
|
option_file.write_all(format!("JavaPath={}/java-{}-{}/bin/{}\n", get_local_data_directory().join("java").into_os_string().to_str().unwrap().replace("\\", "/"), java, if env::consts::OS == "windows" {"win"} else {"lin"}, get_java_executable()).as_bytes());
|
||||||
format!(
|
|
||||||
"JavaPath={}/java-{}-{}/bin/{}\n",
|
|
||||||
get_local_data_directory()
|
|
||||||
.join("java")
|
|
||||||
.into_os_string()
|
|
||||||
.to_str()
|
|
||||||
.unwrap()
|
|
||||||
.replace("\\", "/"),
|
|
||||||
java,
|
|
||||||
if env::consts::OS == "windows" {
|
|
||||||
"win"
|
|
||||||
} else {
|
|
||||||
"lin"
|
|
||||||
},
|
|
||||||
get_java_executable()
|
|
||||||
)
|
|
||||||
.as_bytes(),
|
|
||||||
);
|
|
||||||
option_file.write_all("OverrideJavaLocation=true\n".as_bytes());
|
option_file.write_all("OverrideJavaLocation=true\n".as_bytes());
|
||||||
option_file.write_all("OverrideJava=true\n".as_bytes());
|
option_file.write_all("OverrideJava=true\n".as_bytes());
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tauri::command]
|
#[tauri::command]
|
||||||
pub async fn get_modpacks() -> Vec<ModpackEntry> {
|
pub fn get_modpacks() -> Vec<ModpackEntry> {
|
||||||
//unsafe{
|
unsafe{
|
||||||
let mut modpacks: Vec<ModpackEntry> = Vec::new();
|
static mut modpacks: Vec<ModpackEntry> = Vec::new();
|
||||||
//if modpacks.is_empty() {
|
if modpacks.is_empty() {
|
||||||
let mut buf = Cursor::new(vec![]);
|
let mut buf = Cursor::new(vec![]);
|
||||||
//ftp::ftp_retr(None, PathBuf::new().join("modpacks.json"), &mut buf, |_, _, _| return);
|
ftp::ftp_retr(None, PathBuf::new().join("modpacks.json"), &mut buf, |_, _, _| return);
|
||||||
https::download(
|
buf.rewind();
|
||||||
None,
|
let v: Value = serde_json::from_reader(buf).unwrap();
|
||||||
format!("https://gitea.piwalker.net/fclauncher/modpacks.json"),
|
println!("{}", v[0]["name"]);
|
||||||
&mut buf,
|
for pack in v.as_array().unwrap() {
|
||||||
format!("modpacks.json"),
|
modpacks.push(ModpackEntry{name: pack["name"].as_str().unwrap().to_string(), id: pack["id"].as_str().unwrap().to_string()});
|
||||||
)
|
|
||||||
.await;
|
|
||||||
buf.rewind();
|
|
||||||
let res = serde_json::from_reader(buf);
|
|
||||||
if !res.is_ok() {
|
|
||||||
println!("Result not ok!");
|
|
||||||
let paths =
|
|
||||||
fs::read_dir(get_local_data_directory().join("prism").join("instances")).unwrap();
|
|
||||||
for path in paths {
|
|
||||||
let path = path.unwrap();
|
|
||||||
if fs::metadata(path.path()).unwrap().is_file() {
|
|
||||||
continue;
|
|
||||||
}
|
}
|
||||||
let name = path.file_name().into_string().unwrap();
|
|
||||||
if name.starts_with(".") {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
modpacks.push(ModpackEntry {
|
|
||||||
name: name.clone(),
|
|
||||||
id: name,
|
|
||||||
last_updated: format!(""),
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
return modpacks.clone();
|
return modpacks.clone();
|
||||||
}
|
}
|
||||||
let modpacks: Vec<ModpackEntry> = res.unwrap();
|
|
||||||
//println!("{}", v[0].name);
|
|
||||||
//for pack in v.as_array().unwrap() {
|
|
||||||
//modpacks.push(ModpackEntry{name: pack["name"].as_str().unwrap().to_string(), id: pack["id"].as_str().unwrap().to_string(), last_updated: pack["last-updated"].as_str().unwrap().to_string()});
|
|
||||||
//}
|
|
||||||
//}
|
|
||||||
return modpacks.clone();
|
|
||||||
//}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tauri::command]
|
fn get_versions(id: String) -> Vec<VersionEntry> {
|
||||||
pub async fn get_versions(id: String) -> Result<Vec<VersionEntry>, String> {
|
|
||||||
let mut versions: Vec<VersionEntry> = Vec::new();
|
let mut versions: Vec<VersionEntry> = Vec::new();
|
||||||
let mut buf = Cursor::new(vec![]);
|
let mut buf = Cursor::new(vec![]);
|
||||||
//ftp::ftp_retr(None, PathBuf::new().join(id).join("versions.json"), &mut buf, |_, _, _| return);
|
ftp::ftp_retr(None, PathBuf::new().join(id).join("versions.json"), &mut buf, |_, _, _| return);
|
||||||
https::download(
|
|
||||||
None,
|
|
||||||
format!(
|
|
||||||
"https://gitea.piwalker.net/fclauncher/{}/versions.json",
|
|
||||||
id.clone()
|
|
||||||
),
|
|
||||||
&mut buf,
|
|
||||||
format!("{}/versions.json", id.clone()),
|
|
||||||
)
|
|
||||||
.await;
|
|
||||||
buf.rewind();
|
buf.rewind();
|
||||||
let versions: Vec<VersionEntry> =
|
let v: Value = serde_json::from_reader(buf).unwrap();
|
||||||
serde_json::from_reader(buf).or(Err(format!("Unable to parse json")))?;
|
for version in v.as_array().unwrap() {
|
||||||
//for version in v.as_array().unwrap() {
|
versions.push(VersionEntry{version: version["Version"].as_str().unwrap().to_string(), file: version["File"].as_str().unwrap().to_string(), date: version["Date"].as_str().unwrap().to_string()});
|
||||||
//versions.push(VersionEntry{version: version["Version"].as_str().unwrap().to_string(), file: version["File"].as_str().unwrap().to_string(), date: version["Date"].as_str().unwrap().to_string()});
|
}
|
||||||
//}
|
return versions.clone();
|
||||||
return Ok(versions.clone());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_java_version(mc_version: &str) -> u8 {
|
fn get_java_version(mc_version: &str) -> u8{
|
||||||
let components: Vec<&str> = mc_version.split(".").collect();
|
let components: Vec<&str> = mc_version.split(".").collect();
|
||||||
let mut java = 8;
|
let mut java = 8;
|
||||||
if components[1] == "17" {
|
if components[1] == "17" {
|
||||||
@ -309,16 +173,3 @@ fn get_java_version(mc_version: &str) -> u8 {
|
|||||||
}
|
}
|
||||||
return java;
|
return java;
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tauri::command]
|
|
||||||
pub async fn get_latest_version(id: String) -> Result<String, String> {
|
|
||||||
let versions = get_versions(id).await.unwrap();
|
|
||||||
if (versions.len() == 0) {
|
|
||||||
return Ok(format!(""));
|
|
||||||
}
|
|
||||||
Ok(versions[versions.len() - 1].Version.clone())
|
|
||||||
}
|
|
||||||
|
|
||||||
//pub fn create_json(modpacks: Vec<ModpackEntry>) -> Result<serde_json::Value, String> {
|
|
||||||
|
|
||||||
//}
|
|
||||||
|
@ -1,20 +1,10 @@
|
|||||||
|
use std::{env, fs::File, io::{BufRead, Cursor, Seek, Write}, path::PathBuf, process::Command, str::FromStr};
|
||||||
use flate2::read::GzDecoder;
|
use flate2::read::GzDecoder;
|
||||||
use std::{
|
|
||||||
env,
|
|
||||||
fs::File,
|
|
||||||
io::{BufRead, Cursor, Seek, Write},
|
|
||||||
path::PathBuf,
|
|
||||||
process::Command,
|
|
||||||
str::FromStr,
|
|
||||||
};
|
|
||||||
use tar::Archive;
|
use tar::Archive;
|
||||||
//use tauri::file;
|
use tauri::api::file;
|
||||||
|
|
||||||
|
use crate::{ftp, java, system_dirs::{get_local_data_directory, get_prism_executable}, util};
|
||||||
|
|
||||||
use crate::{
|
|
||||||
https, java,
|
|
||||||
system_dirs::{get_local_data_directory, get_prism_executable},
|
|
||||||
util,
|
|
||||||
};
|
|
||||||
|
|
||||||
pub fn check_prism() -> bool {
|
pub fn check_prism() -> bool {
|
||||||
let path = get_local_data_directory().join("prism");
|
let path = get_local_data_directory().join("prism");
|
||||||
@ -22,67 +12,34 @@ pub fn check_prism() -> bool {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[tauri::command]
|
#[tauri::command]
|
||||||
pub async fn install_prism(window: tauri::AppHandle) {
|
pub async fn install_prism(window: tauri::Window){
|
||||||
if check_prism() {
|
if check_prism() {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
java::install_java(21, window.clone()).await;
|
java::install_java(21, window.clone());
|
||||||
|
let path = PathBuf::new().join("prism").join(format!("prism-{}",if env::consts::OS == "windows" {"win.zip"} else {"lin.tar.gz"}));
|
||||||
|
let size = ftp::ftp_get_size(path.clone()).unwrap();
|
||||||
let mut buff = Cursor::new(vec![]);
|
let mut buff = Cursor::new(vec![]);
|
||||||
let mut total = 0;
|
let mut total = 0;
|
||||||
//ftp::ftp_retr(Some(window.clone()), path, &mut buff, |window: Option<tauri::Window>, data, size| util::download_callback("Prism Launcher".to_string(),window, data, size)).unwrap();
|
ftp::ftp_retr(Some(window.clone()), path, &mut buff, |window: Option<tauri::Window>, data, size| util::download_callback("Prism Launcher".to_string(),window, data, size)).unwrap();
|
||||||
https::download(
|
|
||||||
Some(window.clone()),
|
|
||||||
format!(
|
|
||||||
"https://gitea.piwalker.net/fclauncher/prism/prism-{}",
|
|
||||||
if env::consts::OS == "windows" {
|
|
||||||
"win.zip"
|
|
||||||
} else {
|
|
||||||
"lin.tar.gz"
|
|
||||||
}
|
|
||||||
),
|
|
||||||
&mut buff,
|
|
||||||
format!("Prism Launcher"),
|
|
||||||
)
|
|
||||||
.await;
|
|
||||||
std::fs::create_dir_all(get_local_data_directory().join("prism")).unwrap();
|
std::fs::create_dir_all(get_local_data_directory().join("prism")).unwrap();
|
||||||
buff.rewind().unwrap();
|
buff.rewind().unwrap();
|
||||||
if env::consts::OS != "windows" {
|
if env::consts::OS != "windows" {
|
||||||
let tar = GzDecoder::new(buff);
|
let tar = GzDecoder::new(buff);
|
||||||
let mut archive = Archive::new(tar);
|
let mut archive = Archive::new(tar);
|
||||||
if !archive
|
if !archive.unpack(get_local_data_directory().join("prism")).is_ok() {
|
||||||
.unpack(get_local_data_directory().join("prism"))
|
|
||||||
.is_ok()
|
|
||||||
{
|
|
||||||
std::fs::remove_dir_all(get_local_data_directory().join("prism"));
|
std::fs::remove_dir_all(get_local_data_directory().join("prism"));
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if !zip_extract::extract(
|
if !zip_extract::extract(buff, get_local_data_directory().join("prism").as_path(), true).is_ok() {
|
||||||
buff,
|
|
||||||
get_local_data_directory().join("prism").as_path(),
|
|
||||||
true,
|
|
||||||
)
|
|
||||||
.is_ok()
|
|
||||||
{
|
|
||||||
std::fs::remove_dir_all(get_local_data_directory().join("prism"));
|
std::fs::remove_dir_all(get_local_data_directory().join("prism"));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut buff = Cursor::new(vec![]);
|
let mut buff = Cursor::new(vec![]);
|
||||||
//ftp::ftp_retr(Some(window.clone()), PathBuf::new().join("prism").join("prismlauncher.cfg"), &mut buff, |_, _, _| return).unwrap();
|
ftp::ftp_retr(Some(window.clone()), PathBuf::new().join("prism").join("prismlauncher.cfg"), &mut buff, |_, _, _| return).unwrap();
|
||||||
https::download(
|
|
||||||
None,
|
|
||||||
format!("https://gitea.piwalker.net/fclauncher/prism/prismlauncher.cfg"),
|
|
||||||
&mut buff,
|
|
||||||
format!("prismlauncher.cfg"),
|
|
||||||
)
|
|
||||||
.await;
|
|
||||||
buff.rewind();
|
buff.rewind();
|
||||||
let mut file = File::create(
|
let mut file = File::create(get_local_data_directory().join("prism").join("prismlauncher.cfg")).unwrap();
|
||||||
get_local_data_directory()
|
|
||||||
.join("prism")
|
|
||||||
.join("prismlauncher.cfg"),
|
|
||||||
)
|
|
||||||
.unwrap();
|
|
||||||
loop {
|
loop {
|
||||||
let mut buf = String::new();
|
let mut buf = String::new();
|
||||||
let count = buff.read_line(&mut buf).unwrap();
|
let count = buff.read_line(&mut buf).unwrap();
|
||||||
@ -90,35 +47,19 @@ pub async fn install_prism(window: tauri::AppHandle) {
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
if buf.starts_with("JavaPath") {
|
if buf.starts_with("JavaPath") {
|
||||||
buf = format!(
|
buf = format!("JavaPath={}/java/java-21-{}\n", get_local_data_directory().to_str().unwrap().replace("\\", "/"), if env::consts::OS == "windows" { "win" } else { "lin" });
|
||||||
"JavaPath={}/java/java-21-{}\n",
|
}else if buf.starts_with("LastHostname") {
|
||||||
get_local_data_directory()
|
buf = format!("LastHostname={}\n", gethostname::gethostname().to_str().unwrap());
|
||||||
.to_str()
|
|
||||||
.unwrap()
|
|
||||||
.replace("\\", "/"),
|
|
||||||
if env::consts::OS == "windows" {
|
|
||||||
"win"
|
|
||||||
} else {
|
|
||||||
"lin"
|
|
||||||
}
|
|
||||||
);
|
|
||||||
} else if buf.starts_with("LastHostname") {
|
|
||||||
buf = format!(
|
|
||||||
"LastHostname={}\n",
|
|
||||||
gethostname::gethostname().to_str().unwrap()
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
file.write_all(buf.as_bytes());
|
file.write_all(buf.as_bytes());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
#[tauri::command]
|
#[tauri::command]
|
||||||
pub fn launch_prism() {
|
pub fn launch_prism() {
|
||||||
let mut child = Command::new(
|
|
||||||
get_local_data_directory()
|
let mut child = Command::new(get_local_data_directory().join("prism").join(get_prism_executable())).spawn().unwrap();
|
||||||
.join("prism")
|
|
||||||
.join(get_prism_executable()),
|
|
||||||
)
|
|
||||||
.spawn()
|
|
||||||
.unwrap();
|
|
||||||
}
|
}
|
||||||
|
@ -1,102 +0,0 @@
|
|||||||
use futures_util::future::BoxFuture;
|
|
||||||
use futures_util::io::BufReader;
|
|
||||||
use futures_util::io::Cursor;
|
|
||||||
use futures_util::FutureExt;
|
|
||||||
use ssh2::OpenFlags;
|
|
||||||
use ssh2::Session;
|
|
||||||
use ssh2::Sftp;
|
|
||||||
use std::io::prelude::*;
|
|
||||||
use std::net::TcpStream;
|
|
||||||
use std::path::Path;
|
|
||||||
use std::path::PathBuf;
|
|
||||||
use tauri::Emitter;
|
|
||||||
|
|
||||||
use crate::https;
|
|
||||||
|
|
||||||
pub fn connect(username: String, password: String) -> Result<Session, String> {
|
|
||||||
let tcp = TcpStream::connect("gitea.piwalker.net:22")
|
|
||||||
.or(Err(format!("Unable to connect to host")))?;
|
|
||||||
let mut sess = Session::new().or(Err(format!("Unable to creat stream")))?;
|
|
||||||
sess.set_tcp_stream(tcp);
|
|
||||||
sess.handshake().unwrap();
|
|
||||||
sess.userauth_password(username.as_str(), password.as_str())
|
|
||||||
.or(Err(format!("Invalid username or password")))?;
|
|
||||||
Ok(sess)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub async fn uplaod(
|
|
||||||
window: Option<tauri::AppHandle>,
|
|
||||||
sess: Session,
|
|
||||||
path: PathBuf,
|
|
||||||
mut reader: impl Read,
|
|
||||||
upload_name: String,
|
|
||||||
total_size: usize,
|
|
||||||
) -> Result<(), String> {
|
|
||||||
let sftp = sess.sftp().or(Err("unable to open sftp session"))?;
|
|
||||||
let mut file = sftp.create(path.as_path()).or(Err("Unable to open file"))?;
|
|
||||||
let mut uploaded = 0;
|
|
||||||
while true {
|
|
||||||
let mut buf = vec![0u8; 1024 * 32];
|
|
||||||
let res = reader.read(&mut buf).unwrap();
|
|
||||||
if res <= 0 {
|
|
||||||
if let Some(window) = window.clone() {
|
|
||||||
window
|
|
||||||
.emit("download_finished", true)
|
|
||||||
.or(Err(format!("Unable to signal window!")))?;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
file.write_all(buf.split_at(res).0).unwrap();
|
|
||||||
uploaded += res;
|
|
||||||
println!(
|
|
||||||
"Uploading {} {}MB / {}MB",
|
|
||||||
upload_name,
|
|
||||||
uploaded / (1024 * 1024),
|
|
||||||
total_size / (1024 * 1024)
|
|
||||||
);
|
|
||||||
if let Some(window) = window.clone() {
|
|
||||||
window
|
|
||||||
.emit(
|
|
||||||
"download_progress",
|
|
||||||
https::DownloadStatus {
|
|
||||||
downloaded: uploaded as usize,
|
|
||||||
total: total_size as usize,
|
|
||||||
time_elapsed: 0,
|
|
||||||
download_name: upload_name.clone(),
|
|
||||||
},
|
|
||||||
)
|
|
||||||
.or(Err(format!("Unable to signal window")))?;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
pub async fn mkdir(sess: Session, path: PathBuf) -> Result<(), String> {
|
|
||||||
let sftp = sess.sftp().or(Err("Unable to open sftp session"))?;
|
|
||||||
sftp.mkdir(path.as_path(), 0o775)
|
|
||||||
.or(Err(format!("Unable to create directory")))?;
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn rmdir(sess: Session, path: PathBuf) -> BoxFuture<'static, ()> {
|
|
||||||
async move {
|
|
||||||
let sftp = sess.sftp().or(Err("Unable to open sftp session")).unwrap();
|
|
||||||
let dirs = sftp
|
|
||||||
.readdir(path.as_path())
|
|
||||||
.or(Err("unable to stat directory"))
|
|
||||||
.unwrap();
|
|
||||||
for dir in dirs {
|
|
||||||
if dir.1.is_dir() {
|
|
||||||
rmdir(sess.clone(), dir.0).await;
|
|
||||||
} else {
|
|
||||||
sftp.unlink(dir.0.as_path())
|
|
||||||
.or(Err(format!("Unable to delete file")))
|
|
||||||
.unwrap();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
sftp.rmdir(path.as_path())
|
|
||||||
.or(Err(format!("Unable to delete directory")))
|
|
||||||
.unwrap();
|
|
||||||
}
|
|
||||||
.boxed()
|
|
||||||
}
|
|
@ -1,8 +1,6 @@
|
|||||||
|
use std::{env, path::{Path, PathBuf}};
|
||||||
use dirs::home_dir;
|
use dirs::home_dir;
|
||||||
use std::{
|
|
||||||
env,
|
|
||||||
path::{Path, PathBuf},
|
|
||||||
};
|
|
||||||
|
|
||||||
pub fn get_local_data_directory() -> PathBuf {
|
pub fn get_local_data_directory() -> PathBuf {
|
||||||
dirs::data_local_dir().unwrap().join("FCLauncher")
|
dirs::data_local_dir().unwrap().join("FCLauncher")
|
||||||
@ -13,23 +11,10 @@ pub fn get_data_directory() -> PathBuf {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_java_executable() -> String {
|
pub fn get_java_executable() -> String {
|
||||||
return format!(
|
return format!("java{}", if env::consts::OS == "windows" { ".exe" } else { "" })
|
||||||
"java{}",
|
|
||||||
if env::consts::OS == "windows" {
|
|
||||||
".exe"
|
|
||||||
} else {
|
|
||||||
""
|
|
||||||
}
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
pub fn get_prism_executable() -> String {
|
pub fn get_prism_executable() -> String {
|
||||||
return format!(
|
return format!("{}", if env::consts::OS == "windows" { "prismlauncher.exe" } else { "PrismLauncher" })
|
||||||
"{}",
|
|
||||||
if env::consts::OS == "windows" {
|
|
||||||
"prismlauncher.exe"
|
|
||||||
} else {
|
|
||||||
"PrismLauncher"
|
|
||||||
}
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
@ -1,41 +1,24 @@
|
|||||||
use std::clone;
|
use std::clone;
|
||||||
use tauri::Emitter;
|
|
||||||
|
|
||||||
use serde::Serialize;
|
use serde::Serialize;
|
||||||
|
|
||||||
|
|
||||||
#[derive(Clone, Serialize)]
|
#[derive(Clone, Serialize)]
|
||||||
pub struct DownloadStatus {
|
pub struct DownloadStatus {
|
||||||
downloaded: usize,
|
downloaded: usize,
|
||||||
total: usize,
|
total: usize,
|
||||||
time_elapsed: usize,
|
time_elapsed: usize,
|
||||||
download_name: String,
|
download_name: String
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn download_callback(
|
pub fn download_callback(download_name: String, window: Option<tauri::Window>, count: usize, size: usize){
|
||||||
download_name: String,
|
unsafe{
|
||||||
window: Option<tauri::AppHandle>,
|
|
||||||
count: usize,
|
|
||||||
size: usize,
|
|
||||||
) {
|
|
||||||
unsafe {
|
|
||||||
static mut total: usize = 0;
|
static mut total: usize = 0;
|
||||||
total += count;
|
total += count;
|
||||||
if let Some(window1) = window.clone() {
|
if let Some(window1) = window.clone() {
|
||||||
window1.emit(
|
window1.emit("download_progress", DownloadStatus{downloaded: total, total: size, time_elapsed: 0, download_name: download_name});
|
||||||
"download_progress",
|
|
||||||
DownloadStatus {
|
|
||||||
downloaded: total,
|
|
||||||
total: size,
|
|
||||||
time_elapsed: 0,
|
|
||||||
download_name: download_name,
|
|
||||||
},
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
println!(
|
println!("Downloading {}MB / {}MB", total/(1024*1024), size/(1024*1024));
|
||||||
"Downloading {}MB / {}MB",
|
|
||||||
total / (1024 * 1024),
|
|
||||||
size / (1024 * 1024)
|
|
||||||
);
|
|
||||||
if count == 0 {
|
if count == 0 {
|
||||||
if let Some(window2) = window {
|
if let Some(window2) = window {
|
||||||
window2.emit("download_finished", true);
|
window2.emit("download_finished", true);
|
||||||
@ -44,3 +27,4 @@ pub fn download_callback(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,36 +1,25 @@
|
|||||||
{
|
{
|
||||||
"build": {
|
"build": {
|
||||||
"frontendDist": "../src"
|
"devPath": "../src",
|
||||||
|
"distDir": "../src",
|
||||||
|
"withGlobalTauri": true
|
||||||
},
|
},
|
||||||
"bundle": {
|
"package": {
|
||||||
"active": true,
|
"productName": "FCLauncher",
|
||||||
"targets": "all",
|
"version": "0.0.0"
|
||||||
"icon": [
|
|
||||||
"icons/32x32.png",
|
|
||||||
"icons/128x128.png",
|
|
||||||
"icons/128x128@2x.png",
|
|
||||||
"icons/icon.icns",
|
|
||||||
"icons/icon.ico"
|
|
||||||
],
|
|
||||||
"createUpdaterArtifacts": "v1Compatible"
|
|
||||||
},
|
},
|
||||||
"productName": "FCLauncher",
|
"tauri": {
|
||||||
"mainBinaryName": "FCLauncher",
|
"allowlist": {
|
||||||
"version": "1.0.5",
|
"all": false,
|
||||||
"identifier": "net.piwalker",
|
"shell": {
|
||||||
"plugins": {
|
"all": false,
|
||||||
"updater": {
|
"open": true
|
||||||
"pubkey": "dW50cnVzdGVkIGNvbW1lbnQ6IG1pbmlzaWduIHB1YmxpYyBrZXk6IDNDOEUwMjYxRUU2NEI5RgpSV1NmUytZZUp1RElBN3dEaGhpWG9JZVNQcFlnNFFzaXN0UnBsVmxNeVdWWnJoQmh4cGJRbjN3Ygo=",
|
},
|
||||||
"endpoints": [
|
"dialog": {
|
||||||
"https://gitea.piwalker.net/fclauncher/app.json"
|
"all": false,
|
||||||
]
|
"ask": true
|
||||||
}
|
}
|
||||||
},
|
|
||||||
"app": {
|
|
||||||
"security": {
|
|
||||||
"csp": null
|
|
||||||
},
|
},
|
||||||
"withGlobalTauri": true,
|
|
||||||
"windows": [
|
"windows": [
|
||||||
{
|
{
|
||||||
"title": "FCLauncher",
|
"title": "FCLauncher",
|
||||||
@ -38,6 +27,21 @@
|
|||||||
"height": 600,
|
"height": 600,
|
||||||
"resizable": false
|
"resizable": false
|
||||||
}
|
}
|
||||||
]
|
],
|
||||||
|
"security": {
|
||||||
|
"csp": null
|
||||||
|
},
|
||||||
|
"bundle": {
|
||||||
|
"active": true,
|
||||||
|
"targets": "all",
|
||||||
|
"identifier": "net.piwalker",
|
||||||
|
"icon": [
|
||||||
|
"icons/32x32.png",
|
||||||
|
"icons/128x128.png",
|
||||||
|
"icons/128x128@2x.png",
|
||||||
|
"icons/icon.icns",
|
||||||
|
"icons/icon.ico"
|
||||||
|
]
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,46 +0,0 @@
|
|||||||
<!doctype html>
|
|
||||||
<html lang="en">
|
|
||||||
<head>
|
|
||||||
<meta charset="UTF-8" />
|
|
||||||
<link rel="stylesheet" href="styles.css" />
|
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0, viewport-width=cover" />
|
|
||||||
<title>Tauri App</title>
|
|
||||||
<script type="module" src="/admin.js" defer></script>
|
|
||||||
</head>
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<body>
|
|
||||||
|
|
||||||
<div class="Logo">
|
|
||||||
<button id="back"></button>
|
|
||||||
<img src="assets/Title.png" alt="Title" id="Title">
|
|
||||||
</div>
|
|
||||||
<div class="progress">
|
|
||||||
<div class="progressFinished"></div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="container-horizontal" data-bs-theme="dark">
|
|
||||||
<div id="modpacks" >
|
|
||||||
</div>
|
|
||||||
<div class="vertical-buttons" >
|
|
||||||
<button id="up" class="square-button"></button>
|
|
||||||
<button id="down" class="square-button"></button>
|
|
||||||
<button id="remove" class="square-button"></button>
|
|
||||||
</div>
|
|
||||||
<div class="vertical" id="update">
|
|
||||||
<input placeholder="Version" id="pack_version" />
|
|
||||||
<input placeholder="File" id="file_path" />
|
|
||||||
<button id="browse">Browse</button>
|
|
||||||
<button id="update_pack">Update Pack</button>
|
|
||||||
</div>
|
|
||||||
<div class="vertical" id="create">
|
|
||||||
<input placeholder="Name" id="pack_name" />
|
|
||||||
<input placeholder="ID" id="pack_id" />
|
|
||||||
<button id="add">Create Pack</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
@ -1,33 +0,0 @@
|
|||||||
<!doctype html>
|
|
||||||
<html lang="en">
|
|
||||||
<head>
|
|
||||||
<meta charset="UTF-8" />
|
|
||||||
<link rel="stylesheet" href="styles.css" />
|
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0, viewport-width=cover" />
|
|
||||||
<title>Tauri App</title>
|
|
||||||
<script type="module" src="/login.js" defer></script>
|
|
||||||
</head>
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<body>
|
|
||||||
|
|
||||||
<div class="Logo">
|
|
||||||
<button id="back"></button>
|
|
||||||
<img src="assets/Title.png" alt="Title" id="Title">
|
|
||||||
</div>
|
|
||||||
|
|
||||||
|
|
||||||
<div class="container" data-bs-theme="dark">
|
|
||||||
<p class="Error" id="Incorrect">Username or Password is incorrect!</p>
|
|
||||||
<input id="Username" placeholder="Username" />
|
|
||||||
<input id="Password" placeholder="Password" type="password" />
|
|
||||||
<div class="loginButtons">
|
|
||||||
<button id="Cancel">Cancel</button>
|
|
||||||
<button id="Login">Login</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
@ -1,141 +0,0 @@
|
|||||||
const { invoke } = window.__TAURI__.tauri;
|
|
||||||
const { listen } = window.__TAURI__.event;
|
|
||||||
const { ask, open, message } = window.__TAURI__.dialog;
|
|
||||||
const downBar = document.querySelector(".progressFinished");
|
|
||||||
//import { listen } from '@tauri-apps/api';
|
|
||||||
|
|
||||||
const error = listen("Error", (error) => {
|
|
||||||
message(error.payload, {title: "Error", type: "error"});
|
|
||||||
});
|
|
||||||
|
|
||||||
var selected_pack = "";
|
|
||||||
var update_menu = document.getElementById("update");
|
|
||||||
var create_menu = document.getElementById("create");
|
|
||||||
|
|
||||||
const download_progress = listen("download_progress", (progress) => {
|
|
||||||
console.log("Downloading");
|
|
||||||
//console.log("Downloaded "+progress.payload.downloaded/(1024*1024) +"MB / " + progress.payload.total/(1024*1024) + "MB");
|
|
||||||
let downProgress = (progress.payload.downloaded/(1024*1024)).toFixed(2);
|
|
||||||
let downTotal = (progress.payload.total/(1024*1024)).toFixed(2);
|
|
||||||
downBar.style.width = `${(progress.payload.downloaded / progress.payload.total) * 100}%`;
|
|
||||||
document.querySelector(".progress").style.visibility = "visible";
|
|
||||||
});
|
|
||||||
|
|
||||||
const download_finished = listen("download_finished", (event) => {
|
|
||||||
downBar.style.width = 0;
|
|
||||||
document.querySelector(".progress").style.visibility = "hidden";
|
|
||||||
});
|
|
||||||
|
|
||||||
window.addEventListener("DOMContentLoaded", () => {
|
|
||||||
document.getElementById("browse").addEventListener("click", browse);
|
|
||||||
document.getElementById("update_pack").addEventListener("click", update_pack);
|
|
||||||
document.getElementById("up").addEventListener("click", up);
|
|
||||||
document.getElementById("down").addEventListener("click", down);
|
|
||||||
document.getElementById("add").addEventListener("click", add);
|
|
||||||
document.getElementById("remove").addEventListener("click", remove);
|
|
||||||
document.getElementById("back").addEventListener("click", back);
|
|
||||||
|
|
||||||
});
|
|
||||||
|
|
||||||
window.onload = async function() {
|
|
||||||
refresh();
|
|
||||||
}
|
|
||||||
|
|
||||||
function up(){
|
|
||||||
invoke("shift_up", { id: selected_pack }).then(refresh);
|
|
||||||
}
|
|
||||||
|
|
||||||
function down(){
|
|
||||||
invoke("shift_down", { id: selected_pack }).then(refresh);
|
|
||||||
}
|
|
||||||
|
|
||||||
function back(){
|
|
||||||
invoke("drop_session");
|
|
||||||
window.location.href = "index.html";
|
|
||||||
}
|
|
||||||
|
|
||||||
function refresh(){
|
|
||||||
update_menu.style.display = "none";
|
|
||||||
create_menu.style.display = "none";
|
|
||||||
invoke("get_modpacks").then(addModpacks);
|
|
||||||
}
|
|
||||||
|
|
||||||
function addModpacks(modpacks) {
|
|
||||||
var modpacks_list = document.getElementById("modpacks");
|
|
||||||
while (modpacks_list.firstChild) {
|
|
||||||
modpacks_list.removeChild(modpacks_list.lastChild);
|
|
||||||
}
|
|
||||||
for (let i = 0; i < modpacks.length; i++){
|
|
||||||
var div = document.createElement("div");
|
|
||||||
div.textContent = modpacks[i].name;
|
|
||||||
div.className = "modpack";
|
|
||||||
div.id = modpacks[i].id;
|
|
||||||
if(modpacks[i].id == selected_pack){
|
|
||||||
div.classList.add("modpack-selected");
|
|
||||||
update_menu.style.display = "flex";
|
|
||||||
invoke("get_latest_version", {id: selected_pack}).then(update_version);
|
|
||||||
}
|
|
||||||
div.addEventListener("click", function() { modpackClick(modpacks[i].id) });
|
|
||||||
modpacks_list.appendChild(div);
|
|
||||||
}
|
|
||||||
|
|
||||||
var div = document.createElement("div");
|
|
||||||
div.textContent = "<Create New Pack>";
|
|
||||||
div.className = "modpack";
|
|
||||||
div.id = "*new*";
|
|
||||||
div.addEventListener("click", function() { modpackClick("*new*") });
|
|
||||||
modpacks_list.appendChild(div);
|
|
||||||
}
|
|
||||||
|
|
||||||
function modpackClick(id){
|
|
||||||
var old = selected_pack;
|
|
||||||
document.getElementById(id).classList.add("modpack-selected");
|
|
||||||
selected_pack = id;
|
|
||||||
if (old == id){
|
|
||||||
selected_pack = "";
|
|
||||||
update_menu.style.display = "none";
|
|
||||||
create_menu.style.display = "none";
|
|
||||||
}else if (id == "*new*"){
|
|
||||||
update_menu.style.display = "none";
|
|
||||||
create_menu.style.display = "flex";
|
|
||||||
}else{
|
|
||||||
update_menu.style.display = "flex";
|
|
||||||
create_menu.style.display = "none";
|
|
||||||
invoke("get_latest_version", {id: selected_pack}).then(update_version);
|
|
||||||
}
|
|
||||||
document.getElementById(old).classList.remove("modpack-selected");
|
|
||||||
}
|
|
||||||
|
|
||||||
function add(){
|
|
||||||
var id = document.getElementById("pack_id").value;
|
|
||||||
var name = document.getElementById("pack_name").value;
|
|
||||||
selected_pack = id;
|
|
||||||
invoke("add_pack", {id: id, name: name}).then(refresh);
|
|
||||||
}
|
|
||||||
|
|
||||||
function remove(){
|
|
||||||
ask("Are you sure you want to remove " + document.getElementById(selected_pack).textContent + "?", {title: "Are you sure?", type: "Message"}).then((value) => { if (value) { invoke("remove_pack", {id: selected_pack}).then(refresh); } });
|
|
||||||
}
|
|
||||||
|
|
||||||
async function browse(){
|
|
||||||
const selected = await open ({
|
|
||||||
multiple: false,
|
|
||||||
filters: [{
|
|
||||||
name: 'Modrinth Modpack',
|
|
||||||
extensions: ['mrpack']
|
|
||||||
}]
|
|
||||||
});
|
|
||||||
if (selected != null){
|
|
||||||
document.getElementById("file_path").value = selected;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function update_version(version){
|
|
||||||
document.getElementById("pack_version").value = version;
|
|
||||||
}
|
|
||||||
|
|
||||||
function update_pack(){
|
|
||||||
var version = document.getElementById("pack_version").value;
|
|
||||||
var path = document.getElementById("file_path").value;
|
|
||||||
invoke("update_pack", {id: selected_pack, path: path, version: version}).then(refresh);
|
|
||||||
}
|
|
Before Width: | Height: | Size: 9.5 KiB |
@ -1,11 +0,0 @@
|
|||||||
<?xml version='1.0' encoding='utf-8'?>
|
|
||||||
<!DOCTYPE svg PUBLIC '-//W3C//DTD SVG 1.1//EN' 'http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd'>
|
|
||||||
<!-- Uploaded to: SVG Repo, www.svgrepo.com, Generator: SVG Repo Mixer Tools -->
|
|
||||||
<svg fill="#000000" height="800px" width="800px" version="1.1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" xmlns:xlink="http://www.w3.org/1999/xlink" enable-background="new 0 0 512 512">
|
|
||||||
<g>
|
|
||||||
<g>
|
|
||||||
<path d="M256,11C120.9,11,11,120.9,11,256s109.9,245,245,245s245-109.9,245-245S391.1,11,256,11z M256,460.2 c-112.6,0-204.2-91.6-204.2-204.2S143.4,51.8,256,51.8S460.2,143.4,460.2,256S368.6,460.2,256,460.2z"/>
|
|
||||||
<path d="m357.6,235.6h-81.2v-81.2c0-11.3-9.1-20.4-20.4-20.4-11.3,0-20.4,9.1-20.4,20.4v81.2h-81.2c-11.3,0-20.4,9.1-20.4,20.4s9.1,20.4 20.4,20.4h81.2v81.2c0,11.3 9.1,20.4 20.4,20.4 11.3,0 20.4-9.1 20.4-20.4v-81.2h81.2c11.3,0 20.4-9.1 20.4-20.4s-9.1-20.4-20.4-20.4z"/>
|
|
||||||
</g>
|
|
||||||
</g>
|
|
||||||
</svg>
|
|
Before Width: | Height: | Size: 943 B |
Before Width: | Height: | Size: 4.7 KiB |
Before Width: | Height: | Size: 5.0 KiB |
Before Width: | Height: | Size: 5.8 KiB |
Before Width: | Height: | Size: 42 KiB |
Before Width: | Height: | Size: 8.5 KiB |
Before Width: | Height: | Size: 5.0 KiB |
@ -15,7 +15,6 @@
|
|||||||
<body>
|
<body>
|
||||||
|
|
||||||
<div class="Logo">
|
<div class="Logo">
|
||||||
<button id="settings"></button>
|
|
||||||
<img src="assets/Title.png" alt="Title" id="Title">
|
<img src="assets/Title.png" alt="Title" id="Title">
|
||||||
</div>
|
</div>
|
||||||
<div class="progress">
|
<div class="progress">
|
||||||
|
@ -1,42 +0,0 @@
|
|||||||
const { invoke } = window.__TAURI__.tauri;
|
|
||||||
const { listen } = window.__TAURI__.event;
|
|
||||||
const { ask, message } = window.__TAURI__.dialog;
|
|
||||||
const downBar = document.querySelector(".progressFinished");
|
|
||||||
//import { listen } from '@tauri-apps/api';
|
|
||||||
|
|
||||||
const error = listen("Error", (error) => {
|
|
||||||
message(error.payload, {title: "Error", type: "error"});
|
|
||||||
});
|
|
||||||
|
|
||||||
window.addEventListener("DOMContentLoaded", () => {
|
|
||||||
|
|
||||||
document.getElementById("back").addEventListener("click", back);
|
|
||||||
document.getElementById("Cancel").addEventListener("click", back);
|
|
||||||
document.getElementById("Login").addEventListener("click", login);
|
|
||||||
document.getElementById("Password").addEventListener("keypress", keypress);
|
|
||||||
|
|
||||||
});
|
|
||||||
|
|
||||||
function back(){
|
|
||||||
invoke("drop_session");
|
|
||||||
window.location.href = "index.html";
|
|
||||||
}
|
|
||||||
|
|
||||||
function login(){
|
|
||||||
invoke("login", { username: document.getElementById("Username").value, password: document.getElementById("Password").value});
|
|
||||||
}
|
|
||||||
|
|
||||||
function keypress(e){
|
|
||||||
if(e.keyCode === 13){
|
|
||||||
e.preventDefault();
|
|
||||||
login();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const failed = listen("Login_Failed", (event) => {
|
|
||||||
document.getElementById("Incorrect").style.visibility = "visible";
|
|
||||||
})
|
|
||||||
|
|
||||||
const success = listen("Login_Success", (event) => {
|
|
||||||
window.location.href = "Admin.html";
|
|
||||||
})
|
|
@ -1,14 +1,9 @@
|
|||||||
const { invoke } = window.__TAURI__.tauri;
|
const { invoke } = window.__TAURI__.tauri;
|
||||||
const { listen } = window.__TAURI__.event;
|
const { listen } = window.__TAURI__.event;
|
||||||
const { ask, message } = window.__TAURI__.dialog;
|
const { ask } = window.__TAURI__.dialog;
|
||||||
const { exit } = window.__TAURI__.process;
|
|
||||||
const downBar = document.querySelector(".progressFinished");
|
const downBar = document.querySelector(".progressFinished");
|
||||||
//import { listen } from '@tauri-apps/api';
|
//import { listen } from '@tauri-apps/api';
|
||||||
|
|
||||||
const error = listen("Error", (error) => {
|
|
||||||
message(error.payload, {title: "Error", type: "error"});
|
|
||||||
});
|
|
||||||
|
|
||||||
const download_progress = listen("download_progress", (progress) => {
|
const download_progress = listen("download_progress", (progress) => {
|
||||||
console.log("Downloading");
|
console.log("Downloading");
|
||||||
//console.log("Downloaded "+progress.payload.downloaded/(1024*1024) +"MB / " + progress.payload.total/(1024*1024) + "MB");
|
//console.log("Downloaded "+progress.payload.downloaded/(1024*1024) +"MB / " + progress.payload.total/(1024*1024) + "MB");
|
||||||
@ -38,8 +33,6 @@ window.addEventListener("DOMContentLoaded", () => {
|
|||||||
|
|
||||||
document.getElementById("launchGame").addEventListener("click", gameLaunch);
|
document.getElementById("launchGame").addEventListener("click", gameLaunch);
|
||||||
document.getElementById("prism").addEventListener("click", prism);
|
document.getElementById("prism").addEventListener("click", prism);
|
||||||
document.getElementById("settings").addEventListener("click", login);
|
|
||||||
document.getElementById("back").addEventListener("click", back);
|
|
||||||
|
|
||||||
});
|
});
|
||||||
function packSelect() {
|
function packSelect() {
|
||||||
@ -47,15 +40,6 @@ function packSelect() {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function login(){
|
|
||||||
window.location.href = "Login.html";
|
|
||||||
}
|
|
||||||
|
|
||||||
function back(){
|
|
||||||
console.log("test");
|
|
||||||
window.location.href = "index.html";
|
|
||||||
}
|
|
||||||
|
|
||||||
function load() {
|
function load() {
|
||||||
console.log("loading");
|
console.log("loading");
|
||||||
var dropdown = document.getElementById("Modpacks");
|
var dropdown = document.getElementById("Modpacks");
|
||||||
@ -74,8 +58,8 @@ window.onload = async function() {
|
|||||||
function addModpacks(modpacks) {
|
function addModpacks(modpacks) {
|
||||||
|
|
||||||
var dropdown = document.getElementById("Modpacks");
|
var dropdown = document.getElementById("Modpacks");
|
||||||
//modpacks.sort((a, b) => a.name.localeCompare(b.name));
|
modpacks.sort((a, b) => a.name.localeCompare(b.name));
|
||||||
//modpacks.reverse();
|
modpacks.reverse();
|
||||||
for (let i = 0; i < modpacks.length; i++){
|
for (let i = 0; i < modpacks.length; i++){
|
||||||
var opt = document.createElement("option");
|
var opt = document.createElement("option");
|
||||||
opt.text = modpacks[i].name;
|
opt.text = modpacks[i].name;
|
||||||
@ -90,7 +74,7 @@ function gameLaunch() {
|
|||||||
document.getElementById("launchGame").disabled = true;
|
document.getElementById("launchGame").disabled = true;
|
||||||
document.getElementById("launchGame").textContent ="Launching...";
|
document.getElementById("launchGame").textContent ="Launching...";
|
||||||
//TODO Launch Game
|
//TODO Launch Game
|
||||||
invoke("launch_modpack", { id: selectedId}).then(() => { exit(1); });
|
invoke("launch_modpack", { id: selectedId}).then(window.close);
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -27,39 +27,6 @@
|
|||||||
text-align: center;
|
text-align: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
.vertical {
|
|
||||||
margin: 0;
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
justify-content: center;
|
|
||||||
text-align: center;
|
|
||||||
gap: 1em;
|
|
||||||
}
|
|
||||||
|
|
||||||
.container-horizontal {
|
|
||||||
margin: 0;
|
|
||||||
padding-top: 30px;
|
|
||||||
display: flex;
|
|
||||||
flex-direction: row;
|
|
||||||
justify-content: center;
|
|
||||||
text-align: center;
|
|
||||||
align-items: left;
|
|
||||||
justify-content: left;
|
|
||||||
gap: 20px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.horizontal-input {
|
|
||||||
margin: 0;
|
|
||||||
margin-left: 5px;
|
|
||||||
display: flex;
|
|
||||||
flex-direction: row;
|
|
||||||
justify-content: center;
|
|
||||||
text-align: center;
|
|
||||||
align-items: left;
|
|
||||||
justify-content: left;
|
|
||||||
gap: 20px;
|
|
||||||
}
|
|
||||||
|
|
||||||
body {
|
body {
|
||||||
margin: 0px;
|
margin: 0px;
|
||||||
padding: 0px;
|
padding: 0px;
|
||||||
@ -261,114 +228,3 @@ button {
|
|||||||
display: block;
|
display: block;
|
||||||
margin: 0.5em;
|
margin: 0.5em;
|
||||||
}
|
}
|
||||||
|
|
||||||
#settings{
|
|
||||||
width: 2em;
|
|
||||||
height: 2.5em;
|
|
||||||
top: 5px;
|
|
||||||
left: 5px;
|
|
||||||
float: right;
|
|
||||||
position: absolute;
|
|
||||||
background-image: url('assets/settings.png');
|
|
||||||
background-size:cover;
|
|
||||||
background-repeat: no-repeat;
|
|
||||||
}
|
|
||||||
|
|
||||||
#back{
|
|
||||||
width: 2em;
|
|
||||||
height: 2.5em;
|
|
||||||
top: 5px;
|
|
||||||
left: 5px;
|
|
||||||
float: right;
|
|
||||||
position: absolute;
|
|
||||||
background-image: url('assets/back.png');
|
|
||||||
background-size:cover;
|
|
||||||
background-repeat: no-repeat;
|
|
||||||
}
|
|
||||||
|
|
||||||
.container input{
|
|
||||||
margin: 0.5em;
|
|
||||||
width: 45%
|
|
||||||
}
|
|
||||||
|
|
||||||
.loginButtons{
|
|
||||||
display: flex;
|
|
||||||
margin: 0.5em;
|
|
||||||
flex-direction: row;
|
|
||||||
margin-left: auto;
|
|
||||||
margin-right: auto;
|
|
||||||
width: 50%;
|
|
||||||
gap: .5em;
|
|
||||||
}
|
|
||||||
|
|
||||||
.Error{
|
|
||||||
color: black;
|
|
||||||
background-color: red;
|
|
||||||
margin: 0;
|
|
||||||
visibility: hidden;
|
|
||||||
}
|
|
||||||
|
|
||||||
#modpacks{
|
|
||||||
width: 30%;
|
|
||||||
height: 13em;
|
|
||||||
background-color: #666565;
|
|
||||||
overflow: auto;
|
|
||||||
margin-left: 10px;
|
|
||||||
border: 1px solid;
|
|
||||||
}
|
|
||||||
|
|
||||||
.modpack {
|
|
||||||
width: 100%;
|
|
||||||
}
|
|
||||||
|
|
||||||
.modpack:hover {
|
|
||||||
background-color: lightgray;
|
|
||||||
color: black;
|
|
||||||
}
|
|
||||||
|
|
||||||
.modpack-selected {
|
|
||||||
background-color: blue;
|
|
||||||
color: white;
|
|
||||||
}
|
|
||||||
|
|
||||||
.vertical-buttons {
|
|
||||||
display: flex;
|
|
||||||
margin-left: 0.5em;
|
|
||||||
margin-top: 0;
|
|
||||||
flex-direction: column;
|
|
||||||
height: 15em;
|
|
||||||
gap: 1.5em;
|
|
||||||
}
|
|
||||||
|
|
||||||
.square-button {
|
|
||||||
width: 2em;
|
|
||||||
height: 2.5em;
|
|
||||||
top: 5px;
|
|
||||||
left: 5px;
|
|
||||||
}
|
|
||||||
|
|
||||||
#up{
|
|
||||||
background-image: url('assets/up.png');
|
|
||||||
background-size:cover;
|
|
||||||
background-repeat: no-repeat;
|
|
||||||
}
|
|
||||||
|
|
||||||
#down{
|
|
||||||
background-image: url('assets/down.png');
|
|
||||||
background-size:cover;
|
|
||||||
background-repeat: no-repeat;
|
|
||||||
}
|
|
||||||
|
|
||||||
#create{
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
#update{
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
#remove{
|
|
||||||
background-image: url('assets/remove.png');
|
|
||||||
background-size:cover;
|
|
||||||
background-repeat: no-repeat;
|
|
||||||
}
|
|
@ -1,5 +1,3 @@
|
|||||||
# FCLauncher
|
# FCLauncher
|
||||||
|
|
||||||
A launcher made to facilitate the distribution of FamilyCraft, and other modpacks that I manage.
|
Launcher for Familycraft Modpacks
|
||||||
|
|
||||||
The launcher's core guiding principle is simplicity. It makes it extremely easy to install and use any of the provided modpacks with the click of a button. There is no need to worry about versions, different instances, or anything else. Simply select a modpack and click 'Play'.
|
|
12
launcher/Cargo.toml
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
[package]
|
||||||
|
name = "launcher"
|
||||||
|
version = "0.1.0"
|
||||||
|
edition = "2021"
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
dirs = "5.0.1"
|
||||||
|
flate2 = "1.0.30"
|
||||||
|
serde_json = "1.0.117"
|
||||||
|
suppaftp = { version = "6.0.1", features = ["native-tls"] }
|
||||||
|
tar = "0.4.41"
|
||||||
|
zip-extract = "0.1.3"
|
24
launcher/res/vsftpd.crt
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
-----BEGIN CERTIFICATE-----
|
||||||
|
MIID8TCCAtmgAwIBAgIUeFYZmMrAiIFZ9/QFwUS636XZrJMwDQYJKoZIhvcNAQEL
|
||||||
|
BQAwgYcxCzAJBgNVBAYTAlVTMRAwDgYDVQQIDAdXeW9taW5nMREwDwYDVQQHDAhD
|
||||||
|
aGV5ZW5uZTERMA8GA1UECgwIUGVyc29uYWwxGzAZBgNVBAMMEmdpdGVhLnBpd2Fs
|
||||||
|
a2VyLm5ldDEjMCEGCSqGSIb3DQEJARYUc3dhbGtlckBwaXdhbGtlci5uZXQwHhcN
|
||||||
|
MjQwNjIyMDAzNzEwWhcNMjUwNjIyMDAzNzEwWjCBhzELMAkGA1UEBhMCVVMxEDAO
|
||||||
|
BgNVBAgMB1d5b21pbmcxETAPBgNVBAcMCENoZXllbm5lMREwDwYDVQQKDAhQZXJz
|
||||||
|
b25hbDEbMBkGA1UEAwwSZ2l0ZWEucGl3YWxrZXIubmV0MSMwIQYJKoZIhvcNAQkB
|
||||||
|
FhRzd2Fsa2VyQHBpd2Fsa2VyLm5ldDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCC
|
||||||
|
AQoCggEBANOo7BOAUKhbBWodZqY8U34sQhK5Zj6WkVPQrFG1MWXX3KarDtBSuZ99
|
||||||
|
PjbIoDR+Xm5MuNcJMnbeG4+EH6SrNsogHoyn7m8XJAQ/1N6kHEii4qeDzMIbcNu6
|
||||||
|
7L54ZbONBw1Sygilnavp1iPY/2GzWH5ynaT4w4hQQrmDm8GlDNjxWGnw1CpOExAs
|
||||||
|
LdUP3sF6RNtN6dX1vgYMo9ziNtRazRmDANXykgrfBrPCyjUGDsI9wnqm21qoaQ/s
|
||||||
|
w506XovYI1Q6zWVu6cWUYyCFy4mABQxOOf7doJi4h6Wbxfp4WbNdcoBDHDN4nHzo
|
||||||
|
pdrMzJ8GlZD0aCmmU+8ERvIk+IXY6+kCAwEAAaNTMFEwHQYDVR0OBBYEFJ/4/N4x
|
||||||
|
fO/5nu/snApQO7Cw6CyCMB8GA1UdIwQYMBaAFJ/4/N4xfO/5nu/snApQO7Cw6CyC
|
||||||
|
MA8GA1UdEwEB/wQFMAMBAf8wDQYJKoZIhvcNAQELBQADggEBAMFwno+imd5ApXP4
|
||||||
|
NsuX6db5GLiT6SHV65iactFUbnvqvK35KQMKVW03hOb2FPwAzEPARcPtFlENAWBl
|
||||||
|
mHphDwAmfLbHXHdiTAKJNFO7D/AOB4TG6geBFlhYvwHCVS17nzFRJvF/0APlgbO8
|
||||||
|
8f3XkmPBPudaGiuKHWdppdHCisk6CfYvNNnjguxihyUL/mDkwiKYQPcHsMYwdYM0
|
||||||
|
QWCcTNyCjnFK/pbo6dLyPAFpXE9becSEhbxvFziNelADRflLkOUSd+sfxmoLMMsA
|
||||||
|
EJajfocYQkAOiuh8uVzol9xsnKcZiujRoTSnndZsRVqfiNZaJbpvZoD/kY0aBXHo
|
||||||
|
SIh5Ff4=
|
||||||
|
-----END CERTIFICATE-----
|
56
launcher/src/ftp.rs
Normal file
@ -0,0 +1,56 @@
|
|||||||
|
use std::io::Write;
|
||||||
|
use std::path::PathBuf;
|
||||||
|
|
||||||
|
use suppaftp::FtpError;
|
||||||
|
use suppaftp::NativeTlsFtpStream;
|
||||||
|
use suppaftp::NativeTlsConnector;
|
||||||
|
use suppaftp::native_tls::Certificate;
|
||||||
|
use suppaftp::native_tls::TlsConnector;
|
||||||
|
|
||||||
|
|
||||||
|
fn ftp_connection_anonymous() -> Result<NativeTlsFtpStream, FtpError>{
|
||||||
|
ftp_connection("anonymous", "anonymous@")
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
fn ftp_connection(username: &str, password: &str) -> Result<NativeTlsFtpStream, FtpError>{
|
||||||
|
let ftp_stream = NativeTlsFtpStream::connect("gitea.piwalker.net:21").unwrap_or_else(|err|
|
||||||
|
panic!("{}", err)
|
||||||
|
);
|
||||||
|
let cert = include_bytes!("../res/vsftpd.crt");
|
||||||
|
let cert = Certificate::from_pem(cert).unwrap();
|
||||||
|
let mut ftp_stream = ftp_stream.into_secure(NativeTlsConnector::from(TlsConnector::builder().add_root_certificate(cert).build().unwrap()), "gitea.piwalker.net").unwrap();
|
||||||
|
ftp_stream.login("anonymous", "anonymous@").map(|_| Ok(ftp_stream)).unwrap()
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
pub fn ftp_retr(file: PathBuf , mut writer: impl Write, mut callback: impl FnMut(usize)) -> Result<bool, FtpError> {
|
||||||
|
let mut ftp_stream = ftp_connection_anonymous().unwrap();
|
||||||
|
let file = file.to_str().unwrap().replace("\\", "/");
|
||||||
|
let size = ftp_stream.size(&file)?;
|
||||||
|
let mut total = 0;
|
||||||
|
ftp_stream.retr(file.as_str(), |stream| {
|
||||||
|
let mut tx_bytes = [0u8; 1500];
|
||||||
|
loop {
|
||||||
|
let bytes_read = stream.read(&mut tx_bytes).unwrap();
|
||||||
|
writer.write_all(&mut tx_bytes.split_at(bytes_read).0).unwrap();
|
||||||
|
total += bytes_read;
|
||||||
|
if total == size || bytes_read == 0 {
|
||||||
|
break;
|
||||||
|
}else{
|
||||||
|
callback(bytes_read);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(true)
|
||||||
|
})?;
|
||||||
|
Ok(true)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn ftp_get_size(file: PathBuf) -> Result<usize, FtpError> {
|
||||||
|
let mut stream = ftp_connection_anonymous()?;
|
||||||
|
stream.size(file.to_str().unwrap().replace("\\", "/"))
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
54
launcher/src/java.rs
Normal file
@ -0,0 +1,54 @@
|
|||||||
|
use std::env;
|
||||||
|
use std::io::{Cursor, Seek, Read};
|
||||||
|
use std::path::{Components, Path, PathBuf};
|
||||||
|
use flate2::read::GzDecoder;
|
||||||
|
use tar::Archive;
|
||||||
|
|
||||||
|
|
||||||
|
use crate::system_dirs::get_local_data_directory;
|
||||||
|
use crate::ftp::{self, ftp_get_size};
|
||||||
|
use crate::util;
|
||||||
|
|
||||||
|
fn check_java(version: u8) -> bool {
|
||||||
|
let dir = get_local_data_directory().join("java").join(format!("java-{}-{}", version, if env::consts::OS == "windows" { "win" } else {"lin"}));
|
||||||
|
dir.exists()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn install_java(version: u8) {
|
||||||
|
if check_java(version) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
let ftp_dir = PathBuf::new().join("java").join(format!("java-{}-{}", version, if env::consts::OS == "windows" { "win.zip" } else {"lin.tar.gz"}));
|
||||||
|
let mut buff = Cursor::new(vec![]);
|
||||||
|
let mut total: usize = 0;
|
||||||
|
let size = ftp_get_size(ftp_dir.clone()).unwrap();
|
||||||
|
ftp::ftp_retr(ftp_dir, &mut buff, |data| util::download_callback(data, &mut total, size)).unwrap();
|
||||||
|
|
||||||
|
std::fs::create_dir_all(get_local_data_directory().join("java").join(format!("java-{}-{}", version, if env::consts::OS == "windows" { "win" } else {"lin"}))).unwrap();
|
||||||
|
buff.rewind().unwrap();
|
||||||
|
if env::consts::OS != "windows" {
|
||||||
|
let tar = GzDecoder::new(buff);
|
||||||
|
let mut archive = Archive::new(tar);
|
||||||
|
if !unpack_archive(archive, get_local_data_directory().join("java").join(format!("java-{}-lin", version))).is_ok() {
|
||||||
|
std::fs::remove_dir_all(get_local_data_directory().join("java").join(format!("java-{}-lin", version))).unwrap();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if !zip_extract::extract(buff, get_local_data_directory().join("java").join(format!("java-{}-win", version)).as_path(), true).is_ok() {
|
||||||
|
std::fs::remove_dir_all(get_local_data_directory().join("java").join(format!("java-{}-win", version))).unwrap();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
fn unpack_archive<T: Read>(mut archive: Archive<T>, dst: PathBuf) -> Result<(), Box<dyn std::error::Error>> {
|
||||||
|
for file in archive.entries()? {
|
||||||
|
let path = PathBuf::new().join(dst.clone());
|
||||||
|
let mut file = file?;
|
||||||
|
let file_path = file.path()?;
|
||||||
|
let mut file_path = file_path.components();
|
||||||
|
let _ = file_path.next();
|
||||||
|
let file_path = file_path.as_path();
|
||||||
|
file.unpack(path.join(file_path))?;
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
32
launcher/src/main.rs
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
use std::fs::File;
|
||||||
|
use std::io::{Cursor, Read, Seek, Write};
|
||||||
|
use std::env::temp_dir;
|
||||||
|
use std::path::{Path, PathBuf};
|
||||||
|
use serde_json::{Result, Value};
|
||||||
|
use std::process::Command;
|
||||||
|
use std::process::Child;
|
||||||
|
|
||||||
|
mod ftp;
|
||||||
|
mod prism;
|
||||||
|
mod system_dirs;
|
||||||
|
mod java;
|
||||||
|
mod util;
|
||||||
|
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
prism::install_prism().unwrap();
|
||||||
|
//let mut data = Cursor::new(vec![]);
|
||||||
|
//ftp::ftpRetr(PathBuf::new().join("fcs7").join("versions.json"), &mut data, |_| return).unwrap();
|
||||||
|
//data.seek(std::io::SeekFrom::Start(0)).unwrap();
|
||||||
|
//let v: Value = serde_json::from_reader(data).unwrap();
|
||||||
|
//println!("fcs7/{}",v[v.as_array().unwrap().len()-1]["File"].as_str().unwrap());
|
||||||
|
//println!("{}", temp_dir().join("pack.mrpack").display());
|
||||||
|
//let mut file = File::create(temp_dir().join("pack.mrpack")).unwrap();
|
||||||
|
//ftp::ftpRetr(PathBuf::new().join("fcs7").join(v[v.as_array().unwrap().len()-1]["File"].as_str().unwrap()),file, |data| println!("Transferred {} Bytes", data)).unwrap();
|
||||||
|
//let output = Command::new("prismlauncher").arg("-I").arg(temp_dir().join("pack.mrpack")).spawn();
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn test(){}
|
45
launcher/src/prism.rs
Normal file
@ -0,0 +1,45 @@
|
|||||||
|
use std::{env, io::{Cursor, Seek}, path::PathBuf};
|
||||||
|
use flate2::read::GzDecoder;
|
||||||
|
use tar::Archive;
|
||||||
|
|
||||||
|
use crate::{ftp, java, system_dirs::get_local_data_directory};
|
||||||
|
|
||||||
|
|
||||||
|
pub fn check_prism() -> bool {
|
||||||
|
let path = get_local_data_directory().join("prism");
|
||||||
|
path.exists()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn install_prism() -> Result<(), Box<dyn std::error::Error>>{
|
||||||
|
if check_prism() {
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
java::install_java(21);
|
||||||
|
let path = PathBuf::new().join("prism").join(format!("prism-{}",if env::consts::OS == "windows" {"win.zip"} else {"lin.tar.gz"}));
|
||||||
|
let size = ftp::ftp_get_size(path.clone())?;
|
||||||
|
let mut buff = Cursor::new(vec![]);
|
||||||
|
let mut total = 0;
|
||||||
|
ftp::ftp_retr(path, &mut buff, |data| {
|
||||||
|
total += data;
|
||||||
|
println!("Downloading Prism: {}MB / {}MB", total/(1024*1024), size/(1024*1024));
|
||||||
|
}).unwrap();
|
||||||
|
std::fs::create_dir_all(get_local_data_directory().join("prism"))?;
|
||||||
|
buff.rewind()?;
|
||||||
|
if env::consts::OS != "windows" {
|
||||||
|
let tar = GzDecoder::new(buff);
|
||||||
|
let mut archive = Archive::new(tar);
|
||||||
|
if !archive.unpack(get_local_data_directory().join("prism")).is_ok() {
|
||||||
|
std::fs::remove_dir_all(get_local_data_directory().join("prism"));
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if !zip_extract::extract(buff, get_local_data_directory().join("prism").as_path(), true).is_ok() {
|
||||||
|
std::fs::remove_dir_all(get_local_data_directory().join("prism"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let buff = Cursor::new(vec![]);
|
||||||
|
ftp::ftp_retr(PathBuf, writer, callback)
|
||||||
|
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
8
launcher/src/system_dirs.rs
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
use std::{env, path::{Path, PathBuf}};
|
||||||
|
use dirs::home_dir;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
pub fn get_local_data_directory() -> PathBuf {
|
||||||
|
dirs::data_local_dir().unwrap().join("FCLauncher")
|
||||||
|
}
|
6
launcher/src/util.rs
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
|
||||||
|
pub fn download_callback(count: usize, total: &mut usize, size: usize){
|
||||||
|
*total += count;
|
||||||
|
println!("Downloading {}MB / {}MB", *total/(1024*1024), size/(1024*1024));
|
||||||
|
}
|
||||||
|
|