mirror of
https://github.com/Grasscutters/Cultivation.git
synced 2025-12-15 00:24:45 +01:00
Merge branch 'main' into patching
This commit is contained in:
@@ -62,20 +62,27 @@
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
align-items: flex-start;
|
||||
|
||||
height: 100%;
|
||||
max-height: 60px;
|
||||
}
|
||||
|
||||
#officialPlay {
|
||||
width: 60%
|
||||
.ServerLaunchButtons .BigButton {
|
||||
margin-right: 8px;
|
||||
}
|
||||
|
||||
#officialPlay {
|
||||
max-width: 60%;
|
||||
flex-grow: 1;
|
||||
}
|
||||
|
||||
#akebiLaunch,
|
||||
#serverLaunch {
|
||||
width: 5%;
|
||||
}
|
||||
|
||||
.AkebiIcon,
|
||||
.ServerIcon {
|
||||
height: 20px;
|
||||
filter: invert(28%) sepia(28%) saturate(1141%) hue-rotate(352deg) brightness(96%) contrast(88%);
|
||||
|
||||
@@ -8,6 +8,8 @@ import { translate } from '../../utils/language'
|
||||
import { invoke } from '@tauri-apps/api/tauri'
|
||||
|
||||
import Server from '../../resources/icons/server.svg'
|
||||
import Akebi from '../../resources/icons/akebi.svg'
|
||||
|
||||
import './ServerLaunchSection.css'
|
||||
import {dataDir} from '@tauri-apps/api/path'
|
||||
import { getGameExecutable } from '../../utils/game'
|
||||
@@ -31,6 +33,8 @@ interface IState {
|
||||
|
||||
httpsLabel: string;
|
||||
httpsEnabled: boolean;
|
||||
|
||||
swag: boolean;
|
||||
}
|
||||
|
||||
export default class ServerLaunchSection extends React.Component<IProps, IState> {
|
||||
@@ -47,11 +51,13 @@ export default class ServerLaunchSection extends React.Component<IProps, IState>
|
||||
portPlaceholder: '',
|
||||
portHelpText: '',
|
||||
httpsLabel: '',
|
||||
httpsEnabled: false
|
||||
httpsEnabled: false,
|
||||
swag: false
|
||||
}
|
||||
|
||||
this.toggleGrasscutter = this.toggleGrasscutter.bind(this)
|
||||
this.playGame = this.playGame.bind(this)
|
||||
this.launchAkebi = this.launchAkebi.bind(this)
|
||||
this.setIp = this.setIp.bind(this)
|
||||
this.setPort = this.setPort.bind(this)
|
||||
this.toggleHttps = this.toggleHttps.bind(this)
|
||||
@@ -71,6 +77,7 @@ export default class ServerLaunchSection extends React.Component<IProps, IState>
|
||||
portHelpText: await translate('help.port_help_text'),
|
||||
httpsLabel: await translate('main.https_enable'),
|
||||
httpsEnabled: config.https_enabled || false,
|
||||
swag: config.swag_mode || false
|
||||
})
|
||||
}
|
||||
|
||||
@@ -87,7 +94,7 @@ export default class ServerLaunchSection extends React.Component<IProps, IState>
|
||||
await saveConfig(config)
|
||||
}
|
||||
|
||||
async playGame() {
|
||||
async playGame(exe?: string, proc_name?: string) {
|
||||
const config = await getConfig()
|
||||
|
||||
if(!await getGameExecutable()) {
|
||||
@@ -113,7 +120,7 @@ export default class ServerLaunchSection extends React.Component<IProps, IState>
|
||||
// Set IP
|
||||
await invoke('set_proxy_addr', { addr: (this.state.httpsEnabled ? 'https':'http') + '://' + this.state.ip + ':' + this.state.port })
|
||||
await invoke('enable_process_watcher', {
|
||||
process: game_exe
|
||||
process: proc_name || game_exe
|
||||
})
|
||||
|
||||
// Connect to proxy
|
||||
@@ -143,18 +150,11 @@ export default class ServerLaunchSection extends React.Component<IProps, IState>
|
||||
|
||||
// Launch the program
|
||||
const gameExists = await invoke('dir_exists', {
|
||||
path: config.game_install_path
|
||||
path: exe || config.game_install_path
|
||||
})
|
||||
|
||||
if (gameExists) {
|
||||
await invoke('run_program', {
|
||||
path: config.game_install_path
|
||||
})
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
alert('Game not found! At: ' + config.game_install_path)
|
||||
if (gameExists) await invoke('run_program', { path: exe || config.game_install_path })
|
||||
else alert('Game not found! At: ' + (exe || config.game_install_path))
|
||||
}
|
||||
|
||||
async launchServer() {
|
||||
@@ -178,6 +178,16 @@ export default class ServerLaunchSection extends React.Component<IProps, IState>
|
||||
})
|
||||
}
|
||||
|
||||
async launchAkebi() {
|
||||
const config = await getConfig()
|
||||
|
||||
// Get game exe from game path, so we can watch it
|
||||
const pathArr = config.game_install_path.replace(/\\/g, '/').split('/')
|
||||
const gameExec = pathArr[pathArr.length - 1]
|
||||
|
||||
await this.playGame(config.akebi_path, gameExec)
|
||||
}
|
||||
|
||||
setIp(text: string) {
|
||||
this.setState({
|
||||
ip: text
|
||||
@@ -222,13 +232,19 @@ export default class ServerLaunchSection extends React.Component<IProps, IState>
|
||||
<Checkbox id="httpsEnable" label={this.state.httpsLabel} onChange={this.toggleHttps} checked={this.state.httpsEnabled} />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
<div className="ServerLaunchButtons" id="serverLaunchContainer">
|
||||
<BigButton onClick={this.playGame} id="officialPlay">{this.state.buttonLabel}</BigButton>
|
||||
{
|
||||
this.state.swag && (
|
||||
<BigButton onClick={this.launchAkebi} id="akebiLaunch">
|
||||
<img className="AkebiIcon" id="akebiIcon" src={Akebi} />
|
||||
</BigButton>
|
||||
)
|
||||
}
|
||||
<BigButton onClick={this.launchServer} id="serverLaunch">
|
||||
<img className="ServerIcon" id="serverLaunchIcon" src={Server} />
|
||||
</BigButton>
|
||||
|
||||
@@ -25,4 +25,30 @@
|
||||
#version {
|
||||
margin: 0px 6px;
|
||||
color: #434343;
|
||||
}
|
||||
|
||||
#unassumingButton {
|
||||
font-weight: bold;
|
||||
margin: 0px 6px;
|
||||
color: #141414;
|
||||
|
||||
transition: color 0.2s ease-in-out;
|
||||
}
|
||||
|
||||
#unassumingButton:hover {
|
||||
color: #434343;
|
||||
}
|
||||
|
||||
#unassumingButton.spin {
|
||||
color: #fff;
|
||||
animation: spin 0.5s ease-in-out;
|
||||
}
|
||||
|
||||
@keyframes spin {
|
||||
from {
|
||||
transform: rotate(0deg);
|
||||
}
|
||||
to {
|
||||
transform: rotate(360deg);
|
||||
}
|
||||
}
|
||||
@@ -10,6 +10,7 @@ import gameBtn from '../../resources/icons/game.svg'
|
||||
import Tr from '../../utils/language'
|
||||
|
||||
import './TopBar.css'
|
||||
import { getConfig, setConfigOption } from '../../utils/configuration'
|
||||
|
||||
interface IProps {
|
||||
optFunc: () => void;
|
||||
@@ -19,13 +20,21 @@ interface IProps {
|
||||
|
||||
interface IState {
|
||||
version: string;
|
||||
clicks: number;
|
||||
intv: NodeJS.Timeout | null;
|
||||
}
|
||||
|
||||
export default class TopBar extends React.Component<IProps, IState> {
|
||||
constructor(props: IProps) {
|
||||
super(props)
|
||||
|
||||
this.state = { version: '0.0.0' }
|
||||
this.state = {
|
||||
version: '0.0.0',
|
||||
clicks: 0,
|
||||
intv: null
|
||||
}
|
||||
|
||||
this.activateClick = this.activateClick.bind(this)
|
||||
}
|
||||
|
||||
async componentDidMount() {
|
||||
@@ -41,6 +50,39 @@ export default class TopBar extends React.Component<IProps, IState> {
|
||||
appWindow.minimize()
|
||||
}
|
||||
|
||||
async activateClick() {
|
||||
const config = await getConfig()
|
||||
|
||||
// They already got it, no need to reactivate
|
||||
if (config.swag_mode) return
|
||||
|
||||
if (this.state.clicks === 2) {
|
||||
setTimeout(() => {
|
||||
// Gotta clear it so it goes back to regular colors
|
||||
this.setState({
|
||||
clicks: 0
|
||||
})
|
||||
}, 600)
|
||||
|
||||
// Activate... SWAG MODE
|
||||
await setConfigOption('swag_mode', true)
|
||||
|
||||
// Reload the window
|
||||
window.location.reload()
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
if (this.state.clicks < 3) {
|
||||
this.setState({
|
||||
clicks: this.state.clicks + 1,
|
||||
intv: setTimeout(() => this.setState({ clicks: 0 }), 1500)
|
||||
})
|
||||
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
render() {
|
||||
return (
|
||||
<div className="TopBar" id="topBarContainer" data-tauri-drag-region>
|
||||
@@ -50,6 +92,16 @@ export default class TopBar extends React.Component<IProps, IState> {
|
||||
</span>
|
||||
<span data-tauri-drag-region id="version">{this.state?.version}</span>
|
||||
</div>
|
||||
{
|
||||
/**
|
||||
* HEY YOU
|
||||
*
|
||||
* If you're looking at the source code to find the swag mode thing, that's okay! If you're not, move along...
|
||||
* Just do me a favor and don't go telling everyone about how you found it. If you are just helping someone who
|
||||
* for some reason needs it, that's fine, but not EVERYONE needs it, which is why it exists in the first place.
|
||||
*/
|
||||
}
|
||||
<div id="unassumingButton" className={this.state.clicks === 2 ? 'spin' : ''} onClick={this.activateClick}>?</div>
|
||||
<div className="TopBtns" id="topBarButtonContainer">
|
||||
<div id="closeBtn" onClick={this.handleClose} className='TopButton'>
|
||||
<img src={closeIcon} alt="close" />
|
||||
|
||||
@@ -14,6 +14,12 @@
|
||||
border-radius: 10px;
|
||||
|
||||
box-shadow: 0px 0px 5px 3px rgba(0, 0, 0, 0.2);
|
||||
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
.Menu::-webkit-scrollbar {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.MenuInner {
|
||||
|
||||
@@ -18,16 +18,20 @@ interface IProps {
|
||||
}
|
||||
|
||||
interface IState {
|
||||
game_install_path: string;
|
||||
grasscutter_path: string;
|
||||
java_path: string;
|
||||
grasscutter_with_game: boolean;
|
||||
language_options: { [key: string]: string }[];
|
||||
current_language: string;
|
||||
bg_url_or_path: string;
|
||||
themes: string[];
|
||||
theme: string;
|
||||
encryption: boolean;
|
||||
game_install_path: string
|
||||
grasscutter_path: string
|
||||
java_path: string
|
||||
grasscutter_with_game: boolean
|
||||
language_options: { [key: string]: string }[],
|
||||
current_language: string
|
||||
bg_url_or_path: string
|
||||
themes: string[]
|
||||
theme: string
|
||||
encryption: boolean
|
||||
swag: boolean
|
||||
|
||||
// Swag stuff
|
||||
akebi_path: string
|
||||
}
|
||||
|
||||
export default class Options extends React.Component<IProps, IState> {
|
||||
@@ -45,11 +49,16 @@ export default class Options extends React.Component<IProps, IState> {
|
||||
themes: ['default'],
|
||||
theme: '',
|
||||
encryption: false,
|
||||
swag: false,
|
||||
|
||||
// Swag stuff
|
||||
akebi_path: '',
|
||||
}
|
||||
|
||||
this.setGameExecutable = this.setGameExecutable.bind(this)
|
||||
this.setGrasscutterJar = this.setGrasscutterJar.bind(this)
|
||||
this.setJavaPath = this.setJavaPath.bind(this)
|
||||
this.setAkebi = this.setAkebi.bind(this)
|
||||
this.toggleGrasscutterWithGame = this.toggleGrasscutterWithGame.bind(this)
|
||||
this.setCustomBackground = this.setCustomBackground.bind(this)
|
||||
this.toggleEncryption = this.toggleEncryption.bind(this)
|
||||
@@ -75,6 +84,10 @@ export default class Options extends React.Component<IProps, IState> {
|
||||
themes: (await getThemeList()).map((t) => t.name),
|
||||
theme: config.theme || 'default',
|
||||
encryption: await translate(encEnabled ? 'options.enabled' : 'options.disabled'),
|
||||
swag: config.swag_mode || false,
|
||||
|
||||
// Swag stuff
|
||||
akebi_path: config.akebi_path || '',
|
||||
})
|
||||
|
||||
this.forceUpdate()
|
||||
@@ -104,6 +117,14 @@ export default class Options extends React.Component<IProps, IState> {
|
||||
})
|
||||
}
|
||||
|
||||
setAkebi(value: string) {
|
||||
setConfigOption('akebi_path', value)
|
||||
|
||||
this.setState({
|
||||
akebi_path: value
|
||||
})
|
||||
}
|
||||
|
||||
async setLanguage(value: string) {
|
||||
await setConfigOption('language', value)
|
||||
window.location.reload()
|
||||
@@ -180,8 +201,20 @@ export default class Options extends React.Component<IProps, IState> {
|
||||
<DirInput onChange={this.setGameExecutable} value={this.state?.game_install_path} extensions={['exe']} />
|
||||
</div>
|
||||
</div>
|
||||
<div className="OptionSection" id="menuOptionsContainerGCJar">
|
||||
<div className="OptionLabel" id="menuOptionsLabelGCJar">
|
||||
{
|
||||
this.state.swag && (
|
||||
<div className='OptionSection' id="menuOptionsContainerAkebi">
|
||||
<div className='OptionLabel' id="menuOptionsLabelAkebi">
|
||||
<Tr text="swag.akebi" />
|
||||
</div>
|
||||
<div className='OptionValue' id="menuOptionsDirAkebi">
|
||||
<DirInput onChange={this.setAkebi} value={this.state?.akebi_path} extensions={['exe']} />
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
<div className='OptionSection' id="menuOptionsContainerGCJar">
|
||||
<div className='OptionLabel' id="menuOptionsLabelGCJar">
|
||||
<Tr text="options.grasscutter_jar" />
|
||||
</div>
|
||||
<div className="OptionValue" id="menuOptionsDirGCJar">
|
||||
|
||||
Reference in New Issue
Block a user