Merge tag 'v1.0.2-alpha' into patcher-2.0

# Conflicts:
#	src-tauri/Cargo.lock
#	src-tauri/src/file_helpers.rs
#	src-tauri/src/main.rs
This commit is contained in:
Benj
2022-07-12 13:37:44 +08:00
14 changed files with 671 additions and 837 deletions

View File

@@ -8,9 +8,9 @@ Consider Cultivation to be the bleeding-edge version of GrassClipper.
During this open-beta testing period, **helpful issues are appreciated**, while unhelpful ones will be closed.
## Fair Warning
Cultivation is **VERY MUCH IN BETA** and a majority of features do not work.\
There are **no official releases of Cultivation**. You are **required** to build the application from **scratch**.\
Please do **NOT install, download, or use pre-compiled versions of Cultivation**. Only use releases from this GitHub repository.
Cultivation is **VERY MUCH IN BETA**.
There are **no official releases of Cultivation**. You are **required** to build the application from **scratch** unless you want to deal with the alpha state of the current builds.
Please do **NOT install, download, or use pre-compiled versions of Cultivation found elsewhere**. Only use releases from this GitHub repository.
# Cultivation
A game launcher designed to easily proxy traffic from anime game to private servers.

View File

@@ -1,6 +1,6 @@
{
"name": "cultivation",
"version": "1.0.1",
"version": "1.0.2",
"private": true,
"dependencies": {
"@tauri-apps/api": "^1.0.0-rc.5",

1265
src-tauri/Cargo.lock generated

File diff suppressed because it is too large Load Diff

View File

@@ -27,14 +27,14 @@ serde = { version = "1.0", features = ["derive"] }
tauri = { version = "1.0.0-rc.9", features = ["api-all"] }
# Access system process info.
sysinfo = "0.23.12"
sysinfo = "0.24.6"
# ZIP-archive library.
zip-extract = "0.1.1"
zip = "0.6.2"
# For creating a "global" downloads list.
lazy_static = "1.4.0"
once_cell = "1.13.0"
# Program opener.
open = "2.1.2"
@@ -43,9 +43,6 @@ duct = "0.13.5"
# Serialization.
serde_json = "1"
# System process elevation.
runas = "0.2.1"
# Dependencies for the HTTP(S) proxy.
http = "0.2"
hudsucker = "0.17.2"

61
src-tauri/lang/vi.json Normal file
View File

@@ -0,0 +1,61 @@
{
"lang_name": "Tiếng Việt",
"main": {
"title": "Cultivation",
"launch_button": "Khởi Chạy",
"gc_enable": "Kết nối đến Grasscutter",
"https_enable": "Sử dụng HTTPS",
"ip_placeholder": "Địa chỉ máy chủ...",
"port_placeholder": "Cổng...",
"files_downloading": "Đang tải file: ",
"files_extracting": "Đang giải nén tệp tin: "
},
"options": {
"enabled": "Bật",
"disabled": "Tắt",
"game_exec": "Đường dẫn đến GenshinImpact.exe",
"grasscutter_jar": "Đường dẫn đến Grasscutter.jar",
"toggle_encryption": "Bật/Tắt mã hoá",
"java_path": "Đường dẫn Java tuỳ chỉnh",
"grasscutter_with_game": "Tự động khởi chạy Grasscutter cùng game",
"language": "Chọn ngôn ngữ",
"background": "Ảnh nền tuỳ chỉnh (đường dẫn hoặc tệp tin ảnh)",
"theme": "Chọn giao diện"
},
"downloads": {
"grasscutter_stable_data": "Tải xuống dữ liệu Grasscutter bản ổn định",
"grasscutter_latest_data": "Tải xuống dữ liệu Grasscutter bản mới nhất",
"grasscutter_stable_data_update": "Cập nhật dữ liệu Grasscutter bản ổn định",
"grasscutter_latest_data_update": "Cập nhật dữ liệu Grasscutter bản mới nhất",
"grasscutter_stable": "Tải xuống Grasscutter phiên bản ổn định",
"grasscutter_latest": "Tải xuống Grasscutter phiển bản mới nhất",
"grasscutter_stable_update": "Cập nhật Grasscutter ổn định",
"grasscutter_latest_update": "Cập nhật Grasscutter mới nhất",
"resources": "Tải xuống tài nguyên cho Grasscutter"
},
"download_status": {
"downloading": "Đang tải",
"extracting": "Đang giải nén",
"error": "Lỗi",
"finished": "Đã xong",
"stopped": "Đã dừng"
},
"components": {
"select_file": "Chọn tệp tin hoặc thư mục...",
"select_folder": "Chọn thư mục...",
"download": "Tải xuống"
},
"news": {
"latest_commits": "Cập nhật gần đây",
"latest_version": "Phiên bản mới nhất"
},
"help": {
"port_help_text": "Đảm bảo đây là cổng của server Dispatch, không phải cổng của server Game. Thường là '443'.",
"game_help_text": "Bạn không cần phải sử dụng một bản sao riêng để chơi với Grasscutter. Việc này chỉ xảy ra nếu bạn hạ phiên bản xuống 2.6 hoặc chưa cài đặt trò chơi.",
"gc_stable_jar": "Tải xuống phiên bản ổn định của Grasscutter, bảo gồm file jar và các file dữ liệu.",
"gc_dev_jar": "Tải xuống phiên bản phát triển mới nhất của Grasscutter, bảo gồm file jar và các file dữ liệu.",
"gc_stable_data": "Tải xuống bản ổn định các tệp dữ liệu của Grasscutter, không bao gồm file jar. Phù hợp khi cập nhật.",
"gc_dev_data": "Tải xuống bản phát triển mới nhất các tệp dữ liệu của Grasscutter, không bao gồm file jar. Phù hợp khi cập nhật.",
"resources": "Chúng được yêu cầu để chạy máy chủ Grasscutter. Nút này sẽ có màu xám nếu bạn có một thư mục tài nguyên có nội dung bên trong"
}
}

View File

@@ -1,4 +1,4 @@
use lazy_static::lazy_static;
use once_cell::sync::Lazy;
use std::sync::Mutex;
use std::cmp::min;
@@ -8,12 +8,7 @@ use std::io::Write;
use futures_util::StreamExt;
// This will create a downloads list that will be used to check if we should continue downloading the file
lazy_static! {
static ref DOWNLOADS: Mutex<Vec<String>> = {
let m = Vec::new();
Mutex::new(m)
};
}
static DOWNLOADS: Lazy<Mutex<Vec<String>>> = Lazy::new(|| Mutex::new(Vec::new()));
// Lots of help from: https://gist.github.com/giuliano-oliveira/4d11d6b3bb003dba3a1b53f43d81b30d
// and docs ofc
@@ -59,17 +54,17 @@ pub async fn download_file(window: tauri::Window, url: &str, path: &str) -> Resu
let chunk = match item {
Ok(itm) => itm,
Err(e) => {
emit_download_err(window, format!("Error while downloading file"), path);
emit_download_err(window, "Error while downloading file".to_string(), path);
return Err(format!("Error while downloading file: {}", e));
}
};
let vect = &chunk.to_vec()[..];
// Write bytes
match file.write_all(&vect) {
match file.write_all(vect) {
Ok(x) => x,
Err(e) => {
emit_download_err(window, format!("Error while writing file"), path);
emit_download_err(window, "Error while writing file".to_string(), path);
return Err(format!("Error while writing file: {}", e));
}
}
@@ -78,7 +73,7 @@ pub async fn download_file(window: tauri::Window, url: &str, path: &str) -> Resu
let new = min(downloaded + (chunk.len() as u64), total_size);
downloaded = new;
total_downloaded = total_downloaded + chunk.len() as u64;
total_downloaded += chunk.len() as u64;
let mut res_hash = std::collections::HashMap::new();
@@ -110,15 +105,15 @@ pub async fn download_file(window: tauri::Window, url: &str, path: &str) -> Resu
window.emit("download_finished", &path).unwrap();
// We are done
return Ok(());
Ok(())
}
pub fn emit_download_err(window: tauri::Window, msg: std::string::String, path: &str) {
pub fn emit_download_err(window: tauri::Window, msg: String, path: &str) {
let mut res_hash = std::collections::HashMap::new();
res_hash.insert(
"error".to_string(),
msg.to_string(),
msg,
);
res_hash.insert(

View File

@@ -6,28 +6,28 @@ pub fn rename(path: String, new_name: String) {
let mut new_path = path.clone();
// Check if file/folder to replace exists
if !fs::metadata(&path).is_ok() {
if fs::metadata(&path).is_err() {
return;
}
// Check if path uses forward or back slashes
if new_path.contains("\\") {
new_path = path.replace("\\", "/");
if new_path.contains('\\') {
new_path = path.replace('\\', "/");
}
let path_replaced = &path.replace(&new_path.split("/").last().unwrap(), &new_name);
let path_replaced = &path.replace(&new_path.split('/').last().unwrap(), &new_name);
fs::rename(path, &path_replaced).unwrap();
}
#[tauri::command]
pub fn dir_exists(path: &str) -> bool {
return fs::metadata(&path).is_ok();
fs::metadata(&path).is_ok()
}
#[tauri::command]
pub fn dir_is_empty(path: &str) -> bool {
return fs::read_dir(&path).unwrap().count() == 0;
fs::read_dir(&path).unwrap().count() == 0
}
#[tauri::command]
@@ -39,3 +39,23 @@ pub fn dir_delete(path: &str) {
pub fn are_files_identical(path1: &str, path2: &str) -> bool {
return diff(path1, path2);
}
#[tauri::command]
pub fn copy_file(path: String, new_path: String) -> bool {
let filename = &path.split("/").last().unwrap();
let mut new_path_buf = std::path::PathBuf::from(&new_path);
// If the new path doesn't exist, create it.
if !dir_exists(new_path_buf.pop().to_string().as_str()) {
std::fs::create_dir_all(&new_path).unwrap();
}
// Copy old to new
match std::fs::copy(&path, format!("{}/{}", new_path, filename)) {
Ok(_) => true,
Err(e) => {
println!("Failed to copy file: {}", e);
false
}
}
}

View File

@@ -7,15 +7,13 @@ pub async fn get_lang(window: tauri::Window, lang: String) -> String {
// Send contents of language file back
let lang_path: PathBuf = [&install_location(), "lang", &format!("{}.json", lang)].iter().collect();
let contents = match std::fs::read_to_string(&lang_path) {
match std::fs::read_to_string(&lang_path) {
Ok(x) => x,
Err(e) => {
emit_lang_err(window, format!("Failed to read language file: {}", e));
return "".to_string();
"".to_string()
}
}
};
return contents;
}
#[tauri::command]
@@ -23,9 +21,9 @@ pub async fn get_languages() -> std::collections::HashMap<String, String> {
// for each lang file, set the key as the filename and the value as the lang_name contained in the file
let mut languages = std::collections::HashMap::new();
let mut lang_files = std::fs::read_dir(Path::new(&install_location()).join("lang")).unwrap();
let lang_files = std::fs::read_dir(Path::new(&install_location()).join("lang")).unwrap();
while let Some(entry) = lang_files.next() {
for entry in lang_files {
let entry = entry.unwrap();
let path = entry.path();
let filename = path.file_name().unwrap().to_str().unwrap();
@@ -41,15 +39,15 @@ pub async fn get_languages() -> std::collections::HashMap<String, String> {
languages.insert(filename.to_string(), content);
}
return languages;
languages
}
pub fn emit_lang_err(window: tauri::Window, msg: std::string::String) {
pub fn emit_lang_err(window: tauri::Window, msg: String) {
let mut res_hash = std::collections::HashMap::new();
res_hash.insert(
"error".to_string(),
msg.to_string(),
msg,
);
window.emit("lang_error", &res_hash).unwrap();

View File

@@ -3,7 +3,7 @@ all(not(debug_assertions), target_os = "windows"),
windows_subsystem = "windows"
)]
use lazy_static::lazy_static;
use once_cell::sync::Lazy;
use std::{sync::Mutex, collections::HashMap};
use std::path::PathBuf;
@@ -21,21 +21,12 @@ mod proxy;
mod web;
mod metadata_patcher;
lazy_static! {
static ref WATCH_GAME_PROCESS: Mutex<String> = {
let m = "".to_string();
Mutex::new(m)
};
}
static WATCH_GAME_PROCESS: Lazy<Mutex<String>> = Lazy::new(|| Mutex::new(String::new()));
fn main() {
// Start the game process watcher.
process_watcher();
// Make BG folder if it doesn't exist.
let bg_folder: PathBuf = [&system_helpers::install_location(), "bg"].iter().collect();
std::fs::create_dir_all(&bg_folder).unwrap();
tauri::Builder::default()
.invoke_handler(tauri::generate_handler![
enable_process_watcher,
@@ -61,6 +52,7 @@ fn main() {
file_helpers::dir_exists,
file_helpers::dir_is_empty,
file_helpers::dir_delete,
file_helpers::copy_file,
file_helpers::are_files_identical,
downloader::download_file,
downloader::stop_download,
@@ -89,14 +81,9 @@ fn process_watcher() {
// Grab the game process name
let proc = WATCH_GAME_PROCESS.lock().unwrap().to_string();
if !&proc.is_empty() {
let proc_with_name = system.processes_by_exact_name(&proc);
let mut exists = false;
for _p in proc_with_name {
exists = true;
break;
}
if !proc.is_empty() {
let mut proc_with_name = system.processes_by_exact_name(&proc);
let exists = proc_with_name.next().is_some();
// If the game process closes, disable the proxy.
if !exists {
@@ -114,7 +101,7 @@ fn is_game_running() -> bool {
// Grab the game process name
let proc = WATCH_GAME_PROCESS.lock().unwrap().to_string();
return !proc.is_empty();
!proc.is_empty()
}
#[tauri::command]
@@ -145,11 +132,8 @@ fn disconnect() {
#[tauri::command]
async fn req_get(url: String) -> String {
// Send a GET request to the specified URL.
let response = web::query(&url.to_string()).await;
// Send the response body back to the client.
return response;
// Send a GET request to the specified URL and send the response body back to the client.
web::query(&url.to_string()).await
}
#[tauri::command]
@@ -185,7 +169,7 @@ async fn get_theme_list(data_dir: String) -> Vec<HashMap<String, String>> {
}
}
return themes;
themes
}
#[tauri::command]
@@ -209,7 +193,7 @@ async fn get_bg_file(bg_path: String, appdata: String) -> String {
}
// Now we check if the bg folder, which is one directory above the game_path, exists.
let bg_img_path = format!("{}\\{}", bg_path.clone().to_string(), file_name.as_str());
let bg_img_path = format!("{}\\{}", &bg_path, &file_name);
// If it doesn't, then we do not have backgrounds to grab.
if !file_helpers::dir_exists(&bg_path) {
@@ -225,15 +209,15 @@ async fn get_bg_file(bg_path: String, appdata: String) -> String {
// The image exists, lets copy it to our local '\bg' folder.
let bg_img_path_local = format!("{}\\bg\\{}", copy_loc, file_name.as_str());
return match std::fs::copy(bg_img_path, bg_img_path_local) {
match std::fs::copy(bg_img_path, bg_img_path_local) {
Ok(_) => {
// Copy was successful, lets return true.
format!("{}\\{}", copy_loc, response_data.bg_file.as_str())
format!("{}\\{}", copy_loc, response_data.bg_file)
}
Err(e) => {
// Copy failed, lets return false
println!("Failed to copy background image: {}", e);
"".to_string()
}
};
}
}

View File

@@ -3,7 +3,7 @@
* https://github.com/omjadas/hudsucker/blob/main/examples/log.rs
*/
use lazy_static::lazy_static;
use once_cell::sync::Lazy;
use std::{sync::Mutex, str::FromStr};
use rcgen::*;
@@ -30,12 +30,7 @@ async fn shutdown_signal() {
}
// Global ver for getting server address.
lazy_static! {
static ref SERVER: Mutex<String> = {
let m = "http://localhost:443".to_string();
Mutex::new(m)
};
}
static SERVER: Lazy<Mutex<String>> = Lazy::new(|| Mutex::new("http://localhost:443".to_string()));
#[derive(Clone)]
struct ProxyHandler;

View File

@@ -1,7 +1,3 @@
use std::thread;
use tauri;
use open;
use duct::cmd;
use crate::file_helpers;
@@ -9,11 +5,7 @@ use crate::file_helpers;
#[tauri::command]
pub fn run_program(path: String) {
// Open the program from the specified path.
// Open in new thread to prevent blocking.
thread::spawn(move || {
open::that(&path).unwrap();
});
}
#[tauri::command]
@@ -31,7 +23,7 @@ pub fn run_jar(path: String, execute_in: String, java_path: String) {
};
// Open the program from the specified path.
match open::with(format!("/k cd /D \"{}\" & {}", &execute_in, &command).to_string(), "C:\\Windows\\System32\\cmd.exe") {
match open::with(format!("/k cd /D \"{}\" & {}", &execute_in, &command), "C:\\Windows\\System32\\cmd.exe") {
Ok(_) => (),
Err(e) => println!("Failed to open jar ({} from {}): {}", &path, &execute_in, e),
};
@@ -46,25 +38,6 @@ pub fn open_in_browser(url: String) {
};
}
#[tauri::command]
pub fn copy_file(path: String, new_path: String) -> bool {
let filename = &path.split("/").last().unwrap();
let mut new_path_buf = std::path::PathBuf::from(&new_path);
// If the new path doesn't exist, create it.
if !file_helpers::dir_exists(new_path_buf.pop().to_string().as_str()) {
std::fs::create_dir_all(&new_path).unwrap();
}
// Copy old to new
match std::fs::copy(&path, format!("{}/{}", new_path, filename)) {
Ok(_) => true,
Err(e) => {
println!("Failed to copy file: {}", e);
false
}
}
}
#[tauri::command]
pub fn copy_file_with_new_name(path: String, new_path: String, new_name: String) -> bool {

View File

@@ -1,5 +1,3 @@
use zip_extract;
use zip;
use std::fs::File;
use std::path;
use std::thread;

View File

@@ -4,7 +4,7 @@ pub(crate) async fn query(site: &str) -> String {
let client = reqwest::Client::new();
let response = client.get(site).header(USER_AGENT, "cultivation").send().await.unwrap();
return response.text().await.unwrap();
response.text().await.unwrap()
}
#[tauri::command]
@@ -14,5 +14,5 @@ pub(crate) async fn valid_url(url: String) -> bool {
let response = client.get(url).header(USER_AGENT, "cultivation").send().await.unwrap();
return response.status().as_str() == "200";
response.status().as_str() == "200"
}

View File

@@ -7,7 +7,7 @@
},
"package": {
"productName": "Cultivation",
"version": "1.0.1"
"version": "1.0.2"
},
"tauri": {
"allowlist": {