Compare commits

...

7 Commits

Author SHA1 Message Date
Thoronium
0ef50b889b Merge pull request #251 from navidmafi/main
fix(web): standardize utils/game.ts for cross-platform (needs windows test)
2025-11-27 00:33:36 -07:00
Thoronium
d9b820c842 Return proper value from getGameExecutable
getGameExecutable returns the executable name only.
basename can remove filetype instead of splitting.
2025-11-27 00:31:12 -07:00
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
Navid Mafi
0370576c11 fix(web): cross-platform game and data paths on 2025-02-17 23:40:30 +03:30
19 changed files with 58 additions and 134 deletions

View File

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

View File

@@ -1,7 +1,7 @@
{ {
"lang_name": "简体中文", "lang_name": "简体中文",
"main": { "main": {
"title": "Cultivation: Thorny Edition", "title": "Cultivation",
"launch_button": "启动", "launch_button": "启动",
"gc_enable": "以 Grasscutter 模式连接", "gc_enable": "以 Grasscutter 模式连接",
"https_enable": "使用 HTTPS", "https_enable": "使用 HTTPS",

View File

@@ -1,7 +1,7 @@
{ {
"lang_name": "繁體中文", "lang_name": "繁體中文",
"main": { "main": {
"title": "Cultivation: Thorny Edition", "title": "Cultivation",
"launch_button": "啟動", "launch_button": "啟動",
"gc_enable": "以Grasscutter模式連接", "gc_enable": "以Grasscutter模式連接",
"https_enable": "使用 HTTPS", "https_enable": "使用 HTTPS",

View File

@@ -1,7 +1,7 @@
{ {
"lang_name": "Deutsch", "lang_name": "Deutsch",
"main": { "main": {
"title": "Cultivation: Thorny Edition", "title": "Cultivation",
"launch_button": "Starten", "launch_button": "Starten",
"gc_enable": "Mit Grasscutter verbinden", "gc_enable": "Mit Grasscutter verbinden",
"https_enable": "HTTPS verwenden", "https_enable": "HTTPS verwenden",

View File

@@ -1,7 +1,7 @@
{ {
"lang_name": "English", "lang_name": "English",
"main": { "main": {
"title": "Cultivation: Thorny Edition", "title": "Cultivation",
"launch_button": "Launch", "launch_button": "Launch",
"gc_enable": "Connect to Grasscutter", "gc_enable": "Connect to Grasscutter",
"https_enable": "Use HTTPS", "https_enable": "Use HTTPS",

View File

@@ -1,7 +1,7 @@
{ {
"lang_name": "Español", "lang_name": "Español",
"main": { "main": {
"title": "Cultivation: Thorny Edition", "title": "Cultivation",
"launch_button": "Launch", "launch_button": "Launch",
"gc_enable": "Conectar Via Grasscutter", "gc_enable": "Conectar Via Grasscutter",
"https_enable": "Usar HTTPS", "https_enable": "Usar HTTPS",

View File

@@ -1,7 +1,7 @@
{ {
"lang_name": "Francais", "lang_name": "Francais",
"main": { "main": {
"title": "Cultivation: Thorny Edition", "title": "Cultivation",
"launch_button": "Lancer", "launch_button": "Lancer",
"gc_enable": "Se connecter avec Grasscutter", "gc_enable": "Se connecter avec Grasscutter",
"https_enable": "Utiliser HTTPS", "https_enable": "Utiliser HTTPS",

View File

@@ -1,7 +1,7 @@
{ {
"lang_name": "Indonesia", "lang_name": "Indonesia",
"main": { "main": {
"title": "Cultivation: Thorny Edition", "title": "Cultivation",
"launch_button": "Luncurkan", "launch_button": "Luncurkan",
"gc_enable": "Terhubung Melalui GrassCutter", "gc_enable": "Terhubung Melalui GrassCutter",
"ip_placeholder": "Alamat Server...", "ip_placeholder": "Alamat Server...",

View File

@@ -1,7 +1,7 @@
{ {
"lang_name": "日本語", "lang_name": "日本語",
"main": { "main": {
"title": "Cultivation: Thorny Edition", "title": "Cultivation",
"launch_button": "起動", "launch_button": "起動",
"gc_enable": "Grasscutterに接続", "gc_enable": "Grasscutterに接続",
"https_enable": "HTTPS接続を使用", "https_enable": "HTTPS接続を使用",

View File

@@ -1,7 +1,7 @@
{ {
"lang_name": "한국어", "lang_name": "한국어",
"main": { "main": {
"title": "Cultivation: Thorny Edition", "title": "Cultivation",
"launch_button": "게임 시작", "launch_button": "게임 시작",
"gc_enable": "Grasscutter 연결", "gc_enable": "Grasscutter 연결",
"https_enable": "HTTPS 사용", "https_enable": "HTTPS 사용",

View File

@@ -1,7 +1,7 @@
{ {
"lang_name": "Latviešu", "lang_name": "Latviešu",
"main": { "main": {
"title": "Cultivation: Thorny Edition", "title": "Cultivation",
"launch_button": "Palaist", "launch_button": "Palaist",
"gc_enable": "Savienot ar Grasscutter", "gc_enable": "Savienot ar Grasscutter",
"https_enable": "Izm. HTTPS", "https_enable": "Izm. HTTPS",

View File

@@ -1,7 +1,7 @@
{ {
"lang_name": "Nederlands", "lang_name": "Nederlands",
"main": { "main": {
"title": "Cultivation: Thorny Edition", "title": "Cultivation",
"launch_button": "Start", "launch_button": "Start",
"gc_enable": "Verbind Met Grasscutter", "gc_enable": "Verbind Met Grasscutter",
"https_enable": "Gebruik HTTPS", "https_enable": "Gebruik HTTPS",

View File

@@ -1,7 +1,7 @@
{ {
"lang_name": "Русский", "lang_name": "Русский",
"main": { "main": {
"title": "Cultivation: Thorny Edition", "title": "Cultivation",
"launch_button": "Запустить", "launch_button": "Запустить",
"gc_enable": "Подключиться с Grasscutter", "gc_enable": "Подключиться с Grasscutter",
"https_enable": "Исп. HTTPS", "https_enable": "Исп. HTTPS",

View File

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

View File

@@ -40,22 +40,22 @@ pub fn config_path(profile: String) -> PathBuf {
if profile.as_str() == "default" { if profile.as_str() == "default" {
path.push("configuration.json"); path.push("configuration.json");
} else { } else {
path.push("profile"); path.push("profiles");
path.push(profile); path.push(profile + ".json");
} }
path path
} }
pub fn get_config(profile_name: String) -> Configuration { pub fn get_config(profile_name: String) -> Configuration {
let path = config_path(profile_name); let path = config_path(profile_name.clone());
let config = std::fs::read_to_string(path).unwrap_or("{}".to_string()); let config = std::fs::read_to_string(path).unwrap_or("{}".to_string());
let config: Configuration = serde_json::from_str(&config).unwrap_or_default(); let config: Configuration = serde_json::from_str(&config).unwrap_or_default();
let default = String::from("default"); //let default = String::from("default");
let prof = config.profile.as_ref().unwrap_or(&default); let prof = config.profile.clone().unwrap_or_default();
if *prof != String::from("default") { if prof != String::from("default") && prof != profile_name.clone() {
get_config(prof.clone()); return get_config(prof.clone());
} }
config config

View File

@@ -7,7 +7,7 @@
}, },
"package": { "package": {
"productName": "Cultivation", "productName": "Cultivation",
"version": "1.7.0" "version": "1.7.2"
}, },
"tauri": { "tauri": {
"allowlist": { "allowlist": {

View File

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

View File

@@ -177,6 +177,19 @@ async function readConfigFile() {
configFilePath = local + 'cultivation/configuration.json' 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 // Read existing config to get profile name
const raw = await fs.readTextFile(configFilePath) const raw = await fs.readTextFile(configFilePath)
const cfg = <Configuration>JSON.parse(raw) const cfg = <Configuration>JSON.parse(raw)
@@ -206,19 +219,6 @@ async function readConfigFile() {
await fs.createDir(local + 'cultivation/grasscutter').catch((e) => console.log(e)) 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 // Finally, read the file
return await fs.readTextFile(configFilePath) return await fs.readTextFile(configFilePath)
} }

View File

@@ -1,110 +1,34 @@
import { invoke } from '@tauri-apps/api' import { invoke } from '@tauri-apps/api'
import { getConfig } from './configuration' import { basename, dirname, join } from '@tauri-apps/api/path'
import { getConfigOption } from './configuration'
export const getGrasscutterJar = () => getConfigOption('grasscutter_path')
export async function getGameExecutable() { export async function getGameExecutable() {
const config = await getConfig() const exe_path = await getConfigOption('game_install_path')
return await basename(exe_path)
if (!config.game_install_path) { }
return null
}
const pathArr = config.game_install_path.replace(/\\/g, '/').split('/')
return pathArr[pathArr.length - 1]
}
export async function getGrasscutterJar() {
const config = await getConfig()
if (!config.grasscutter_path) {
return null
}
const pathArr = config.grasscutter_path.replace(/\\/g, '/').split('/')
return pathArr[pathArr.length - 1]
}
export async function getGameFolder() {
const config = await getConfig()
if (!config.game_install_path) {
return null
}
const pathArr = config.game_install_path.replace(/\\/g, '/').split('/')
pathArr.pop()
const path = pathArr.join('/')
return path
}
export async function getGameDataFolder() {
const gameExec = await getGameExecutable()
if (!gameExec) {
return null
}
return (await getGameFolder()) + '\\' + gameExec.replace('.exe', '_Data')
}
export async function getGameVersion() { export async function getGameVersion() {
const GameData = await getGameDataFolder() const execPath = await getConfigOption('game_install_path')
const platform = await invoke('get_platform') const rootPath = await dirname(execPath)
const baseName = (await basename(execPath, ".exe"))
if (!GameData) { const datapath = await join(rootPath, `${baseName}_Data`)
return null const asbPath = await join(datapath, 'StreamingAssets', 'asb_settings.json')
} const hasAsb = await invoke<boolean>('dir_exists', { path: asbPath })
let hasAsb = await invoke('dir_exists', {
path: GameData + '\\StreamingAssets\\asb_settings.json',
})
if (platform != 'windows') {
hasAsb = await invoke('dir_exists', {
path: GameData + '/StreamingAssets/asb_settings.json',
})
}
if (!hasAsb) { if (!hasAsb) {
// For games that cannot determine game version const versionFile = await join(datapath, 'StreamingAssets', 'BinaryVersion.bytes')
let otherGameVer: string = await invoke('read_file', { const rawVersion = await invoke<string>('read_file', { path: versionFile })
path: GameData + '\\StreamingAssets\\BinaryVersion.bytes', if (!rawVersion) return null
})
if (platform != 'windows') { const [major, minor] = rawVersion.split('.').map(Number)
otherGameVer = await invoke('read_file', { return { major, minor, release: 0 }
path: GameData + '/StreamingAssets/BinaryVersion.bytes',
})
}
const versionRaw = otherGameVer.split('.')
const version = {
major: parseInt(versionRaw[0]),
minor: parseInt(versionRaw[1]),
// This will probably never matter, just use major/minor. If needed, full version values are near EOF
release: 0,
}
if (otherGameVer == null || otherGameVer.length < 1) {
return null
}
return version
} }
const settings = JSON.parse( const settings = JSON.parse(await invoke<string>('read_file', { path: asbPath }))
await invoke('read_file', { const [major, minorRelease] = settings.variance.split('.')
path: GameData + '\\StreamingAssets\\asb_settings.json', const [minor, release] = minorRelease.split('_').map(Number)
})
)
const versionRaw = settings.variance.split('.') return { major: parseInt(major), minor, release }
const version = {
major: parseInt(versionRaw[0]),
minor: parseInt(versionRaw[1].split('_')[0]),
release: parseInt(versionRaw[1].split('_')[1]),
}
return version
} }