mirror of
https://github.com/Grasscutters/Cultivation.git
synced 2025-12-14 08:04:52 +01:00
Compare commits
10 Commits
mod_manage
...
v1.0.5-alp
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
79891238b6 | ||
|
|
0971f5b826 | ||
|
|
7e5f3be4fa | ||
|
|
e29e269c4c | ||
|
|
75f1eef587 | ||
|
|
43a6348b7a | ||
|
|
cc74107dfe | ||
|
|
afa40f437f | ||
|
|
a06a8af7df | ||
|
|
cd628b4f3d |
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "cultivation",
|
||||
"version": "1.0.4",
|
||||
"version": "1.0.5",
|
||||
"private": true,
|
||||
"dependencies": {
|
||||
"@tauri-apps/api": "^1.0.0-rc.5",
|
||||
|
||||
@@ -63,7 +63,11 @@
|
||||
"gc_dev_jar": "Download the latest development Grasscutter build, which includes jar file and data files.",
|
||||
"gc_stable_data": "Download the current stable Grasscutter data files, which does not come with a jar file. This is useful for updating.",
|
||||
"gc_dev_data": "Download the latest development Grasscutter data files, which does not come with a jar file. This is useful for updating.",
|
||||
"resources": "These are also required to run a Grasscutter server. This button will be grey if you have an existing resources folder with contents inside"
|
||||
"encryption": "This should usually be disabled.",
|
||||
"resources": "These are also required to run a Grasscutter server. This button will be grey if you have an existing resources folder with contents inside",
|
||||
"emergency_metadata": "In case something went wrong, restore your metadata to the latest official versions metadata.",
|
||||
"use_proxy": "Use the Cultivation internal proxy. You should have this enabled unless you use something like Fiddler",
|
||||
"patch_metadata": "Patch and unpatch your game metadata automatically. Unless playing with old/non-official versions, or you have manually patched your metadata, this should be enabled."
|
||||
},
|
||||
"swag": {
|
||||
"akebi_name": "Akebi",
|
||||
|
||||
@@ -242,6 +242,7 @@ pub fn install_ca_files(cert_path: &Path) {
|
||||
crate::system_helpers::run_command(
|
||||
"certutil",
|
||||
vec!["-user", "-addstore", "Root", cert_path.to_str().unwrap()],
|
||||
None,
|
||||
);
|
||||
println!("Installed certificate.");
|
||||
}
|
||||
|
||||
@@ -18,8 +18,6 @@ pub fn run_program_relative(path: String, args: Option<String>) {
|
||||
// Set new working directory
|
||||
std::env::set_current_dir(&path_buf).unwrap();
|
||||
|
||||
println!("Opening {} {}", &path, args.clone().unwrap_or("".into()));
|
||||
|
||||
// Without unwrap_or, this can crash when UAC prompt is denied
|
||||
open::that(format!("{} {}", &path, args.unwrap_or("".into()))).unwrap_or(());
|
||||
|
||||
@@ -28,13 +26,28 @@ pub fn run_program_relative(path: String, args: Option<String>) {
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
pub fn run_command(program: &str, args: Vec<&str>) {
|
||||
pub fn run_command(program: &str, args: Vec<&str>, relative: Option<bool>) {
|
||||
let prog = program.to_string();
|
||||
let args = args.iter().map(|s| s.to_string()).collect::<Vec<String>>();
|
||||
|
||||
// Commands should not block (this is for the reshade injector mostly)
|
||||
std::thread::spawn(move || {
|
||||
// Save the current working directory
|
||||
let cwd = std::env::current_dir().unwrap();
|
||||
|
||||
if relative.unwrap_or(false) {
|
||||
// Set the new working directory to the path before the executable
|
||||
let mut path_buf = std::path::PathBuf::from(&prog);
|
||||
path_buf.pop();
|
||||
|
||||
// Set new working directory
|
||||
std::env::set_current_dir(&path_buf).unwrap();
|
||||
}
|
||||
|
||||
cmd(prog, args).run().unwrap();
|
||||
|
||||
// Restore the original working directory
|
||||
std::env::set_current_dir(&cwd).unwrap();
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
use std::fs::{read_dir, File};
|
||||
use std::path;
|
||||
use std::thread;
|
||||
use unrar::archive::{Archive, OpenArchive};
|
||||
use unrar::archive::Archive;
|
||||
|
||||
#[tauri::command]
|
||||
pub fn unzip(
|
||||
@@ -132,9 +132,9 @@ pub fn unzip(
|
||||
fn extract_rar(
|
||||
window: &tauri::Window,
|
||||
rarfile: &String,
|
||||
f: &File,
|
||||
_f: &File,
|
||||
full_path: &path::PathBuf,
|
||||
top_level: bool,
|
||||
_top_level: bool,
|
||||
) {
|
||||
let archive = Archive::new(rarfile.clone());
|
||||
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
},
|
||||
"package": {
|
||||
"productName": "Cultivation",
|
||||
"version": "1.0.4"
|
||||
"version": "1.0.5"
|
||||
},
|
||||
"tauri": {
|
||||
"allowlist": {
|
||||
|
||||
@@ -114,9 +114,6 @@ select:focus {
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.ExtrasMenu {
|
||||
}
|
||||
|
||||
@media (max-height: 580px) {
|
||||
.BottomSection {
|
||||
height: 150px;
|
||||
|
||||
@@ -36,10 +36,6 @@ async function generateInfo() {
|
||||
alert('check your dev console and send that in #cultivation')
|
||||
}
|
||||
|
||||
function none() {
|
||||
alert('none')
|
||||
}
|
||||
|
||||
class Debug extends React.Component {
|
||||
render() {
|
||||
return (
|
||||
|
||||
@@ -24,7 +24,6 @@ import DownloadHandler from '../utils/download'
|
||||
import cogBtn from '../resources/icons/cog.svg'
|
||||
import downBtn from '../resources/icons/download.svg'
|
||||
import wrenchBtn from '../resources/icons/wrench.svg'
|
||||
import Menu from './components/menu/Menu'
|
||||
import { ExtrasMenu } from './components/menu/ExtrasMenu'
|
||||
|
||||
interface IProps {
|
||||
|
||||
@@ -2,7 +2,7 @@ import React from 'react'
|
||||
|
||||
import './HelpButton.css'
|
||||
import Help from '../../../resources/icons/help.svg'
|
||||
import MiniDialog from '../MiniDialog'
|
||||
import { translate } from '../../../utils/language'
|
||||
|
||||
interface IProps {
|
||||
children?: React.ReactNode[] | React.ReactNode
|
||||
@@ -10,45 +10,23 @@ interface IProps {
|
||||
id?: string
|
||||
}
|
||||
|
||||
interface IState {
|
||||
opened: boolean
|
||||
}
|
||||
|
||||
export default class HelpButton extends React.Component<IProps, IState> {
|
||||
export default class HelpButton extends React.Component<IProps, never> {
|
||||
constructor(props: IProps) {
|
||||
super(props)
|
||||
|
||||
this.state = {
|
||||
opened: false,
|
||||
}
|
||||
|
||||
this.setOpen = this.setOpen.bind(this)
|
||||
this.setClosed = this.setClosed.bind(this)
|
||||
this.showAlert = this.showAlert.bind(this)
|
||||
}
|
||||
|
||||
setOpen() {
|
||||
this.setState({ opened: true })
|
||||
}
|
||||
|
||||
setClosed() {
|
||||
this.setState({ opened: false })
|
||||
async showAlert() {
|
||||
if (this.props.contents) alert(await translate(this.props.contents))
|
||||
}
|
||||
|
||||
render() {
|
||||
return (
|
||||
<div className="HelpSection">
|
||||
<div className="HelpButton" onMouseEnter={this.setOpen} onMouseLeave={this.setClosed}>
|
||||
<div className="HelpButton" onClick={this.showAlert}>
|
||||
<img src={Help} />
|
||||
</div>
|
||||
|
||||
<div
|
||||
className="HelpContents"
|
||||
style={{
|
||||
display: this.state.opened ? 'block' : 'none',
|
||||
}}
|
||||
>
|
||||
<MiniDialog closeFn={this.setClosed}>{this.props.contents || this.props.children}</MiniDialog>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -57,7 +57,7 @@ export default class ProgressBar extends React.Component<IProps, IState> {
|
||||
style={{
|
||||
width: `${(() => {
|
||||
// Handles no files downloading
|
||||
if (this.state.files === 0) {
|
||||
if (this.state.files === 0 || this.state.average >= 100) {
|
||||
return '100'
|
||||
}
|
||||
|
||||
|
||||
@@ -202,9 +202,7 @@ export default class Downloads extends React.Component<IProps, IState> {
|
||||
<Tr
|
||||
text={this.state.grasscutter_set ? 'downloads.grasscutter_stable' : 'downloads.grasscutter_stable_update'}
|
||||
/>
|
||||
<HelpButton>
|
||||
<Tr text="help.gc_stable_jar" />
|
||||
</HelpButton>
|
||||
<HelpButton contents="help.gc_stable_jar" />
|
||||
</div>
|
||||
<div className="DownloadValue" id="downloadMenuButtonGCStable">
|
||||
<BigButton
|
||||
@@ -221,9 +219,7 @@ export default class Downloads extends React.Component<IProps, IState> {
|
||||
<Tr
|
||||
text={this.state.grasscutter_set ? 'downloads.grasscutter_latest' : 'downloads.grasscutter_latest_update'}
|
||||
/>
|
||||
<HelpButton>
|
||||
<Tr text="help.gc_dev_jar" />
|
||||
</HelpButton>
|
||||
<HelpButton contents="help.gc_dev_jar" />
|
||||
</div>
|
||||
<div className="DownloadValue" id="downloadMenuButtonGCDev">
|
||||
<BigButton
|
||||
@@ -247,9 +243,7 @@ export default class Downloads extends React.Component<IProps, IState> {
|
||||
: 'downloads.grasscutter_stable_data_update'
|
||||
}
|
||||
/>
|
||||
<HelpButton>
|
||||
<Tr text="help.gc_stable_data" />
|
||||
</HelpButton>
|
||||
<HelpButton contents="help.gc_stable_data" />
|
||||
</div>
|
||||
<div className="DownloadValue" id="downloadMenuButtonGCStableData">
|
||||
<BigButton
|
||||
@@ -270,9 +264,7 @@ export default class Downloads extends React.Component<IProps, IState> {
|
||||
: 'downloads.grasscutter_latest_data_update'
|
||||
}
|
||||
/>
|
||||
<HelpButton>
|
||||
<Tr text="help.gc_dev_data" />
|
||||
</HelpButton>
|
||||
<HelpButton contents="help.gc_dev_data" />
|
||||
</div>
|
||||
<div className="DownloadValue" id="downloadMenuButtonGCDevData">
|
||||
<BigButton
|
||||
@@ -290,9 +282,7 @@ export default class Downloads extends React.Component<IProps, IState> {
|
||||
<div className="DownloadMenuSection" id="downloadMenuContainerResources">
|
||||
<div className="DownloadLabel" id="downloadMenuLabelResources">
|
||||
<Tr text="downloads.resources" />
|
||||
<HelpButton>
|
||||
<Tr text="help.resources" />
|
||||
</HelpButton>
|
||||
<HelpButton contents="help.resources" />
|
||||
</div>
|
||||
<div className="DownloadValue" id="downloadMenuButtonResources">
|
||||
<BigButton
|
||||
|
||||
@@ -115,6 +115,7 @@ export class ExtrasMenu extends React.Component<IProps, IState> {
|
||||
await invoke('run_command', {
|
||||
program: config.reshade_path,
|
||||
args: [await getGameExecutable()],
|
||||
relative: true,
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import React from 'react'
|
||||
import Menu from './Menu'
|
||||
import Tr, { translate } from '../../../utils/language'
|
||||
import { translate } from '../../../utils/language'
|
||||
import DownloadHandler from '../../../utils/download'
|
||||
|
||||
import './Game.css'
|
||||
@@ -70,9 +70,7 @@ export default class Downloads extends React.Component<IProps, IState> {
|
||||
Download Game
|
||||
</BigButton>
|
||||
)}
|
||||
<HelpButton>
|
||||
<Tr text="main.game_help_text" />
|
||||
</HelpButton>
|
||||
<HelpButton contents="main.game_help_text" />
|
||||
</div>
|
||||
|
||||
<div className="GameDownloadDir">
|
||||
|
||||
@@ -16,3 +16,7 @@
|
||||
.OptionSection .BigButtonText {
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.OptionSection .HelpButton img {
|
||||
filter: invert(0%) sepia(91%) saturate(7464%) hue-rotate(101deg) brightness(0%) contrast(107%);
|
||||
}
|
||||
|
||||
@@ -14,6 +14,7 @@ import './Options.css'
|
||||
import BigButton from '../common/BigButton'
|
||||
import DownloadHandler from '../../../utils/download'
|
||||
import * as meta from '../../../utils/metadata'
|
||||
import HelpButton from '../common/HelpButton'
|
||||
|
||||
interface IProps {
|
||||
closeFn: () => void
|
||||
@@ -269,6 +270,7 @@ export default class Options extends React.Component<IProps, IState> {
|
||||
<div className="OptionSection" id="menuOptionsContainermetaDownload">
|
||||
<div className="OptionLabel" id="menuOptionsLabelmetaDownload">
|
||||
<Tr text="options.recover_metadata" />
|
||||
<HelpButton contents="help.emergency_metadata" />
|
||||
</div>
|
||||
<div className="OptionValue" id="menuOptionsButtonmetaDownload">
|
||||
<BigButton onClick={this.restoreMetadata} id="metaDownload">
|
||||
@@ -279,6 +281,7 @@ export default class Options extends React.Component<IProps, IState> {
|
||||
<div className="OptionSection" id="menuOptionsContainerPatchMeta">
|
||||
<div className="OptionLabel" id="menuOptionsLabelPatchMeta">
|
||||
<Tr text="options.patch_metadata" />
|
||||
<HelpButton contents="help.patch_metadata" />
|
||||
</div>
|
||||
<div className="OptionValue" id="menuOptionsCheckboxPatchMeta">
|
||||
<Checkbox onChange={this.toggleMetadata} checked={this.state?.patch_metadata} id="patchMeta" />
|
||||
@@ -287,6 +290,7 @@ export default class Options extends React.Component<IProps, IState> {
|
||||
<div className="OptionSection" id="menuOptionsContainerUseProxy">
|
||||
<div className="OptionLabel" id="menuOptionsLabelUseProxy">
|
||||
<Tr text="options.use_proxy" />
|
||||
<HelpButton contents="help.use_proxy" />
|
||||
</div>
|
||||
<div className="OptionValue" id="menuOptionsCheckboxUseProxy">
|
||||
<Checkbox onChange={this.toggleProxy} checked={this.state?.use_internal_proxy} id="useProxy" />
|
||||
@@ -306,6 +310,7 @@ export default class Options extends React.Component<IProps, IState> {
|
||||
<div className="OptionSection" id="menuOptionsContainerToggleEnc">
|
||||
<div className="OptionLabel" id="menuOptionsLabelToggleEnc">
|
||||
<Tr text="options.toggle_encryption" />
|
||||
<HelpButton contents="help.encryption" />
|
||||
</div>
|
||||
<div className="OptionValue" id="menuOptionsButtonToggleEnc">
|
||||
<BigButton onClick={this.toggleEncryption} id="toggleEnc">
|
||||
|
||||
@@ -21,38 +21,53 @@ export default class Tr extends React.Component<IProps, IState> {
|
||||
}
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
async componentDidMount() {
|
||||
const { text } = this.props
|
||||
getConfigOption('language').then((language: string) => {
|
||||
// Get translation file
|
||||
if (!language) language = 'en'
|
||||
let language = await getConfigOption('language')
|
||||
|
||||
invoke('get_lang', { lang: language }).then((response) => {
|
||||
const translation_obj = JSON.parse((response as string) || '{}')
|
||||
// Get translation file
|
||||
if (!language) language = 'en'
|
||||
|
||||
// Traversal
|
||||
if (text.includes('.')) {
|
||||
const keys = text.split('.')
|
||||
let translation: string | Record<string, string> = translation_obj
|
||||
const response = await invoke('get_lang', { lang: language })
|
||||
const default_resp = await invoke('get_lang', { lang: 'en' })
|
||||
|
||||
for (let i = 0; i < keys.length; i++) {
|
||||
if (!translation) {
|
||||
translation = ''
|
||||
} else {
|
||||
translation = typeof translation !== 'string' ? translation[keys[i]] : (translation as string)
|
||||
}
|
||||
}
|
||||
const translation_obj = JSON.parse((response as string) || '{}')
|
||||
const default_obj = JSON.parse((default_resp as string) || '{}')
|
||||
|
||||
this.setState({
|
||||
translated_text: translation as string,
|
||||
})
|
||||
// Traversal
|
||||
if (text.includes('.')) {
|
||||
const keys = text.split('.')
|
||||
let translation: string | Record<string, string> = translation_obj
|
||||
|
||||
for (let i = 0; i < keys.length; i++) {
|
||||
if (!translation) {
|
||||
translation = ''
|
||||
} else {
|
||||
this.setState({
|
||||
translated_text: translation_obj[text] || '',
|
||||
})
|
||||
translation = typeof translation !== 'string' ? translation[keys[i]] : (translation as string)
|
||||
}
|
||||
}
|
||||
|
||||
// If we could not find a translation, use the default one
|
||||
if (!translation) {
|
||||
translation = default_obj
|
||||
|
||||
for (let i = 0; i < keys.length; i++) {
|
||||
if (!translation) {
|
||||
translation = ''
|
||||
} else {
|
||||
translation = typeof translation !== 'string' ? translation[keys[i]] : (translation as string)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
this.setState({
|
||||
translated_text: translation as string,
|
||||
})
|
||||
})
|
||||
} else {
|
||||
this.setState({
|
||||
translated_text: translation_obj[text] || '',
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
render() {
|
||||
@@ -82,6 +97,7 @@ export async function getLanguages() {
|
||||
export async function translate(text: string) {
|
||||
const language = (await getConfigOption('language')) || 'en'
|
||||
const translation_json = JSON.parse((await invoke('get_lang', { lang: language })) || '{}')
|
||||
const default_json = JSON.parse(await invoke('get_lang', { lang: 'en' }))
|
||||
|
||||
// Traversal
|
||||
if (text.includes('.')) {
|
||||
@@ -96,6 +112,19 @@ export async function translate(text: string) {
|
||||
}
|
||||
}
|
||||
|
||||
// If we could not find a translation, use the default one
|
||||
if (!translation) {
|
||||
translation = default_json
|
||||
|
||||
for (let i = 0; i < keys.length; i++) {
|
||||
if (!translation) {
|
||||
translation = ''
|
||||
} else {
|
||||
translation = typeof translation !== 'string' ? translation[keys[i]] : (translation as string)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return translation
|
||||
} else {
|
||||
return translation_json[text] || ''
|
||||
|
||||
Reference in New Issue
Block a user