Compare commits

...

5 Commits

Author SHA1 Message Date
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
28 changed files with 317 additions and 54 deletions

View File

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

View File

@@ -1,7 +1,7 @@
{
"lang_name": "简体中文",
"main": {
"title": "Cultivation",
"title": "Cultivation: Thorny Edition",
"launch_button": "启动",
"gc_enable": "以 Grasscutter 模式连接",
"https_enable": "使用 HTTPS",
@@ -37,7 +37,9 @@
"web_cache": "删除 webCaches 文件夹",
"launch_args": "启动参数",
"offline_mode": "离线模式",
"fix_res": "修复登录超时"
"fix_res": "修复登录超时",
"show_version": "在按钮上显示游戏版本",
"save_profile": "保存配置配置文件"
},
"downloads": {
"grasscutter_fullbuild": "下载 Grasscutter 一体化",

View File

@@ -1,7 +1,7 @@
{
"lang_name": "繁體中文",
"main": {
"title": "Cultivation",
"title": "Cultivation: Thorny Edition",
"launch_button": "啟動",
"gc_enable": "以Grasscutter模式連接",
"https_enable": "使用 HTTPS",
@@ -37,7 +37,9 @@
"web_cache": "刪除 webCaches 文件夾",
"launch_args": "啟動參數",
"offline_mode": "離線模式",
"fix_res": "修復登入逾時"
"fix_res": "修復登入逾時",
"show_version": "在按鈕上顯示遊戲版本",
"save_profile": "儲存配置設定檔"
},
"downloads": {
"grasscutter_fullbuild": "下載Grasscutter多合一下載",

View File

@@ -1,7 +1,7 @@
{
"lang_name": "Deutsch",
"main": {
"title": "Cultivation",
"title": "Cultivation: Thorny Edition",
"launch_button": "Starten",
"gc_enable": "Mit Grasscutter verbinden",
"https_enable": "HTTPS verwenden",
@@ -39,7 +39,9 @@
"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",

View File

@@ -1,7 +1,7 @@
{
"lang_name": "English",
"main": {
"title": "Cultivation",
"title": "Cultivation: Thorny Edition",
"launch_button": "Launch",
"gc_enable": "Connect to Grasscutter",
"https_enable": "Use HTTPS",
@@ -39,7 +39,9 @@
"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 4.0 All-in-One",

View File

@@ -1,7 +1,7 @@
{
"lang_name": "Español",
"main": {
"title": "Cultivation",
"title": "Cultivation: Thorny Edition",
"launch_button": "Launch",
"gc_enable": "Conectar Via Grasscutter",
"https_enable": "Usar HTTPS",
@@ -37,7 +37,9 @@
"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",

View File

@@ -1,7 +1,7 @@
{
"lang_name": "Francais",
"main": {
"title": "Cultivation",
"title": "Cultivation: Thorny Edition",
"launch_button": "Lancer",
"gc_enable": "Se connecter avec Grasscutter",
"https_enable": "Utiliser HTTPS",
@@ -37,7 +37,9 @@
"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",

View File

@@ -1,7 +1,7 @@
{
"lang_name": "Indonesia",
"main": {
"title": "Cultivation",
"title": "Cultivation: Thorny Edition",
"launch_button": "Luncurkan",
"gc_enable": "Terhubung Melalui GrassCutter",
"ip_placeholder": "Alamat Server...",
@@ -36,7 +36,9 @@
"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",

View File

@@ -37,7 +37,9 @@
"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",

View File

@@ -1,7 +1,7 @@
{
"lang_name": "日本語",
"main": {
"title": "Cultivation",
"title": "Cultivation: Thorny Edition",
"launch_button": "起動",
"gc_enable": "Grasscutterに接続",
"https_enable": "HTTPS接続を使用",
@@ -37,7 +37,9 @@
"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をダウンロード",

View File

@@ -1,7 +1,7 @@
{
"lang_name": "한국어",
"main": {
"title": "Cultivation",
"title": "Cultivation: Thorny Edition",
"launch_button": "게임 시작",
"gc_enable": "Grasscutter 연결",
"https_enable": "HTTPS 사용",
@@ -37,7 +37,9 @@
"web_cache": "webCaches 폴더 삭제",
"launch_args": "실행 인수",
"offline_mode": "오프라인 모드",
"fix_res": "로그인 시간 초과 수정"
"fix_res": "로그인 시간 초과 수정",
"show_version": "버튼에 게임 버전 표시",
"save_profile": "프로필 저장"
},
"downloads": {
"grasscutter_fullbuild": "올인원 Grasscutter 다운로드",

View File

@@ -1,7 +1,7 @@
{
"lang_name": "Latviešu",
"main": {
"title": "Cultivation",
"title": "Cultivation: Thorny Edition",
"launch_button": "Palaist",
"gc_enable": "Savienot ar Grasscutter",
"https_enable": "Izm. HTTPS",
@@ -35,7 +35,9 @@
"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ā",

View File

@@ -1,7 +1,7 @@
{
"lang_name": "Nederlands",
"main": {
"title": "Cultivation",
"title": "Cultivation: Thorny Edition",
"launch_button": "Start",
"gc_enable": "Verbind Met Grasscutter",
"https_enable": "Gebruik HTTPS",
@@ -36,7 +36,9 @@
"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",

View File

@@ -39,7 +39,9 @@
"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)",

View File

@@ -37,7 +37,9 @@
"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",

View File

@@ -1,7 +1,7 @@
{
"lang_name": "Русский",
"main": {
"title": "Cultivation",
"title": "Cultivation: Thorny Edition",
"launch_button": "Запустить",
"gc_enable": "Подключиться с Grasscutter",
"https_enable": "Исп. HTTPS",
@@ -36,7 +36,9 @@
"web_cache": "Удалить папку webCaches",
"launch_args": "Параметры запуска",
"offline_mode": "Автономный режим",
"fix_res": "Исправить таймаут входа в систему"
"fix_res": "Исправить таймаут входа в систему",
"show_version": "Версия игры отображается на кнопках",
"save_profile": "Сохранить профиль"
},
"downloads": {
"grasscutter_fullbuild": "Скачать все в одном Grasscutter",

View File

@@ -1,7 +1,7 @@
{
"lang_name": "Tiếng Việt",
"main": {
"title": "Cultivation",
"title": "Cultivation: Thorny Edition",
"launch_button": "Khởi Chạy",
"gc_enable": "Kết nối qua Grasscutter",
"https_enable": "Dùng HTTPS",
@@ -37,7 +37,9 @@
"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",

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");
path.push("configuration.json");
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

@@ -349,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.6.3"
"version": "1.7.1"
},
"tauri": {
"allowlist": {

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()
@@ -393,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">
@@ -403,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

@@ -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,19 +777,14 @@ 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> */}
</div>
<Divider />

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.6.3
version: Grasscutter 1.7.4 (4.0) / Forks (6.1) - Cultivation 1.7.1
</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,69 @@ 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'
}
// 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)
@@ -157,6 +223,12 @@ async function readConfigFile() {
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({