Compare commits

..

21 Commits

Author SHA1 Message Date
NotThorny
986259d96b Bump version 2025-11-15 00:41:10 -07:00
NotThorny
97454de75e Fix default config not generating
Fixes #262
2025-11-15 00:35:51 -07:00
NotThorny
f61f4eed51 Bump version 2025-11-14 15:24:14 -07:00
NotThorny
99b45ddf52 Fix reading profile path 2025-11-14 15:24:14 -07:00
NotThorny
fafec01fe3 Fix not loading old configs from correct dir 2025-11-14 02:45:24 -07:00
NotThorny
0c910b7317 Update langs 2025-11-14 02:38:22 -07:00
NotThorny
6f2be3c5a5 Add profiles 2025-11-14 02:37:37 -07:00
NotThorny
d2b8124877 Update AIO to 6.1 2025-10-31 21:17:31 -06:00
NotThorny
247150c62a Update patch 2025-10-25 16:18:44 -06:00
NotThorny
1599c37100 Update AIO 6.0 2025-10-17 00:48:37 -06:00
NotThorny
ecb7936a8f Add 6.0 patch from @pmagixc
Bump version
2025-10-15 21:36:14 -06:00
Thoronium
a40080cca2 Merge pull request #255 from GID0317/main
Update launch_args, Fix login timeout and transparency support
2025-07-01 14:32:09 -06:00
GID
364d138779 Update launch_args and fix login timeout to use the options OptionSection for better UX and easier for theming 2025-07-01 19:35:14 +07:00
GID
f03cc0a09f Update to make the transparency enabled to make easier to support rounded corners windows 2025-07-01 19:31:39 +07:00
Thoronium
7750266a3d Bump version 2025-03-27 01:19:00 -06:00
Thoronium
028ee380f2 Replace 5.x+ individual patch files with universal 2025-03-27 01:17:24 -06:00
Thoronium
fcd08cace5 Bump version 2025-02-26 13:05:21 -07:00
Thoronium
0258aa006a Remove unused patch 2025-02-26 13:04:25 -07:00
Thoronium
8c10c00b53 Update file 2025-02-15 14:28:50 -07:00
Thoronium
fe094c952b Fix typo 2025-02-14 23:49:11 -07:00
Thoronium
63a883cf1d Bump version 2025-02-14 15:25:56 -07:00
38 changed files with 459 additions and 178 deletions

1
.gitignore vendored
View File

@@ -10,6 +10,7 @@
# production
/build
/target
# misc
.DS_Store

View File

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

2
src-tauri/Cargo.lock generated
View File

@@ -959,7 +959,7 @@ checksum = "b365fabc795046672053e29c954733ec3b05e4be654ab130fe8f1f94d7051f35"
[[package]]
name = "cultivation"
version = "1.5.5"
version = "1.6.3"
dependencies = [
"anime-launcher-sdk",
"anyhow",

View File

@@ -1,13 +1,13 @@
[package]
name = "cultivation"
version = "1.5.5"
version = "1.6.3"
description = "A custom launcher for anime game."
authors = ["KingRainbow44", "SpikeHD"]
license = ""
repository = "https://github.com/Grasscutters/Cultivation.git"
default-run = "cultivation"
edition = "2021"
rust-version = "1.58"
rust-version = "1.57"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

View File

@@ -37,11 +37,13 @@
"web_cache": "删除 webCaches 文件夹",
"launch_args": "启动参数",
"offline_mode": "离线模式",
"fix_res": "修复登录超时"
"fix_res": "修复登录超时",
"show_version": "在按钮上显示游戏版本",
"save_profile": "保存配置配置文件"
},
"downloads": {
"grasscutter_fullbuild": "下载 Grasscutter 一体化",
"grasscutter_fullquest": "下载 5.4 一体化",
"grasscutter_fullquest": "下载 6.1 一体化",
"grasscutter_stable_data": "下载 Grasscutter 稳定版数据",
"grasscutter_latest_data": "下载 Grasscutter 开发版数据",
"grasscutter_stable_data_update": "更新 Grasscutter 稳定版数据",

View File

@@ -37,11 +37,13 @@
"web_cache": "刪除 webCaches 文件夾",
"launch_args": "啟動參數",
"offline_mode": "離線模式",
"fix_res": "修復登入逾時"
"fix_res": "修復登入逾時",
"show_version": "在按鈕上顯示遊戲版本",
"save_profile": "儲存配置設定檔"
},
"downloads": {
"grasscutter_fullbuild": "下載Grasscutter多合一下載",
"grasscutter_fullquest": "下载 5.4 一体化",
"grasscutter_fullquest": "下载 6.1 一体化",
"grasscutter_stable_data": "下載Grasscutter穩定版數據Data",
"grasscutter_latest_data": "下載Grasscutter開發板數據Data",
"grasscutter_stable_data_update": "更新Grasscutter穩定版數據Data",

View File

@@ -39,11 +39,13 @@
"web_cache": "WebCaches-Ordner löschen",
"launch_args": "Start-Argumente",
"offline_mode": "Offline-Modus",
"fix_res": "Login-Zeitüberschreitung beheben"
"fix_res": "Login-Zeitüberschreitung beheben",
"show_version": "Spielversion in Schaltflächen anzeigen",
"save_profile": "Profil speichern"
},
"downloads": {
"grasscutter_fullbuild": "Grasscutter All-in-One herunterladen",
"grasscutter_fullquest": "5.4 All-in-One herunterladen",
"grasscutter_fullquest": "6.1 All-in-One herunterladen",
"grasscutter_stable_data": "Stabile Grasscutter-Daten herunterladen",
"grasscutter_latest_data": "Neueste Grasscutter-Daten herunterladen",
"grasscutter_stable_data_update": "Stabile Grasscutter-Daten aktualisieren",

View File

@@ -39,11 +39,13 @@
"web_cache": "Delete webCaches folder",
"launch_args": "Launch Args",
"offline_mode": "Offline Mode",
"fix_res": "Fix Login Timeout"
"fix_res": "Fix Login Timeout",
"show_version": "Show game version in buttons",
"save_profile": "Save profile"
},
"downloads": {
"grasscutter_fullbuild": "Download Grasscutter All-in-One",
"grasscutter_fullquest": "Download 5.4 All-in-One",
"grasscutter_fullbuild": "Download Grasscutter 4.0 All-in-One",
"grasscutter_fullquest": "Download 6.1 All-in-One",
"grasscutter_stable_data": "Download Grasscutter Stable Data",
"grasscutter_latest_data": "Download Grasscutter Latest Data",
"grasscutter_stable_data_update": "Update Grasscutter Stable Data",

View File

@@ -37,11 +37,13 @@
"web_cache": "Eliminar la carpeta webCaches",
"launch_args": "Args de lanzamiento",
"offline_mode": "Modo sin conexión",
"fix_res": "Reparar el tiempo de espera"
"fix_res": "Reparar el tiempo de espera",
"show_version": "Mostrar versión del juego en botones",
"save_profile": "Guardar perfil"
},
"downloads": {
"grasscutter_fullbuild": "Descargar datos todo en uno de Grasscutter",
"grasscutter_fullquest": "Descargar datos todo en uno de 5.4",
"grasscutter_fullquest": "Descargar datos todo en uno de 6.1",
"grasscutter_stable_data": "Descargar datos Estables de Grasscutter",
"grasscutter_latest_data": "Descargar datos más Recientes de Grasscutter",
"grasscutter_stable_data_update": "Actualizar datos estables de Grasscutter",

View File

@@ -37,11 +37,13 @@
"web_cache": "Supprimer le dossier webCaches",
"launch_args": "Arguments de lancement",
"offline_mode": "Mode hors ligne",
"fix_res": "Réparation du login"
"fix_res": "Réparation du login",
"show_version": "Afficher la version du jeu sur les boutons",
"save_profile": "Enregistrer le profil"
},
"downloads": {
"grasscutter_fullbuild": "Telecharger Grasscutter tout-en-un",
"grasscutter_fullquest": "Télécharger les 5.4 tout-en-un",
"grasscutter_fullquest": "Télécharger les 6.1 tout-en-un",
"grasscutter_stable_data": "Télécharger les donnees de Grasscutter (version stable)",
"grasscutter_latest_data": "Télécharger les donnees de Grasscutter (derniere version)",
"grasscutter_stable_data_update": "Mettre à jour les données de Grasscutter (version stable)",

View File

@@ -36,11 +36,13 @@
"web_cache": "Hapus folder webCaches",
"launch_args": "Luncurkan Args",
"offline_mode": "Mode Offline",
"fix_res": "Perbaiki batas waktu login"
"fix_res": "Perbaiki batas waktu login",
"show_version": "Tampilkan versi game pada tombol",
"save_profile": "Simpan profil"
},
"downloads": {
"grasscutter_fullbuild": "Sedang Mendownload Grasscutter Semua Dalam Satu",
"grasscutter_fullquest": "Unduh 5.4 semua dalam satu",
"grasscutter_fullquest": "Unduh 6.1 semua dalam satu",
"grasscutter_stable_data": "Sedang Mendownload Grasscutter Versi Stabil",
"grasscutter_latest_data": "Sedang Mendownload Grasscutter Data Terbaru",
"grasscutter_stable_data_update": "Memperbaharui Grasscutter Data Stabil",

View File

@@ -37,11 +37,13 @@
"web_cache": "Elimina la cartella webCaches",
"launch_args": "Argomenti di lancio",
"offline_mode": "Modalità Offline",
"fix_res": "Correggere il timeout dell'accesso"
"fix_res": "Correggere il timeout dell'accesso",
"show_version": "Mostra la versione del gioco sui pulsanti",
"save_profile": "Salva il profilo"
},
"downloads": {
"grasscutter_fullbuild": "Scarica Grasscutter Tutto-in-Uno",
"grasscutter_fullquest": "Scarica 5.4 Tutto-in-Uno",
"grasscutter_fullquest": "Scarica 6.1 Tutto-in-Uno",
"grasscutter_stable_data": "Scarica i dati di Grasscutter Stabili",
"grasscutter_latest_data": "Scarica i dati di Grasscutter Più Recenti",
"grasscutter_stable_data_update": "Aggiorna i dati di Grasscutter Stabili",

View File

@@ -37,11 +37,13 @@
"check_aagl": "その他のオプションは、他のランチャーをチェックしてください",
"grasscutter_elevation": "制限されたポートでのGCの実行方法",
"web_cache": "webCachesフォルダを削除",
"launch_args": "Launch Args"
"launch_args": "Launch Args",
"show_version": "ボタンにゲームバージョンを表示",
"save_profile": "プロフィールを保存"
},
"downloads": {
"grasscutter_fullbuild": "Grasscutter All-in-Oneをダウンロード",
"grasscutter_fullquest": "5.4 All-in-Oneをダウンロード",
"grasscutter_fullquest": "6.1 All-in-Oneをダウンロード",
"grasscutter_stable_data": "Grasscutter安定版データファイルをダウンロード",
"grasscutter_latest_data": "Grasscutter最新版データファイルをダウンロード",
"grasscutter_stable_data_update": "Grasscutter安定版データファイルをアップデート",

View File

@@ -37,11 +37,13 @@
"web_cache": "webCaches 폴더 삭제",
"launch_args": "실행 인수",
"offline_mode": "오프라인 모드",
"fix_res": "로그인 시간 초과 수정"
"fix_res": "로그인 시간 초과 수정",
"show_version": "버튼에 게임 버전 표시",
"save_profile": "프로필 저장"
},
"downloads": {
"grasscutter_fullbuild": "올인원 Grasscutter 다운로드",
"grasscutter_fullquest": "5.4 올인원 다운로드",
"grasscutter_fullquest": "6.1 올인원 다운로드",
"grasscutter_stable_data": "안정적인 데이터 다운로드",
"grasscutter_latest_data": "최신 데이터 다운로드",
"grasscutter_stable_data_update": "안정적 데이터 업데이트",

View File

@@ -35,11 +35,13 @@
"web_cache": "Dzēsiet mapi WebCaches",
"launch_args": "Palaišanas args",
"offline_mode": "Bezsaistes režīms",
"fix_res": "Fiksēt pieteikšanās laika"
"fix_res": "Fiksēt pieteikšanās laika",
"show_version": "Pogās redzamā spēles versija",
"save_profile": "Saglabāt profilu"
},
"downloads": {
"grasscutter_fullbuild": "Lejupielādējiet Grasscutter viss vienā",
"grasscutter_fullquest": "Lejupielādēt 5.4 viss vienā",
"grasscutter_fullquest": "Lejupielādēt 6.1 viss vienā",
"grasscutter_stable_data": "Lejupielādējiet Grasscutter stabilos datus",
"grasscutter_latest_data": "Lejupielādējiet Grasscutter jaunākos datus",
"grasscutter_stable_data_update": "Atjauniniet Grasscutter stabilos datus",

View File

@@ -36,11 +36,13 @@
"web_cache": "Verwijder de webCaches-map",
"launch_args": "Args starten",
"offline_mode": "Offline Modus",
"fix_res": "Time-out inloggen verhelpen"
"fix_res": "Time-out inloggen verhelpen",
"show_version": "Spelversie weergegeven op knoppen",
"save_profile": "Profiel opslaan"
},
"downloads": {
"grasscutter_fullbuild": "Grasscutter Alles-in-één Downloaden",
"grasscutter_fullquest": "Alles-in-één 5.4 downloaden",
"grasscutter_fullquest": "Alles-in-één 6.1 downloaden",
"grasscutter_stable_data": "Download Stabiele Gegevens Van Grasscutter",
"grasscutter_latest_data": "Download De Nieuwste Gegevens Van Grasscutter",
"grasscutter_stable_data_update": "Stabiele gegevens Van Grasscutter bijwerken",

View File

@@ -39,11 +39,13 @@
"web_cache": "Usuń folder webCaches",
"launch_args": "Argumenty uruchamiania",
"offline_mode": "Tryb offline",
"fix_res": "Napraw limit czasu logowania"
"fix_res": "Napraw limit czasu logowania",
"show_version": "Wersja gry wyświetlana na przyciskach",
"save_profile": "Zapisz profil"
},
"downloads": {
"grasscutter_fullbuild": "Pobierz Grasscutter (wszystko w jednym)",
"grasscutter_fullquest": "Pobierz 5.4 (wszystko w jednym)",
"grasscutter_fullquest": "Pobierz 6.1 (wszystko w jednym)",
"grasscutter_stable_data": "Pobierz stabilne dane Grasscuttera",
"grasscutter_latest_data": "Pobierz najnowsze dane Grasscuttera",
"grasscutter_stable_data_update": "Zaaktualizuj stabilne dane Grasscuttera",

View File

@@ -37,11 +37,13 @@
"web_cache": "Excluir pasta webCaches",
"launch_args": "Argumentos de lançamento",
"offline_mode": "Modo offline",
"fix_res": "Corrigir o tempo limite de login"
"fix_res": "Corrigir o tempo limite de login",
"show_version": "Versão do jogo exibida nos botões",
"save_profile": "Salvar perfil"
},
"downloads": {
"grasscutter_fullbuild": "Baixar o Grasscutter Tudo-em-Um",
"grasscutter_fullquest": "Baixar de 5.4 em um só lugar",
"grasscutter_fullquest": "Baixar de 6.1 em um só lugar",
"grasscutter_stable_data": "Baixar os Dados do Grasscutter Estável",
"grasscutter_latest_data": "Baixar os Dados do Grasscutter Mais Recente",
"grasscutter_stable_data_update": "Atualizar os Dados do Grasscutter Estável",

View File

@@ -36,11 +36,13 @@
"web_cache": "Удалить папку webCaches",
"launch_args": "Параметры запуска",
"offline_mode": "Автономный режим",
"fix_res": "Исправить таймаут входа в систему"
"fix_res": "Исправить таймаут входа в систему",
"show_version": "Версия игры отображается на кнопках",
"save_profile": "Сохранить профиль"
},
"downloads": {
"grasscutter_fullbuild": "Скачать все в одном Grasscutter",
"grasscutter_fullquest": "Скачать 5.4 все в одном",
"grasscutter_fullquest": "Скачать 6.1 все в одном",
"grasscutter_stable_data": "Скачать стабильные данные Grasscutter",
"grasscutter_latest_data": "Скачать последние данные Grasscutter",
"grasscutter_stable_data_update": "Обновить стабильные данные Grasscutter",

View File

@@ -37,11 +37,13 @@
"web_cache": "Xóa thư mục webCaches",
"launch_args": "Khởi chạy đối số",
"offline_mode": "Chế độ ngoại tuyến",
"fix_res": "Sửa lỗi hết thời gian đăng nhập"
"fix_res": "Sửa lỗi hết thời gian đăng nhập",
"show_version": "Hiển thị phiên bản trò chơi trên các nút",
"save_profile": "Lưu hồ sơ"
},
"downloads": {
"grasscutter_fullbuild": "Tải Grasscutter tất cả trong một",
"grasscutter_fullquest": "Tải 5.4 tất cả trong một",
"grasscutter_fullquest": "Tải 6.1 tất cả trong một",
"grasscutter_stable_data": "Tải dữ liệu Grasscutter bản ổn định",
"grasscutter_latest_data": "Tải 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",

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@@ -30,20 +30,33 @@ pub struct Configuration {
pub redirect_more: Option<bool>,
pub launch_args: Option<String>,
pub offline_mode: Option<bool>,
pub show_version: Option<bool>,
pub profile: Option<String>,
}
pub fn config_path() -> PathBuf {
pub fn config_path(profile: String) -> PathBuf {
let mut path = tauri::api::path::data_dir().unwrap();
path.push("cultivation");
if profile.as_str() == "default" {
path.push("configuration.json");
} else {
path.push("profiles");
path.push(profile + ".json");
}
path
}
pub fn get_config() -> Configuration {
let path = config_path();
pub fn get_config(profile_name: String) -> Configuration {
let path = config_path(profile_name.clone());
let config = std::fs::read_to_string(path).unwrap_or("{}".to_string());
let config: Configuration = serde_json::from_str(&config).unwrap_or_default();
//let default = String::from("default");
let prof = config.profile.clone().unwrap_or_default();
if prof != String::from("default") && prof != profile_name.clone() {
return get_config(prof.clone());
}
config
}

View File

@@ -101,7 +101,7 @@ async fn parse_args(inp: &Vec<String>) -> Result<Args, ArgsError> {
args.parse(inp).unwrap();
let config = config::get_config();
let config = config::get_config(String::from("default"));
if args.value_of("help")? {
println!("{}", args.full_usage());
@@ -207,6 +207,7 @@ fn main() -> Result<(), ArgsError> {
is_grasscutter_running,
restart_grasscutter,
get_theme_list,
get_profile_list,
system_helpers::run_command,
system_helpers::run_program,
system_helpers::run_program_args,
@@ -536,3 +537,20 @@ async fn get_theme_list(data_dir: String) -> Vec<HashMap<String, String>> {
themes
}
#[tauri::command]
async fn get_profile_list(data_dir: String) -> Vec<String> {
let profile_loc = format!("{}/profiles", data_dir);
// Ensure folder exists
if !std::path::Path::new(&profile_loc).exists() {
std::fs::create_dir_all(&profile_loc).unwrap();
}
let mut p_list = Vec::new();
for entry in std::fs::read_dir(&profile_loc).unwrap() {
p_list.push(entry.unwrap().file_name().into_string().unwrap());
}
p_list
}

View File

@@ -50,44 +50,45 @@ struct WhatToUnpach {
#[cfg(windows)]
#[tauri::command]
pub async fn patch_game(newer_game: bool, version: String) -> bool {
pub async fn patch_game(_newer_game: bool, version: String) -> bool {
let mut patch_path;
// Altpatch first - Now using as hoyonet switch
if newer_game {
let alt_patch_path = PathBuf::from(system_helpers::install_location()).join("altpatch");
// People keep using this when they shouldn't, 99.8% of people will never need it. Just remove for now.
// if newer_game {
// let alt_patch_path = PathBuf::from(system_helpers::install_location()).join("altpatch");
// Should handle overwriting backup with new version backup later
let backup_path = PathBuf::from(system_helpers::install_location())
.join("altpatch/original-mihoyonet.dll")
.to_str()
.unwrap()
.to_string();
let backup_exists = file_helpers::does_file_exist(&backup_path);
// // Should handle overwriting backup with new version backup later
// let backup_path = PathBuf::from(system_helpers::install_location())
// .join("altpatch/original-mihoyonet.dll")
// .to_str()
// .unwrap()
// .to_string();
// let backup_exists = file_helpers::does_file_exist(&backup_path);
if !backup_exists {
let backup = file_helpers::copy_file_with_new_name(
get_game_rsa_path().await.unwrap()
+ &String::from("/GenshinImpact_Data/Plugins/mihoyonet.dll"),
alt_patch_path.clone().to_str().unwrap().to_string(),
String::from("original-mihoyonet.dll"),
);
// if !backup_exists {
// let backup = file_helpers::copy_file_with_new_name(
// get_game_rsa_path().await.unwrap()
// + &String::from("/GenshinImpact_Data/Plugins/mihoyonet.dll"),
// alt_patch_path.clone().to_str().unwrap().to_string(),
// String::from("original-mihoyonet.dll"),
// );
if !backup {
println!("Unable to backup file!");
}
}
// if !backup {
// println!("Unable to backup file!");
// }
// }
patch_path = PathBuf::from(system_helpers::install_location()).join("altpatch/mihoyonet.dll");
// Copy the other part of patch to game files
let alt_replaced = file_helpers::copy_file_with_new_name(
patch_path.clone().to_str().unwrap().to_string(),
get_game_rsa_path().await.unwrap() + &String::from("/GenshinImpact_Data/Plugins"),
String::from("mihoyonet.dll"),
);
// patch_path = PathBuf::from(system_helpers::install_location()).join("altpatch/mihoyonet.dll");
// // Copy the other part of patch to game files
// let alt_replaced = file_helpers::copy_file_with_new_name(
// patch_path.clone().to_str().unwrap().to_string(),
// get_game_rsa_path().await.unwrap() + &String::from("/GenshinImpact_Data/Plugins"),
// String::from("mihoyonet.dll"),
// );
if !alt_replaced {
return false;
}
// if !alt_replaced {
// return false;
// }
/*** For replacing old backup file with new one, for example when version changes
* Currently replaces when it shouldn't. Will figure it out when it matters
@@ -122,7 +123,7 @@ pub async fn patch_game(newer_game: bool, version: String) -> bool {
// }
// }
// }
}
// }
// Standard patch
patch_path = PathBuf::from(system_helpers::install_location()).join("patch/version.dll");
@@ -152,8 +153,14 @@ pub async fn patch_game(newer_game: bool, version: String) -> bool {
return true;
}
// For 5.0 and up
// For 5.0 and up use universal
if i_ver > 49 {
if i_ver < 59 {
patch_path = PathBuf::from(system_helpers::install_location()).join("patch/5version.dll");
} else {
// 6.0 patch not checked/tested if works for old vers, so separate
patch_path = PathBuf::from(system_helpers::install_location()).join("patch/6version.dll");
}
let replaced50 = file_helpers::copy_file_with_new_name(
patch_path.clone().to_str().unwrap().to_string(),
get_game_rsa_path().await.unwrap(),
@@ -342,7 +349,7 @@ pub async fn unpatch_game() -> bool {
}
pub async fn get_game_rsa_path() -> Option<String> {
let config = config::get_config();
let config = config::get_config(String::from("default"));
config.game_install_path.as_ref()?;

View File

@@ -7,7 +7,7 @@
},
"package": {
"productName": "Cultivation",
"version": "1.5.5"
"version": "1.7.2"
},
"tauri": {
"allowlist": {
@@ -67,11 +67,13 @@
"windows": [
{
"fullscreen": false,
"transparent": true,
"height": 730,
"resizable": true,
"title": "Cultivation",
"width": 1280,
"decorations": false
"decorations": false,
"center": true
}
]
}

View File

@@ -170,6 +170,8 @@ export class Main extends React.Component<IProps, IState> {
}
// Ensure old configs are updated to use RSA
const updatedProfile = await getConfigOption('profile')
await setConfigOption('profile', updatedProfile)
const updatedConfig = await getConfigOption('patch_rsa')
await setConfigOption('patch_rsa', updatedConfig)

View File

@@ -14,7 +14,7 @@
}
#playButton > div {
margin-bottom: 6px;
margin-bottom: 2px;
}
#playButton .BigButton {
@@ -26,10 +26,27 @@
#serverControls {
color: white;
display: flex;
justify-content: space-between;
flex-direction: row;
text-shadow: 1px 1px 8px black;
}
#menuOptionsContainerProfiles {
justify-self: right;
align-self: right;
padding-left: 8px;
width: min-content;
}
#serverControls select {
width: 150px;
height: 30px;
border: none;
border-bottom: 2px solid #cecece;
border-radius: 6px;
}
.BottomSection .CheckboxDisplay {
border-color: #c5c5c5;
background: #fff;

View File

@@ -3,7 +3,7 @@ import Checkbox from './common/Checkbox'
import BigButton from './common/BigButton'
import TextInput from './common/TextInput'
import HelpButton from './common/HelpButton'
import { getConfig, saveConfig, setConfigOption } from '../../utils/configuration'
import { getConfig, saveConfig, setConfigOption, setProfileOption } from '../../utils/configuration'
import { translate } from '../../utils/language'
import { invoke } from '@tauri-apps/api/tauri'
@@ -43,6 +43,8 @@ interface IState {
migotoSet: boolean
unElevated: boolean
profile: string
profiles: string[]
}
export default class ServerLaunchSection extends React.Component<IProps, IState> {
@@ -66,6 +68,8 @@ export default class ServerLaunchSection extends React.Component<IProps, IState>
akebiSet: false,
migotoSet: false,
unElevated: false,
profile: 'default',
profiles: ['default'],
}
this.toggleGrasscutter = this.toggleGrasscutter.bind(this)
@@ -75,6 +79,7 @@ export default class ServerLaunchSection extends React.Component<IProps, IState>
this.toggleHttps = this.toggleHttps.bind(this)
this.launchServer = this.launchServer.bind(this)
this.setButtonLabel = this.setButtonLabel.bind(this)
this.setProfile = this.setProfile.bind(this)
listen('start_grasscutter', async () => {
this.launchServer()
@@ -102,6 +107,8 @@ export default class ServerLaunchSection extends React.Component<IProps, IState>
akebiSet: config.akebi_path !== '',
migotoSet: config.migoto_path !== '',
unElevated: config.un_elevated || false,
profile: config.profile || 'default',
profiles: (await this.getProfileList()).map((t) => t),
})
this.setButtonLabel()
@@ -149,7 +156,7 @@ export default class ServerLaunchSection extends React.Component<IProps, IState>
// Connect to proxy
if (config.toggle_grasscutter) {
const game_exe = await getGameExecutable()
let newerGame = false
const newerGame = false
const patchable = game_exe?.toLowerCase().includes('yuanshen') || game_exe?.toLowerCase().includes('genshin')
@@ -183,35 +190,36 @@ export default class ServerLaunchSection extends React.Component<IProps, IState>
const versionString = gameVersion?.major.toString() + gameVersion?.minor.toString()
if ((gameVersion?.major == 4 && gameVersion?.minor > 5) || config.newer_game) {
newerGame = true
// Keeps being misused, remove for now.
// if ((gameVersion?.major == 4 && gameVersion?.minor > 5) || config.newer_game) {
// newerGame = true
const path = (await invoke('install_location')) as string
// const path = (await invoke('install_location')) as string
const patchstring = '\\altpatch\\'
const altPatch = path + patchstring
// const patchstring = '\\altpatch\\'
// const altPatch = path + patchstring
const ALT_PATCH =
'https://autopatchhk.yuanshen.com/client_app/download/pc_zip/20231030132335_iOEfPMcbrXpiA8Ca/ScatteredFiles/GenshinImpact_Data/Plugins/mihoyonet.dll'
const pExists = (await invoke('dir_exists', {
path: altPatch,
})) as boolean
// const ALT_PATCH =
// 'https://autopatchhk.yuanshen.com/client_app/download/pc_zip/20231030132335_iOEfPMcbrXpiA8Ca/ScatteredFiles/GenshinImpact_Data/Plugins/mihoyonet.dll'
// const pExists = (await invoke('dir_exists', {
// path: altPatch,
// })) as boolean
if (!pExists) {
await invoke('dir_create', {
path: altPatch,
})
this.props.downloadHandler.addDownload(ALT_PATCH, path + '/altpatch/mihoyonet.dll')
await confirm('Please wait for the download in the bottom left to disappear, then click yes')
}
/* For custom address patch only, used in 4.5 */
// let httpString = 'http://'
// if (this.state.httpsEnabled) {
// httpString = 'https://'
// if (!pExists) {
// await invoke('dir_create', {
// path: altPatch,
// })
// this.props.downloadHandler.addDownload(ALT_PATCH, path + '/altpatch/mihoyonet.dll')
// await confirm('Please wait for the download in the bottom left to disappear, then click yes')
// }
// /* For custom address patch only, used in 4.5 */
// // let httpString = 'http://'
// // if (this.state.httpsEnabled) {
// // httpString = 'https://'
// // }
// // config.launch_args = '-server=' + httpString + this.state.ip + ':' + this.state.port
// }
// config.launch_args = '-server=' + httpString + this.state.ip + ':' + this.state.port
}
const patched = await patchGame(newerGame, versionString)
@@ -392,6 +400,25 @@ export default class ServerLaunchSection extends React.Component<IProps, IState>
}
}
async setProfile(value: string) {
this.setState({ profile: value })
await setProfileOption('profile', value)
window.location.reload()
}
async getProfileList() {
const profiles: string[] = await invoke('get_profile_list', {
dataDir: `${await dataDir()}/cultivation`,
})
const list = ['default']
profiles.forEach((t) => {
list.push(t.split('.json')[0])
})
return list
}
render() {
return (
<div id="playButton">
@@ -402,6 +429,23 @@ export default class ServerLaunchSection extends React.Component<IProps, IState>
onChange={this.toggleGrasscutter}
checked={this.state.grasscutterEnabled}
/>
<div className="OptionSection" id="menuOptionsContainerProfiles">
<div className="OptionValue" id="menuOptionsSelectProfiles">
<select
value={this.state.profile}
id="menuOptionsSelectMenuProfiles"
onChange={(event) => {
this.setProfile(event.target.value)
}}
>
{this.state.profiles.map((t) => (
<option key={t} value={t}>
{t}
</option>
))}
</select>
</div>
</div>
</div>
{this.state.grasscutterEnabled && (

View File

@@ -16,7 +16,7 @@ import { ask } from '@tauri-apps/api/dialog'
const FULL_BUILD_DOWNLOAD = 'https://github.com/NotThorny/Grasscutter/releases/download/culti-aio/GrasscutterCulti.zip' // Change to link that can be updated without modifying here
const FULL_QUEST_DOWNLOAD = 'https://github.com/NotThorny/Grasscutter/releases/download/culti-aio/GrasscutterQuests.zip'
const FULL_50_DOWNLOAD = 'https://github.com/NotThorny/Grasscutter/releases/download/culti-aio/GrasscutterLunaGC54.zip' // https://github.com/pmagixc/LunaGC_5.4.0
const FULL_50_DOWNLOAD = 'https://github.com/NotThorny/Grasscutter/releases/download/culti-aio/GrasscutterLunaGC61.zip' // https://github.com/pmagixc/LunaGC_6.0.0
const STABLE_REPO_DOWNLOAD = 'https://github.com/Grasscutters/Grasscutter/archive/refs/heads/stable.zip'
const DEV_REPO_DOWNLOAD = 'https://github.com/Grasscutters/Grasscutter/archive/refs/heads/development.zip'
const UNSTABLE_DOWNLOAD = 'https://nightly.link/Grasscutters/Grasscutter/workflows/build/unstable/Grasscutter.zip'

View File

@@ -20,3 +20,8 @@
.OptionSection .HelpButton img {
filter: invert(0%) sepia(91%) saturate(7464%) hue-rotate(101deg) brightness(0%) contrast(107%);
}
input#profile_name {
height: 25px;
border-radius: 6px;
}

View File

@@ -4,7 +4,14 @@ import { dataDir } from '@tauri-apps/api/path'
import DirInput from '../common/DirInput'
import Menu from './Menu'
import Tr, { getLanguages } from '../../../utils/language'
import { setConfigOption, getConfig, getConfigOption, Configuration } from '../../../utils/configuration'
import {
setConfigOption,
getConfig,
getConfigOption,
Configuration,
saveNewProfileConfig,
setProfileOption,
} from '../../../utils/configuration'
import Checkbox from '../common/Checkbox'
import Divider from './Divider'
import { getThemeList } from '../../../utils/themes'
@@ -57,6 +64,8 @@ interface IState {
launch_args: string
offline_mode: boolean
newer_game: boolean
show_version: boolean
profile_name: string
// Linux stuff
grasscutter_elevation: string
@@ -95,6 +104,8 @@ export default class Options extends React.Component<IProps, IState> {
launch_args: '',
offline_mode: false,
newer_game: false,
show_version: true,
profile_name: '',
// Linux stuff
grasscutter_elevation: GrasscutterElevation.None,
@@ -118,6 +129,9 @@ export default class Options extends React.Component<IProps, IState> {
this.addMigotoDelay = this.addMigotoDelay.bind(this)
this.toggleUnElevatedGame = this.toggleUnElevatedGame.bind(this)
this.setLaunchArgs = this.setLaunchArgs.bind(this)
this.toggleShowVersion = this.toggleShowVersion.bind(this)
this.setProfileName = this.setProfileName.bind(this)
this.saveProfile = this.saveProfile.bind(this)
}
async componentDidMount() {
@@ -156,6 +170,7 @@ export default class Options extends React.Component<IProps, IState> {
launch_args: config.launch_args,
offline_mode: config.offline_mode || false,
newer_game: config.newer_game || false,
show_version: config.show_version || false,
// Linux stuff
grasscutter_elevation: config.grasscutter_elevation || GrasscutterElevation.None,
@@ -350,6 +365,17 @@ export default class Options extends React.Component<IProps, IState> {
})
}
async toggleShowVersion() {
const changedVal = !(await getConfigOption('show_version'))
await setConfigOption('show_version', changedVal)
this.setState({
show_version: changedVal,
})
emit('set_config', { show_version: changedVal })
}
async setGCElevation(value: string) {
setConfigOption('grasscutter_elevation', value)
@@ -487,6 +513,28 @@ export default class Options extends React.Component<IProps, IState> {
})
}
setProfileName(text: string) {
this.setState({
profile_name: text,
})
}
async saveProfile() {
if (this.state.profile_name == '') {
alert('No name set')
return
}
const config = await getConfig()
await saveNewProfileConfig(config, this.state.profile_name)
await setProfileOption('profile', this.state.profile_name)
this.setState({
profile_name: '',
})
window.location.reload()
}
render() {
return (
<Menu closeFn={this.props.closeFn} className="Options" heading="Options">
@@ -659,6 +707,24 @@ export default class Options extends React.Component<IProps, IState> {
<Divider />
<div className="OptionSection" id="profileConfigContainer">
<div className="OptionLabel" id="menuOptionsLabelProfile">
<Tr text="options.save_profile" />
</div>
<TextInput
id="profile_name"
key="profile_name"
placeholder={'Profile name...'}
onChange={this.setProfileName}
initalValue={''}
/>
<BigButton onClick={this.saveProfile} id="saveProfile">
{'Save'}
</BigButton>
</div>
<Divider />
<div className="OptionSection" id="menuOptionsContainerGCWGame">
<div className="OptionLabel" id="menuOptionsLabelGCWDame">
<Tr text="options.grasscutter_with_game" />
@@ -711,17 +777,12 @@ export default class Options extends React.Component<IProps, IState> {
/>
</div>
</div>
<div className="OptionSection" id="menuOptionsContainerNewerGame">
<div className="OptionLabel" id="menuOptionsLabelNewerGame">
<Tr text="Patch Mihoyonet" />
<div className="OptionSection" id="menuOptionsContainerShowVer">
<div className="OptionLabel" id="menuOptionsLabelShowVer">
<Tr text="options.show_version" />
</div>
<div className="OptionValue" id="menuOptionsCheckboxNewerGame">
<Checkbox
onChange={() => this.toggleOption('newer_game')}
checked={this.state?.newer_game}
id="newerGame"
/>
<div className="OptionValue" id="menuOptionsButtonShowVer">
<Checkbox onChange={() => this.toggleShowVersion()} checked={this.state.show_version} id="showVer" />
</div>
</div>
@@ -823,9 +884,13 @@ export default class Options extends React.Component<IProps, IState> {
<Tr text="components.delete" />
</BigButton>
</div>
</div>
<div className="OptionSection" id="menuOptionsContainerLaunchArgs">
<div className="OptionLabel" id="menuOptionsLaunchArgs">
<Tr text="options.launch_args" />
</div>
<div className="OptionValue" id="menuOptionsValueLaunchArgs">
<TextInput
id="launch_args"
key="launch_args"
@@ -834,7 +899,9 @@ export default class Options extends React.Component<IProps, IState> {
value={this.state.launch_args}
/>
</div>
</div>
<div className="OptionSection" id="menuOptionsContainerFixRes">
<div className="OptionLabel" id="menuOptionsLabelFixRes">
<Tr text="options.fix_res" />
</div>
@@ -843,6 +910,7 @@ export default class Options extends React.Component<IProps, IState> {
<Tr text="components.fix" />
</BigButton>
</div>
</div>
</Menu>
)
}

View File

@@ -143,7 +143,7 @@ export default class NewsSection extends React.Component<IProps, IState> {
<tr>
<td>
Work in progress area! These numbers may be outdated, so please do not use them as reference. Latest
version: Grasscutter 1.7.4 - Cultivation 1.5.5
version: Grasscutter 1.7.4 (4.0) / Forks (6.1) - Cultivation 1.7.2
</td>
</tr>
)

View File

@@ -31,6 +31,8 @@ let defaultConfig: Configuration
launch_args: '',
offline_mode: false,
newer_game: false,
show_version: true,
profile: 'default',
// Linux stuff
grasscutter_elevation: 'None',
@@ -68,6 +70,8 @@ export interface Configuration {
launch_args: string
offline_mode: boolean
newer_game: boolean
show_version: boolean
profile: string
// Linux stuff
grasscutter_elevation: string
@@ -90,6 +94,15 @@ export async function setConfigOption<K extends keyof Configuration>(key: K, val
await saveConfig(<Configuration>config)
}
export async function setProfileOption<K extends keyof Configuration>(key: K, value: Configuration[K]): Promise<void> {
const config = await getConfig()
config[key] = value
const defaultConfig = await getDefaultConfig()
defaultConfig[key] = value
await saveProfileConfig(<Configuration>defaultConfig)
}
export async function getConfigOption<K extends keyof Configuration>(key: K): Promise<Configuration[K]> {
const config = await getConfig()
const defaults = defaultConfig
@@ -113,16 +126,82 @@ export async function getConfig() {
return parsed
}
export async function getDefaultConfig() {
const raw = await readDefaultConfigFile()
let parsed: Configuration = defaultConfig
try {
parsed = <Configuration>JSON.parse(raw)
} catch (e) {
// We could not open the file
console.log(e)
}
return parsed
}
export async function saveConfig(obj: Configuration) {
const raw = JSON.stringify(obj)
await writeConfigFile(raw)
}
export async function saveProfileConfig(obj: Configuration) {
const local = await dataDir()
const raw = JSON.stringify(obj)
const prevPath = configFilePath
configFilePath = local + 'cultivation/configuration.json'
await writeConfigFile(raw)
configFilePath = prevPath
}
export async function saveNewProfileConfig(obj: Configuration, prof: string) {
obj['profile'] = prof
const local = await dataDir()
const raw = JSON.stringify(obj)
configFilePath = local + 'cultivation/profiles/' + obj['profile'] + '.json'
const file: fs.FsTextFileOption = {
path: configFilePath,
contents: raw,
}
await fs.writeFile(file)
}
async function readConfigFile() {
const local = await dataDir()
if (!configFilePath) configFilePath = local + 'cultivation/configuration.json'
if (!configFilePath) {
configFilePath = local + 'cultivation/configuration.json'
}
const dataFiles = await fs.readDir(local + 'cultivation')
// Ensure config exists
if (!dataFiles.find((fileOrDir) => fileOrDir?.name === 'configuration.json')) {
// Create config file
const file: fs.FsTextFileOption = {
path: configFilePath,
contents: JSON.stringify(defaultConfig),
}
await fs.writeFile(file)
}
// Read existing config to get profile name
const raw = await fs.readTextFile(configFilePath)
const cfg = <Configuration>JSON.parse(raw)
// Switch file to config-specified profile
let pf = cfg['profile']
if (pf && pf != 'default') {
const pff = pf
pf = 'profiles/' + pff + '.json'
} else {
pf = 'configuration.json'
}
configFilePath = local + 'cultivation/' + pf
// Ensure Cultivation dir exists
const dirs = await fs.readDir(local)
@@ -140,23 +219,16 @@ async function readConfigFile() {
await fs.createDir(local + 'cultivation/grasscutter').catch((e) => console.log(e))
}
const dataFiles = await fs.readDir(local + 'cultivation')
// Ensure config exists
if (!dataFiles.find((fileOrDir) => fileOrDir?.name === 'configuration.json')) {
// Create config file
const file: fs.FsTextFileOption = {
path: configFilePath,
contents: JSON.stringify(defaultConfig),
}
await fs.writeFile(file)
}
// Finally, read the file
return await fs.readTextFile(configFilePath)
}
async function readDefaultConfigFile() {
const local = await dataDir()
configFilePath = local + 'cultivation/configuration.json'
return await fs.readTextFile(configFilePath)
}
async function writeConfigFile(raw: string) {
// All external config functions call readConfigFile, which ensure files exists
await fs.writeFile({