mirror of
https://github.com/Grasscutters/Cultivation.git
synced 2026-02-04 09:25:16 +01:00
Merge pull request #151 from Grasscutters/more_cli
Passable amount of CLI options
This commit is contained in:
3
.vscode/settings.json
vendored
Normal file
3
.vscode/settings.json
vendored
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
{
|
||||||
|
"rust-analyzer.linkedProjects": [".\\src-tauri\\Cargo.toml"]
|
||||||
|
}
|
||||||
27
src-tauri/Cargo.lock
generated
27
src-tauri/Cargo.lock
generated
@@ -76,6 +76,16 @@ version = "1.0.58"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "bb07d2053ccdbe10e2af2995a2f116c1330396493dc1269f6a91d0ae82e19704"
|
checksum = "bb07d2053ccdbe10e2af2995a2f116c1330396493dc1269f6a91d0ae82e19704"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "args"
|
||||||
|
version = "2.2.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "d4b7432c65177b8d5c032d56e020dd8d407e939468479fc8c300e2d93e6d970b"
|
||||||
|
dependencies = [
|
||||||
|
"getopts",
|
||||||
|
"log",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "asn1-rs"
|
name = "asn1-rs"
|
||||||
version = "0.3.1"
|
version = "0.3.1"
|
||||||
@@ -780,11 +790,13 @@ checksum = "b365fabc795046672053e29c954733ec3b05e4be654ab130fe8f1f94d7051f35"
|
|||||||
name = "cultivation"
|
name = "cultivation"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
|
"args",
|
||||||
"cc",
|
"cc",
|
||||||
"ctrlc",
|
"ctrlc",
|
||||||
"duct",
|
"duct",
|
||||||
"file_diff",
|
"file_diff",
|
||||||
"futures-util",
|
"futures-util",
|
||||||
|
"getopts",
|
||||||
"http",
|
"http",
|
||||||
"hudsucker",
|
"hudsucker",
|
||||||
"is_elevated",
|
"is_elevated",
|
||||||
@@ -1387,6 +1399,15 @@ dependencies = [
|
|||||||
"version_check",
|
"version_check",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "getopts"
|
||||||
|
version = "0.2.21"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "14dbbfd5c71d70241ecf9e6f13737f7b5ce823821063188d7e46c41d371eebd5"
|
||||||
|
dependencies = [
|
||||||
|
"unicode-width",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "getrandom"
|
name = "getrandom"
|
||||||
version = "0.1.16"
|
version = "0.1.16"
|
||||||
@@ -4624,6 +4645,12 @@ version = "1.9.0"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "7e8820f5d777f6224dc4be3632222971ac30164d4a258d595640799554ebfd99"
|
checksum = "7e8820f5d777f6224dc4be3632222971ac30164d4a258d595640799554ebfd99"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "unicode-width"
|
||||||
|
version = "0.1.10"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "c0edd1e5b14653f783770bce4a4dabb4a5108a5370a5f5d8cfe8710c361f6c8b"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "unicode-xid"
|
name = "unicode-xid"
|
||||||
version = "0.2.3"
|
version = "0.2.3"
|
||||||
|
|||||||
@@ -26,6 +26,10 @@ sudo = "0.6.0"
|
|||||||
serde = { version = "1.0", features = ["derive"] }
|
serde = { version = "1.0", features = ["derive"] }
|
||||||
tauri = { version = "1.0.7", features = ["api-all"] }
|
tauri = { version = "1.0.7", features = ["api-all"] }
|
||||||
|
|
||||||
|
# Arg parsing
|
||||||
|
args = "2.0"
|
||||||
|
getopts = "0.2"
|
||||||
|
|
||||||
# Access system process info.
|
# Access system process info.
|
||||||
sysinfo = "0.24.6"
|
sysinfo = "0.24.6"
|
||||||
|
|
||||||
|
|||||||
44
src-tauri/src/config.rs
Normal file
44
src-tauri/src/config.rs
Normal file
@@ -0,0 +1,44 @@
|
|||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
use std::path::PathBuf;
|
||||||
|
use std::string::String;
|
||||||
|
|
||||||
|
#[derive(Serialize, Deserialize, Debug)]
|
||||||
|
pub struct Configuration {
|
||||||
|
pub toggle_grasscutter: bool,
|
||||||
|
pub game_install_path: String,
|
||||||
|
pub grasscutter_with_game: bool,
|
||||||
|
pub grasscutter_path: String,
|
||||||
|
pub java_path: String,
|
||||||
|
pub close_action: u64,
|
||||||
|
pub startup_launch: bool,
|
||||||
|
pub last_ip: String,
|
||||||
|
pub last_port: String,
|
||||||
|
pub language: String,
|
||||||
|
pub customBackground: String,
|
||||||
|
pub cert_generated: bool,
|
||||||
|
pub theme: String,
|
||||||
|
pub https_enabled: bool,
|
||||||
|
pub debug_enabled: bool,
|
||||||
|
pub patch_rsa: bool,
|
||||||
|
pub use_internal_proxy: bool,
|
||||||
|
pub wipe_login: bool,
|
||||||
|
pub horny_mode: bool,
|
||||||
|
pub auto_mongodb: bool,
|
||||||
|
pub un_elevated: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn config_path() -> PathBuf {
|
||||||
|
let mut path = tauri::api::path::data_dir().unwrap();
|
||||||
|
path.push("cultivation");
|
||||||
|
path.push("configuration.json");
|
||||||
|
|
||||||
|
path
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_config() -> Configuration {
|
||||||
|
let path = config_path();
|
||||||
|
let config = std::fs::read_to_string(path).unwrap();
|
||||||
|
let config: Configuration = serde_json::from_str(&config).unwrap();
|
||||||
|
|
||||||
|
config
|
||||||
|
}
|
||||||
@@ -3,8 +3,11 @@
|
|||||||
windows_subsystem = "windows"
|
windows_subsystem = "windows"
|
||||||
)]
|
)]
|
||||||
|
|
||||||
|
use args::{Args, ArgsError};
|
||||||
use file_helpers::dir_exists;
|
use file_helpers::dir_exists;
|
||||||
|
|
||||||
use once_cell::sync::Lazy;
|
use once_cell::sync::Lazy;
|
||||||
|
use proxy::set_proxy_addr;
|
||||||
use std::fs;
|
use std::fs;
|
||||||
use std::io::Write;
|
use std::io::Write;
|
||||||
use std::{collections::HashMap, sync::Mutex};
|
use std::{collections::HashMap, sync::Mutex};
|
||||||
@@ -18,10 +21,12 @@ use sysinfo::{Pid, ProcessExt, System, SystemExt};
|
|||||||
use crate::admin::reopen_as_admin;
|
use crate::admin::reopen_as_admin;
|
||||||
|
|
||||||
mod admin;
|
mod admin;
|
||||||
|
mod config;
|
||||||
mod downloader;
|
mod downloader;
|
||||||
mod file_helpers;
|
mod file_helpers;
|
||||||
mod gamebanana;
|
mod gamebanana;
|
||||||
mod lang;
|
mod lang;
|
||||||
|
mod patch;
|
||||||
mod proxy;
|
mod proxy;
|
||||||
mod release;
|
mod release;
|
||||||
mod system_helpers;
|
mod system_helpers;
|
||||||
@@ -36,24 +41,116 @@ fn try_flush() {
|
|||||||
std::io::stdout().flush().unwrap_or(())
|
std::io::stdout().flush().unwrap_or(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn has_arg(args: &[String], arg: &str) -> bool {
|
async fn parse_args(inp: &Vec<String>) -> Result<Args, ArgsError> {
|
||||||
args.contains(&arg.to_string())
|
let mut args = Args::new(
|
||||||
}
|
"Cultivation",
|
||||||
|
"Private server helper program for an Anime Game",
|
||||||
|
);
|
||||||
|
args.flag("h", "help", "Print various CLI args");
|
||||||
|
args.flag("p", "proxy", "Start the proxy server");
|
||||||
|
args.flag("G", "launch-game", "Launch the game");
|
||||||
|
args.flag(
|
||||||
|
"A",
|
||||||
|
"no-admin",
|
||||||
|
"Launch without requiring admin permissions",
|
||||||
|
);
|
||||||
|
args.flag(
|
||||||
|
"g",
|
||||||
|
"no-gui",
|
||||||
|
"Run in CLI mode. Requires -A to be passed as well.",
|
||||||
|
);
|
||||||
|
args.flag("s", "server", "Launch the configured GC server");
|
||||||
|
args.flag(
|
||||||
|
"P",
|
||||||
|
"patch",
|
||||||
|
"Patch your game before launching, with whatever your game version needs",
|
||||||
|
);
|
||||||
|
args.flag(
|
||||||
|
"N",
|
||||||
|
"non-elevated-game",
|
||||||
|
"Launch the game without admin permissions",
|
||||||
|
);
|
||||||
|
args.option(
|
||||||
|
"H",
|
||||||
|
"host",
|
||||||
|
"Set host to connect to (eg. 'localhost:443' or 'my.awesomeserver.com:6969)",
|
||||||
|
"SERVER_HOST",
|
||||||
|
getopts::Occur::Optional,
|
||||||
|
None,
|
||||||
|
);
|
||||||
|
args.option(
|
||||||
|
"a",
|
||||||
|
"game-args",
|
||||||
|
"Arguments to pass to the game process, if launching it",
|
||||||
|
r#""-opt-one -opt-two""#,
|
||||||
|
getopts::Occur::Optional,
|
||||||
|
None,
|
||||||
|
);
|
||||||
|
|
||||||
async fn arg_handler(args: &[String]) {
|
args.parse(inp).unwrap();
|
||||||
if has_arg(args, "--proxy") {
|
|
||||||
|
let config = config::get_config();
|
||||||
|
|
||||||
|
if args.value_of("help")? {
|
||||||
|
println!("{}", args.full_usage());
|
||||||
|
std::process::exit(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
if args.value_of("launch-game")? {
|
||||||
|
let game_path = config.game_install_path;
|
||||||
|
let game_args: String = args.value_of("game-args").unwrap_or(String::new());
|
||||||
|
|
||||||
|
// Patch if needed
|
||||||
|
if args.value_of("patch")? {
|
||||||
|
patch::patch_game().await;
|
||||||
|
}
|
||||||
|
|
||||||
|
if args.value_of("non-elevated-game")? {
|
||||||
|
system_helpers::run_un_elevated(game_path.to_string(), Some(game_args))
|
||||||
|
} else {
|
||||||
|
system_helpers::run_program(game_path.to_string(), Some(game_args))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if args.value_of("server")? {
|
||||||
|
let server_jar = config.grasscutter_path;
|
||||||
|
let mut server_path = server_jar.clone();
|
||||||
|
// Strip jar name from path
|
||||||
|
if server_path.contains('/') {
|
||||||
|
// Can never panic because of if
|
||||||
|
let len = server_jar.rfind('/').unwrap();
|
||||||
|
server_path.truncate(len);
|
||||||
|
} else if server_path.contains('\\') {
|
||||||
|
let len = server_jar.rfind('\\').unwrap();
|
||||||
|
server_path.truncate(len);
|
||||||
|
}
|
||||||
|
let java_path = config.java_path;
|
||||||
|
|
||||||
|
system_helpers::run_jar(server_jar, server_path.to_string(), java_path);
|
||||||
|
}
|
||||||
|
|
||||||
|
if args.value_of::<String>("host").is_ok() && !args.value_of::<String>("host")?.is_empty() {
|
||||||
|
let host = args.value_of::<String>("host")?;
|
||||||
|
set_proxy_addr(host);
|
||||||
|
}
|
||||||
|
|
||||||
|
if args.value_of("proxy")? {
|
||||||
|
println!("Starting proxy server...");
|
||||||
let mut pathbuf = tauri::api::path::data_dir().unwrap();
|
let mut pathbuf = tauri::api::path::data_dir().unwrap();
|
||||||
pathbuf.push("cultivation");
|
pathbuf.push("cultivation");
|
||||||
pathbuf.push("ca");
|
pathbuf.push("ca");
|
||||||
|
|
||||||
connect(8035, pathbuf.to_str().unwrap().to_string()).await;
|
connect(8035, pathbuf.to_str().unwrap().to_string()).await;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Ok(args)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn main() {
|
fn main() -> Result<(), ArgsError> {
|
||||||
let args: Vec<String> = std::env::args().collect();
|
let args: Vec<String> = std::env::args().collect();
|
||||||
|
let parsed_args = block_on(parse_args(&args)).unwrap();
|
||||||
|
|
||||||
if !is_elevated() && !has_arg(&args, "--no-admin") {
|
if !is_elevated() && !parsed_args.value_of("no-admin")? {
|
||||||
println!("===============================================================================");
|
println!("===============================================================================");
|
||||||
println!("You running as a non-elevated user. Some stuff will almost definitely not work.");
|
println!("You running as a non-elevated user. Some stuff will almost definitely not work.");
|
||||||
println!("===============================================================================");
|
println!("===============================================================================");
|
||||||
@@ -71,16 +168,15 @@ fn main() {
|
|||||||
exe_path.pop();
|
exe_path.pop();
|
||||||
std::env::set_current_dir(&exe_path).unwrap();
|
std::env::set_current_dir(&exe_path).unwrap();
|
||||||
|
|
||||||
block_on(arg_handler(&args));
|
|
||||||
|
|
||||||
// For disabled GUI
|
// For disabled GUI
|
||||||
ctrlc::set_handler(|| {
|
ctrlc::set_handler(|| {
|
||||||
disconnect();
|
disconnect();
|
||||||
|
block_on(patch::unpatch_game());
|
||||||
std::process::exit(0);
|
std::process::exit(0);
|
||||||
})
|
})
|
||||||
.unwrap_or(());
|
.unwrap_or(());
|
||||||
|
|
||||||
if !has_arg(&args, "--no-gui") {
|
if !parsed_args.value_of("no-gui")? {
|
||||||
tauri::Builder::default()
|
tauri::Builder::default()
|
||||||
.invoke_handler(tauri::generate_handler![
|
.invoke_handler(tauri::generate_handler![
|
||||||
enable_process_watcher,
|
enable_process_watcher,
|
||||||
@@ -142,6 +238,11 @@ fn main() {
|
|||||||
|
|
||||||
// Always disconnect upon closing the program
|
// Always disconnect upon closing the program
|
||||||
disconnect();
|
disconnect();
|
||||||
|
|
||||||
|
// Always unpatch game upon closing the program
|
||||||
|
block_on(patch::unpatch_game());
|
||||||
|
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tauri::command]
|
#[tauri::command]
|
||||||
|
|||||||
59
src-tauri/src/patch.rs
Normal file
59
src-tauri/src/patch.rs
Normal file
@@ -0,0 +1,59 @@
|
|||||||
|
use crate::config;
|
||||||
|
use crate::file_helpers;
|
||||||
|
use crate::system_helpers;
|
||||||
|
use std::path::PathBuf;
|
||||||
|
|
||||||
|
pub async fn patch_game() -> bool {
|
||||||
|
let patch_path = PathBuf::from(system_helpers::install_location()).join("patch/version.dll");
|
||||||
|
|
||||||
|
// Are we already patched with mhypbase? If so, that's fine, just continue as normal
|
||||||
|
let game_is_patched = file_helpers::are_files_identical(
|
||||||
|
patch_path.clone().to_str().unwrap(),
|
||||||
|
PathBuf::from(get_game_rsa_path().await.unwrap())
|
||||||
|
.join("mhypbase.dll")
|
||||||
|
.to_str()
|
||||||
|
.unwrap(),
|
||||||
|
);
|
||||||
|
|
||||||
|
// Tell user they won't be unpatched with manual mhypbase patch
|
||||||
|
if game_is_patched {
|
||||||
|
println!(
|
||||||
|
"You are already patched using mhypbase, so you will not be auto patched and unpatched!"
|
||||||
|
);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Copy the patch to game files
|
||||||
|
let replaced = file_helpers::copy_file_with_new_name(
|
||||||
|
patch_path.clone().to_str().unwrap().to_string(),
|
||||||
|
get_game_rsa_path().await.unwrap(),
|
||||||
|
String::from("version.dll"),
|
||||||
|
);
|
||||||
|
|
||||||
|
if !replaced {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
true
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn unpatch_game() -> bool {
|
||||||
|
// Just delete patch since it's not replacing any existing file
|
||||||
|
let deleted = file_helpers::delete_file(
|
||||||
|
PathBuf::from(get_game_rsa_path().await.unwrap())
|
||||||
|
.join("version.dll")
|
||||||
|
.to_str()
|
||||||
|
.unwrap()
|
||||||
|
.to_string(),
|
||||||
|
);
|
||||||
|
|
||||||
|
deleted
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn get_game_rsa_path() -> Option<String> {
|
||||||
|
let config = config::get_config();
|
||||||
|
let mut game_folder = PathBuf::from(config.game_install_path);
|
||||||
|
game_folder.pop();
|
||||||
|
|
||||||
|
Some(format!("{}/", game_folder.to_str().unwrap()).replace('\\', "/"))
|
||||||
|
}
|
||||||
@@ -47,6 +47,8 @@ pub fn set_proxy_addr(addr: String) {
|
|||||||
} else {
|
} else {
|
||||||
*SERVER.lock().unwrap() = addr;
|
*SERVER.lock().unwrap() = addr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
println!("Set server to {}", SERVER.lock().unwrap());
|
||||||
}
|
}
|
||||||
|
|
||||||
#[async_trait]
|
#[async_trait]
|
||||||
|
|||||||
@@ -11,7 +11,13 @@ use registry::{Data, Hive, Security};
|
|||||||
#[tauri::command]
|
#[tauri::command]
|
||||||
pub fn run_program(path: String, args: Option<String>) {
|
pub fn run_program(path: String, args: Option<String>) {
|
||||||
// Without unwrap_or, this can crash when UAC prompt is denied
|
// Without unwrap_or, this can crash when UAC prompt is denied
|
||||||
open::that(format!("{} {}", &path, &args.unwrap_or_else(|| "".into()))).unwrap_or(());
|
match open::with(
|
||||||
|
format!("{} {}", path, args.unwrap_or_else(|| "".into())),
|
||||||
|
path.clone(),
|
||||||
|
) {
|
||||||
|
Ok(_) => (),
|
||||||
|
Err(e) => println!("Failed to open program ({}): {}", &path, e),
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tauri::command]
|
#[tauri::command]
|
||||||
@@ -67,6 +73,8 @@ pub fn run_jar(path: String, execute_in: String, java_path: String) {
|
|||||||
format!("\"{}\" -jar \"{}\"", java_path, path)
|
format!("\"{}\" -jar \"{}\"", java_path, path)
|
||||||
};
|
};
|
||||||
|
|
||||||
|
println!("Launching .jar with command: {}", &command);
|
||||||
|
|
||||||
// Open the program from the specified path.
|
// Open the program from the specified path.
|
||||||
match open::with(
|
match open::with(
|
||||||
format!("/k cd /D \"{}\" & {}", &execute_in, &command),
|
format!("/k cd /D \"{}\" & {}", &execute_in, &command),
|
||||||
@@ -78,12 +86,13 @@ pub fn run_jar(path: String, execute_in: String, java_path: String) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[tauri::command]
|
#[tauri::command]
|
||||||
pub fn run_un_elevated(path: String) {
|
pub fn run_un_elevated(path: String, args: Option<String>) {
|
||||||
// Open the program non-elevated.
|
// Open the program non-elevated.
|
||||||
match open::with(
|
match open::with(
|
||||||
format!(
|
format!(
|
||||||
"cmd /min /C \"set __COMPAT_LAYER=RUNASINVOKER && start \"\" \"{}\"\"",
|
"cmd /min /C \"set __COMPAT_LAYER=RUNASINVOKER && start \"\" \"{}\"\" {}",
|
||||||
path
|
path,
|
||||||
|
args.unwrap_or_else(|| "".into())
|
||||||
),
|
),
|
||||||
"C:\\Windows\\System32\\cmd.exe",
|
"C:\\Windows\\System32\\cmd.exe",
|
||||||
) {
|
) {
|
||||||
|
|||||||
Reference in New Issue
Block a user