Compare commits

...

153 Commits

Author SHA1 Message Date
SpikeHD
78c2a8d0c8 Merge pull request #142 from NotThorny/AutoMongo
Automation & Bugfix
2023-04-03 09:13:24 -07:00
Thoronium
fe420e9837 Translations for MongoDB setting 2023-04-02 22:49:45 -06:00
Thoronium
57576f399b Change AIO zip name 2023-04-02 22:36:09 -06:00
Thoronium
8d45b3d11d Log when a service isn't found 2023-04-02 22:01:03 -06:00
Thoronium
1ff96f9c7e Merge branch 'AutoMongo' of https://github.com/NotThorny/Cultivation into AutoMongo 2023-04-02 21:15:33 -06:00
Thoronium
e834022ca7 Automation
- MongoDB tied to Grasscutter rather than game
- Grasscutter restarts when changing encryption if it is running
- AIO link updates without needing manual edits
2023-04-02 21:15:25 -06:00
Thoronium
c72e502603 Update tauri.conf.json 2023-04-02 13:35:51 -06:00
Thoronium
5d6bd72083 Fix 3dmigoto path setting
Apparently broken since 1.0.21 whoops
2023-04-02 13:34:56 -06:00
Thoronium
ef3ba2a045 Update Grasscutter versions
Add auto MongoDB option
2023-04-01 12:41:50 -06:00
SpikeHD
90b86b42d0 codefix 2023-03-23 08:53:42 -07:00
SpikeHD
01d12178ba Merge pull request #138 from NotThorny/CultivationP
Ease-of-use & QoL Additions, Updates, & Fixes
2023-03-23 08:28:40 -07:00
Thoronium
b67e0992f1 Add back frontend-checks 2023-03-23 01:02:00 -06:00
Thoronium
a949754dce Update patch version 2023-03-23 00:54:13 -06:00
Thoronium
cf536b853a De-Thornify 2023-03-23 00:31:44 -06:00
Thoronium
19c8537e91 Bug fix 2023-03-22 23:11:10 -06:00
Thoronium
8a62a5a8c7 Notify on encryption change 2023-03-09 16:30:54 -07:00
Thoronium
d56c6bc069 Bump version 2023-03-05 19:38:40 -07:00
Thoronium
ea73feb8e9 Full offline compatibility 2023-03-05 19:13:09 -07:00
Thoronium
abf7a428f6 Update README.md
Update info about issues page
2023-03-04 13:21:08 -07:00
Thoronium
7bd3493366 Merge branch 'FixedCultivation' of https://github.com/NotThorny/Cultivation into FixedCultivation 2023-03-01 21:05:23 -07:00
Thoronium
20a80e5625 Fix could not patch on very first run 2023-03-01 21:05:16 -07:00
Thoronium
4a8db8eb0f Update README.md 2023-03-01 15:30:30 -07:00
Thoronium
f44dfeb79d Merge branch 'FixedCultivation' of https://github.com/NotThorny/Cultivation into FixedCultivation 2023-02-28 10:28:08 -07:00
Thoronium
098ba63066 Small UI fix 2023-02-28 10:28:02 -07:00
Thoronium
b8255eb5bc Update README.md 2023-02-27 21:15:16 -07:00
Thoronium
812aa64c5e Reduce fallback bg size 2023-02-27 21:05:42 -07:00
Thoronium
b3aaf5c5f7 Auto RSA patching
Add fallback bg for when api is down
2023-02-27 20:58:17 -07:00
Thoronium
9e6029faf3 Fix patch paths 2023-02-27 19:26:04 -07:00
Thoronium
b18f7a0cf6 Quick fix for rsa.ts 2023-02-27 17:29:19 -07:00
Thoronium
62a97d86cb Automatically apply RSA patch 2023-02-27 17:15:21 -07:00
Thoronium
55fa0a8d3c Update README.md
Update images
2023-02-27 00:00:40 -07:00
Thoronium
2aac7fc022 Update README_zh-CN.md
Update images
2023-02-27 00:00:30 -07:00
Thoronium
ddaf24590c Update README_zh-TW.md
Update images
2023-02-27 00:00:23 -07:00
Thoronium
44f5cac2bf Update README_zh-TW.md
機器翻譯,如有錯誤請見諒
2023-02-26 23:50:56 -07:00
Thoronium
e1a519cc5f Update README_zh-CN.md 2023-02-26 23:45:12 -07:00
Thoronium
3dd1ec2674 Replace AIO link 2023-02-26 19:46:39 -07:00
Thoronium
438596f4e2 Update README.md 2023-02-26 19:31:06 -07:00
SpikeHD
af5c9229d4 Merge pull request #135 from NotThorny/3dmigotoPathFix
Fix 3dmigoto Path Setting
2023-02-26 17:44:51 -08:00
SpikeHD
fb842ccc86 Merge pull request #130 from NotThorny/Korean-Lang
Add Korean Translation by @koreako12
2023-02-26 17:44:39 -08:00
Thoronium
27d30a183c Update languages
Small fixes
2023-02-26 16:54:36 -07:00
Thoronium
182943fd69 Syntax 2023-02-26 15:26:10 -07:00
Thoronium
b352b16dc9 Fix syntax 2023-02-26 15:12:45 -07:00
Thoronium
b1b18c75a1 Fix encryption button on jar set 2023-02-26 15:01:16 -07:00
Thoronium
7c53669fc4 Update encryption button on jar set
Update README
2023-02-26 14:37:13 -07:00
Thoronium
249f07927d Fix check 2023-02-26 12:40:14 -07:00
Thoronium
090e1a888d Fix jar setting from full build 2023-02-26 12:02:21 -07:00
Thoronium
5dfa98c9ea Update languages
Format downloads menu
2023-02-26 03:32:25 -07:00
Thoronium
088d007816 Revert handle and add logging
Server.ts isn't actually printing the error to the user anyways so there was no point in trying to fix it
2023-02-26 03:02:52 -07:00
Thoronium
d49fdacf2a Error handle fix 2023-02-26 02:10:55 -07:00
Thoronium
8efd601eda Try to fix err handle 2023-02-26 01:58:00 -07:00
Thoronium
f2e3c69c0f Bug fixes 2023-02-26 01:44:01 -07:00
Thoronium
85a16e3407 Undo unnecessary extra check 2023-02-26 01:20:29 -07:00
Thoronium
596c5b85b1 Correct error message 2023-02-26 01:12:57 -07:00
Thoronium
1115283df8 Fix another typo 2023-02-26 01:09:43 -07:00
Thoronium
696a8a1dec Fix typo 2023-02-26 00:56:58 -07:00
Thoronium
1be06b0a6c Try to fix css 2023-02-26 00:29:15 -07:00
Thoronium
7f71549831 Add download menu translations
Update download menu formatting
Better error logging
2023-02-26 00:15:42 -07:00
Thoronium
08d9db05a5 Remove unused workflow 2023-02-25 22:43:47 -07:00
Thoronium
cd2b8aee12 Correct menu text 2023-02-25 21:36:10 -07:00
Thoronium
ec611f9d7d News testing 2023-02-25 21:00:57 -07:00
Thoronium
d6b1c7a613 Update downloads menu 2023-02-25 20:17:37 -07:00
Thoronium
9b1d3594ed Small fixes 2023-02-25 19:21:40 -07:00
Thoronium
3848c39743 Small fixes
Remove stable download buttons
2023-02-25 18:37:36 -07:00
Thoronium
7831bd48dc Add full build download to menu 2023-02-25 18:12:08 -07:00
Thoronium
86d15cd335 Try extraction message fix
Change version to distinguish from official release
Slightly change metadata message
2023-02-25 16:53:18 -07:00
Thoronium
bf880ac297 Better default config
Resources update
Try to actually show news on startup
2023-02-25 16:32:49 -07:00
Thoronium
e1637cebef Merge pull request #1 from NotThorny/Korean-Lang
Upload Korean TL
2023-02-25 15:13:13 -07:00
Thoronium
d7f3218657 Fix path setting 2023-02-25 15:07:45 -07:00
Thoronium
0dede9b17c Fix path setting 2023-02-22 15:06:05 -07:00
Thoronium
ec7b0904d2 Upload Korean TL 2023-01-30 19:00:17 -07:00
Thoronium
937faf85e2 Update resources download 2023-01-30 18:45:13 -07:00
dependabot[bot]
6c40044931 Bump tauri from 1.0.6 to 1.0.7 in /src-tauri
Bumps [tauri](https://github.com/tauri-apps/tauri) from 1.0.6 to 1.0.7.
- [Release notes](https://github.com/tauri-apps/tauri/releases)
- [Commits](https://github.com/tauri-apps/tauri/compare/tauri-v1.0.6...tauri-v1.0.7)

---
updated-dependencies:
- dependency-name: tauri
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-12-14 19:21:49 -08:00
dependabot[bot]
3d2425fb3d Bump loader-utils from 2.0.2 to 2.0.4
Bumps [loader-utils](https://github.com/webpack/loader-utils) from 2.0.2 to 2.0.4.
- [Release notes](https://github.com/webpack/loader-utils/releases)
- [Changelog](https://github.com/webpack/loader-utils/blob/v2.0.4/CHANGELOG.md)
- [Commits](https://github.com/webpack/loader-utils/compare/v2.0.2...v2.0.4)

---
updated-dependencies:
- dependency-name: loader-utils
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-11-20 12:13:37 -05:00
ChapterII
f7c8059f4c Updated Simplified Chinese translations 2022-11-19 18:08:39 -05:00
SpikeHD
6206b6762a cleanup 2022-10-01 16:12:32 -07:00
SpikeHD
258c656815 Merge pull request #97 from pfyy/main
make passwordKey.txt use CRLF
2022-10-01 15:55:02 -07:00
pfyy
68404f5e39 make passwordKey.txt use CRLF 2022-10-01 11:28:00 +08:00
SpikeHD
8c75541939 Merge pull request #95 from pfyy/main
fix proxy uri (add query)
2022-09-30 14:36:33 -07:00
pfyy
aa32782179 fix proxy uri (add query) 2022-09-27 19:36:39 +08:00
Magix
2cda891f02 THE GREATEST CULTIVATION UPDATE OF ALL TIME
part one of removing deprecated code
2022-09-24 00:08:12 -04:00
SpikeHD
9c8ed852d1 Merge branch 'main' of github.com:Grasscutters/Cultivation 2022-09-18 17:54:20 -07:00
SpikeHD
e75f65ab56 --no-admin and fix migoto setting 2022-09-18 17:54:05 -07:00
SpikeHD
bdeda36043 Merge pull request #92 from Grasscutters/dependabot/cargo/src-tauri/tauri-1.0.6
Bump tauri from 1.0.4 to 1.0.6 in /src-tauri
2022-09-16 23:57:04 -07:00
dependabot[bot]
151e7c3919 Bump tauri from 1.0.4 to 1.0.6 in /src-tauri
Bumps [tauri](https://github.com/tauri-apps/tauri) from 1.0.4 to 1.0.6.
- [Release notes](https://github.com/tauri-apps/tauri/releases)
- [Commits](https://github.com/tauri-apps/tauri/compare/tauri-v1.0.4...tauri-v1.0.6)

---
updated-dependencies:
- dependency-name: tauri
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-09-16 19:33:19 +00:00
SpikeHD
e6365b5592 Merge pull request #91 from trollerr/patch-1
Vietnamese Translations Update
2022-09-12 09:14:46 -07:00
Trollerr
ed76a086b6 Vietnamese Translations Update 2022-09-12 13:09:15 +07:00
SpikeHD
c4526e2ff6 version bump 2022-09-09 17:02:43 -07:00
SpikeHD
80c28669e1 conditionally wipe reg 2022-09-09 16:58:53 -07:00
SpikeHD
5e48a4772e update resources url 2022-09-09 16:58:11 -07:00
SpikeHD
10234bed5a upload MSI bundle 2022-09-04 18:55:13 -07:00
SpikeHD
22d8a23386 version bump 2022-09-04 18:35:38 -07:00
SpikeHD
6ef3e86820 build conditions 2022-09-04 18:13:57 -07:00
SpikeHD
0087801f83 fix linux check 2022-09-04 18:12:31 -07:00
SpikeHD
55be3ebc2b remove cmd requirement 2022-09-04 18:08:02 -07:00
SpikeHD
7cf1b198ff open as admin (windows only) 2022-09-04 18:05:19 -07:00
SpikeHD
fb231acaa6 easy-zip is not easy 2022-09-04 16:55:38 -07:00
SpikeHD
827b8942c9 action fixes 2022-09-04 15:24:37 -07:00
SpikeHD
0fd4376e0d change artifact names 2022-09-04 14:59:05 -07:00
SpikeHD
d6c5463619 include appimage 2022-09-04 14:40:56 -07:00
SpikeHD
9ade56dc6f fix file copy linux 2022-09-04 14:38:52 -07:00
SpikeHD
484cd36565 format 2022-09-04 14:20:36 -07:00
SpikeHD
1c27ae172e lint fixes 2022-09-04 14:18:18 -07:00
SpikeHD
541352c3fd Merge pull request #83 from 4Benj/main
Fix some filesystem related bugs
2022-09-04 14:09:03 -07:00
SpikeHD
bfbf3e77a2 prettier fix 2022-09-04 14:05:24 -07:00
SpikeHD
404e946e7c change path/command option based on platform 2022-09-04 14:00:27 -07:00
Benj
8077285e79 Game version checking 2022-09-04 20:15:05 +08:00
SpikeHD
94c1bfd104 build on push/pr 2022-09-03 22:29:31 -07:00
SpikeHD
ed473ad659 run_command for ubuntu build 2022-09-03 22:22:01 -07:00
SpikeHD
b624ef693e sudo 2022-09-03 19:11:53 -07:00
SpikeHD
c10a5cd82f get libs ubuntu 2022-09-03 19:01:21 -07:00
SpikeHD
25c6a70dc0 action name 2022-09-03 18:47:03 -07:00
SpikeHD
86c595abda ubuntu build 2022-09-03 18:46:13 -07:00
SpikeHD
35e6144733 use node 16 2022-09-03 17:57:56 -07:00
SpikeHD
565f229dac fix toolchain install 2022-09-03 17:55:01 -07:00
SpikeHD
9bd4b9ccaf clippy stuff 2022-09-03 17:52:57 -07:00
SpikeHD
6750787bf9 build action 2022-09-03 17:51:47 -07:00
SpikeHD
c0770606ae lint fixes 2022-09-03 17:22:00 -07:00
SpikeHD
4e03fec2a0 Merge branch 'main' of github.com:Grasscutters/Cultivation 2022-09-02 20:21:24 -07:00
SpikeHD
e7809be97c only show horny mode when swag mode is set 2022-09-02 20:20:49 -07:00
SpikeHD
782e350ae5 unblur when horny mode is on 2022-09-02 20:17:48 -07:00
SpikeHD
6eab66032b horny mode option 2022-09-02 20:11:14 -07:00
SpikeHD
c1842722b4 make toggles less dogshit 2022-09-02 19:56:33 -07:00
SpikeHD
31aef02d5f Update README.md 2022-08-31 11:06:46 -07:00
SpikeHD
bcdbb2ba06 Update README.md 2022-08-31 11:06:21 -07:00
Benj
ff8f35c52a Stop popping the directory when we need it later 2022-08-31 18:28:28 +08:00
Benj
a7188828fa Fix os error 123 while reading key files 2022-08-31 17:18:53 +08:00
jseniuk
db6917df5d fix merge conflict 2022-08-30 19:06:49 -07:00
jseniuk
8268c127a9 linux proxyworking probably 2022-08-30 19:02:20 -07:00
jseniuk
8fd5b895af 1-time cert retry 2022-08-30 18:02:48 -07:00
SpikeHD
10b9141815 fix compile errors 2022-08-28 21:59:40 -07:00
SpikeHD
d7783c5936 fix compile issues 2022-08-28 21:57:35 -07:00
SpikeHD
27122cd399 Merge branch 'main' of github.com:Grasscutters/Cultivation 2022-08-28 21:08:09 -07:00
SpikeHD
49740470ac wip linux proxy stuff 2022-08-28 21:07:22 -07:00
SpikeHD
7b693d7758 Update README.md 2022-08-28 19:15:59 -07:00
SpikeHD
03439c3757 launcher.exe alert 2022-08-27 19:47:28 -07:00
SpikeHD
e83ae64714 webview disclaimer for win7 users 2022-08-27 14:47:23 -07:00
SpikeHD
3ecd13d1c3 Merge branch 'main' of github.com:Grasscutters/Cultivation 2022-08-27 14:45:18 -07:00
SpikeHD
28701ba007 delete backed up meta when patch is unsuccessful against it 2022-08-27 14:43:39 -07:00
SpikeHD
3a84825cf9 Merge pull request #60 from Kawaa-qwq/main
Translation languages are supported in the README file and all matched files use Prettier code style!
2022-08-27 01:01:14 -07:00
SpikeHD
4de8a43c3a begin CLI options 2022-08-27 00:54:14 -07:00
SpikeHD
d28e0a1bc8 Merge branch 'main' of github.com:Grasscutters/Cultivation 2022-08-26 17:54:26 -07:00
SpikeHD
bf8de40caa wipe registry option 2022-08-26 17:53:45 -07:00
SpikeHD
8e7d6ee420 Merge pull request #75 from Pikachubolk/patch-1
Dutch Cultivation Translation
2022-08-26 12:08:53 -07:00
Pikachubolk
d6e9bb100b Rename nl.json to src-tauri/lang/nl.json 2022-08-26 20:55:25 +02:00
Pikachubolk
374c6abbfc Create nl.json 2022-08-26 20:51:10 +02:00
SpikeHD
a728e8ba2c Merge pull request #74 from Arikatsu/main
Fix "visual bug" (again)
2022-08-26 10:32:56 -07:00
Scald
4371804429 Update download.ts 2022-08-26 21:15:04 +05:30
Kawaa-qwq
569c465a50 Update 2022-08-22 13:51:22 +08:00
Kawaa
8a5e66be48 更新日志 2022-08-06 10:47:32 +08:00
Kawaa
e633f7dad7 Updated instructions 2022-08-05 21:38:10 +08:00
Kawaa
bffd089192 Updated instructions 2022-08-05 21:29:47 +08:00
Kawaa
bf3d892af5 Updated instructions 2022-08-05 21:28:15 +08:00
Kawaa
424dd4ff1c Updated instructions 2022-08-05 21:22:40 +08:00
53 changed files with 1979 additions and 905 deletions

4
.gitattributes vendored
View File

@@ -1 +1,3 @@
* text=auto eol=lf
* text=auto eol=lf
src-tauri/keys/* binary

85
.github/workflows/build.yml vendored Normal file
View File

@@ -0,0 +1,85 @@
name: Build
on:
push:
paths:
- '.github/workflows/build.yml'
- 'src-tauri/**/*'
- 'src/**/*'
pull_request:
paths:
- '.github/workflows/build.yml'
- 'src-tauri/**/*'
- 'src/**/*'
concurrency:
group: ${{ github.ref }}-${{ github.workflow }}
cancel-in-progress: true
jobs:
build-win:
runs-on: windows-latest
steps:
- name: Checkout
uses: actions/checkout@v2
- name: setup node
uses: actions/setup-node@v1
with:
node-version: 16
- name: Install Rust
uses: actions-rs/toolchain@v1
with:
toolchain: stable
- name: Install deps and build
run: yarn && yarn build --debug
- name: Compress build
uses: vimtor/action-zip@v1
with:
files: src-tauri/target/debug/lang/ src-tauri/target/debug/keys/ src-tauri/target/debug/patch/ src-tauri/target/debug/Cultivation.exe src-tauri/target/debug/bundle/msi/
recursive: true
dest: Cultivation.zip
- name: Upload build
uses: actions/upload-artifact@v3
with:
name: CultivationWin
path: Cultivation.zip
build-ubuntu:
runs-on: ubuntu-20.04
steps:
- name: Checkout
uses: actions/checkout@v2
- name: setup node
uses: actions/setup-node@v1
with:
node-version: 16
- name: Install Rust
uses: actions-rs/toolchain@v1
with:
toolchain: stable
- name: Install libraries
run: sudo apt install libwebkit2gtk-4.0-dev build-essential curl wget libssl-dev libgtk-3-dev libayatana-appindicator3-dev librsvg2-dev
- name: Install deps and build
run: yarn && yarn build --debug
- name: Compress build
uses: vimtor/action-zip@v1
with:
files: src-tauri/target/debug/lang/ src-tauri/target/debug/keys/ src-tauri/target/debug/patch/ src-tauri/target/debug/cultivation
recursive: true
dest: Cultivation.zip
- name: Upload build
uses: actions/upload-artifact@v3
with:
name: CultivationLinux
path: Cultivation.zip

1
.gitignore vendored
View File

@@ -17,6 +17,7 @@
.env.development.local
.env.test.local
.env.production.local
.vs
npm-debug.log*
yarn-debug.log*

View File

@@ -1,4 +1,4 @@
#!/usr/bin/env sh
. "$(dirname -- "$0")/_/husky.sh"
yarn lint-staged
yarn lint-staged --allow-empty

View File

@@ -1,17 +1,14 @@
# Client Patching Notice
For game versions 2.8 and above, Cultivation automatically makes a small patch to your game client when launching using Grasscutter, and restores it upon closing the game. In theory, you should still be totally safe, however it would be dishonest to not explicitly state that **modifying the game client could, theoretically, lead to a ban if you connect to official servers with it**. It is extremely unlikely AND there are no instances known of it happening, but the possibility exists.
EN | [简中](README_zh-CN.md) | [繁中](README_zh-TW.md) |
# Cultivation
A game launcher designed to easily proxy traffic from anime game to private servers.
While the Cultivation repository is **open**. This does **not** mean it has released.
Please do **NOT install, download, or use pre-compiled versions of Cultivation found elsewhere**. Only use releases from this GitHub repository.
# Table Of Contents
- [Client Patching Notice](#client-patching-notice)
- [Download](#download)
- [Setup](#setup)
- [Developer Quick-start](#developer-quickstart)
- [Setup](#setup)
- [Building](#building)
@@ -21,11 +18,44 @@ Please do **NOT install, download, or use pre-compiled versions of Cultivation f
- [Screenshots](#screenshots)
- [Credits](#credits)
# Client Patching Notice - RSA
For game versions 3.1 and above, Cultivation automatically makes a small patch to your game client when launching using Grasscutter, and restores it upon closing the game. In theory, you should still be totally safe, however it would be dishonest to not explicitly state that **modifying the game client could, theoretically, lead to a ban if you connect to official servers with it**. It is extremely unlikely AND there are no instances known of it happening, but the possibility exists.
# Download
[Find release builds here!](https://github.com/Grasscutters/Cultivation/releases)
Once downloaded, extract somewhere and open as administrator.
Download and open the MSI, and once installed, run Cultivation as administrator. Refer below for more [detailed setup instructions](#setup).
**Windows 7 Users:** You will need to download [WebView](https://developer.microsoft.com/en-us/microsoft-edge/webview2/#download-section) manually, and download the `.zip` instead of the `.msi`.
# Setup
5-minute video for those who don't like to/cannot read: https://youtu.be/e0irOYbQe7I
- Download Cultivation
- If you are on Windows 10 or 11, use the MSI
- If you are on Windows 7, or the MSI doesn't work, use the zip and download [WebView](https://developer.microsoft.com/en-us/microsoft-edge/webview2/)
- If you are on Linux or MacOS, [help us port Windows-specific system calls to Linux/MacOS!](https://github.com/Grasscutters/Cultivation/issues/7)
- Install or extract Cultivation
- Open Cultivation **_as administrator_**
- Before clicking randomly on stuff, in options (top right cog icon), set your Game Install Path.
- If you are using an existing server installation from somewhere else, you can set the `.jar` file in settings as well. All downloads made through Culti will automatically use that path, no additional config needed.
- If you use multiple Java versions, you can set the Java path to your Java 17 installation (only required if you are running your own server)
- Decide if you want to download your own server, or just join a public one
- If joining a public one, you're done. Just click "Connect with Grasscutter" and input the address and port. You do not have to continue these instructions.
- If you are getting System Error, or 4214, ask the [Discord support channels](https://discord.gg/grasscutter)
- Open the "Downloads" menu (top right)
- Download "Grasscutter All-in-One" (top of the list)
- Once that is done, click the icon next to "Launch"
- To play on your new server:
- Click "Connect with Grasscutter"
- Input `localhost` as the address, and `443` as the port
- Ensure HTTPS is disabled
- Any generic "I am getting XYZ error!" should go in the [Discord support channels](https://discord.gg/grasscutter)
- Any specific Cultivation issues should go in [the issues section](/issues)
- Any Grasscutter server related issues should go in [the Grasscutter issues section](https://github.com/Grasscutters/Grasscutter)
# Developer Quickstart
@@ -71,11 +101,10 @@ A full theming reference can be found [here!](/THEMES.md)
# Screenshots
![image](https://user-images.githubusercontent.com/25207995/173211603-e5e85df7-7fd3-430b-9246-749ebbc1e483.png)
![image](https://user-images.githubusercontent.com/25207995/173211543-b7e88943-cfd2-418b-ac48-7f856868129b.png)
![image](https://user-images.githubusercontent.com/25207995/173211561-a1778fdc-5cfe-4687-9a00-44500d29e470.png)
![image](https://user-images.githubusercontent.com/25207995/173211573-8cedfa9a-51c9-4670-a4f7-a334a2fabec5.png)
![image](https://user-images.githubusercontent.com/25207995/173211590-6a2242b5-1e8f-4db9-a5c7-06284688b131.png)
![image](https://user-images.githubusercontent.com/107363768/221495236-ca1e2f2e-0f85-4765-a5f3-8bdcea299612.png)
![image](https://user-images.githubusercontent.com/107363768/221495246-ea309640-f866-4f50-bda8-f9d916380f92.png)
![image](https://user-images.githubusercontent.com/107363768/221495249-5a1aac39-9e8a-4244-9642-72c2e7be8a69.png)
![image](https://user-images.githubusercontent.com/107363768/221495254-ffbfc24e-ef5d-4e72-9068-a02132381dcc.png)
## Credits

87
README_zh-CN.md Normal file
View File

@@ -0,0 +1,87 @@
[EN](README.md) | 简中 | [繁中](README_zh-TW.md)
# Cultivation
一个游戏启动器,旨在轻松将某动漫游戏的流量代理到私人服务器。
虽然此存储库是**开放的**。 但这**并不**意味着它已经发布。
请不要**安装、下载或使用在其他地方找到的预编译版本的 Cultivation**。 仅使用此 GitHub 存储库中的版本。
# 目录
- [下载](#下载)
- [开发人员快速入门](#开发人员快速入门)
- [安装](#安装)
- [编译](#编译)
- [代码格式化与纠错](#代码格式化与纠错)
- [生成更新项目](#生成更新项目)
- [启动器主题](#启动器主题)
- [画面](#画面)
- [成员](#成员)
# 客户端修补通知
对于游戏版本为 3.1 及以上时,使用 Grasscutter 启动时Cultivation 会自动为您的游戏客户端制作一个小补丁,并在关闭游戏时恢复它。 从理论上讲,你应该是完全安全的,但是不明确**如果您使用它连接到官方服务器,修改游戏客户端可能会导致封号**,但可能性是非常小的,并且从未接到发生过此类情况的问题,但存在这种可能性!
# 下载
[在此处查找发布版本!](https://github.com/Grasscutters/Cultivation/releases)
下载后,从某个位置解压缩并以管理员身份打开。
# 开发人员快速入门
### 安装
- 安装 [NodeJS >12](https://nodejs.org/en/)
- 安装 [yarn](https://classic.yarnpkg.com/lang/en/docs/install)
- 安装 [Rust](https://www.rust-lang.org/tools/install)
- `yarn install`
- `yarn start:dev`
### 编译
发布版本,
- `yarn build`
调试版本,
- `yarn build --debug`
### 代码格式化与纠错
格式化:
- `yarn format`
纠错, 修复(一些)错误:
- `yarn lint`, `yarn lint:fix`
### 生成更新项目
-`TAURI_PRIVATE_KEY` 添加到环境变量,其中包含私钥的路径。
-`TAURI_KEY_PASSWORD` 添加到环境变量,其中包含私钥的密码。
- `yarn build`
更新将生成在 `src-tauri/target/(release|debug)/msi/Cultivation_X.X.X_x64_xx-XX.msi.zip`
# 启动器主题
完整的主题参考可以[在这里找到!](/THEMES.md)
# 画面
![image](https://user-images.githubusercontent.com/107363768/221495236-ca1e2f2e-0f85-4765-a5f3-8bdcea299612.png)
![image](https://user-images.githubusercontent.com/107363768/221495246-ea309640-f866-4f50-bda8-f9d916380f92.png)
![image](https://user-images.githubusercontent.com/107363768/221495249-5a1aac39-9e8a-4244-9642-72c2e7be8a69.png)
![image](https://user-images.githubusercontent.com/107363768/221495254-ffbfc24e-ef5d-4e72-9068-a02132381dcc.png)
## 成员
- [SpikeHD](https://github.com/SpikeHD): For originally creating **GrassClipper** and creating the amazing UI of Cultivation.
- [KingRainbow44](https://github.com/KingRainbow44): For building a proxy daemon from scratch and integrating it with Cultivation.
- [Benj](https://github.com/4Benj): For assistance in client patching.
- [lilmayofuksu](https://github.com/lilmayofuksu): For assistance in client patching.
- [Tauri](https://tauri.app): For providing an amazing, efficient, and simple desktop application framework/library.

87
README_zh-TW.md Normal file
View File

@@ -0,0 +1,87 @@
[EN](README.md) | [简中](README_zh-CN.md) | 繁中
# 客戶端修補通知
對於遊戲版本為 3.1 及以上時,使用 Grasscutter 啟動時Cultivation 會自動為您的遊戲客戶端製作一個小修補,並在關閉遊戲時恢復它。 從理論上講,你應該是完全安全的,但是不明確**如果您使用它連接到官方伺服器,修改遊戲客戶端可能會導致封號**,但可能性是非常小的,並且從未接到發生過此類情況的問題,但存在這種可能性!
# Cultivation
一個遊戲啟動器,旨在輕松將某動漫遊戲的流量代理到私人伺服器。
雖然此存儲庫是**開放的**。 但這**並不**意味著它已經發布。
請不要**安裝、下載或使用在其他地方找到的預編譯版本的 Cultivation**。 僅使用此 GitHub 存儲庫中的版本。
# 目錄
- [下載](#下載)
- [開發人員快速入門](#開發人員快速入門)
- [安裝](#安裝)
- [編譯](#編譯)
- [代碼格式化與糾錯](#代碼格式化與糾錯)
- [生成更新項目](#生成更新項目)
- [啟動器主題](#啟動器主題)
- [畫面](#畫面)
- [成員](#成員)
# 下載
[在此處查找發布版本!](https://github.com/Grasscutters/Cultivation/releases)
下載後,從某個位置解壓縮並以管理員身份打開。
# 開發人員快速入門
### 安裝
- 安裝 [NodeJS >12](https://nodejs.org/en/)
- 安裝 [yarn](https://classic.yarnpkg.com/lang/en/docs/install) (`npm`愛好者去哭吧!(滑稽))
- 安裝 [Rust](https://www.rust-lang.org/tools/install)
- `yarn install`
- `yarn start:dev`
### 編譯
發布版本,
- `yarn build`
調試版本,
- `yarn build --debug`
### 代碼格式化與糾錯
格式化:
- `yarn format`
糾錯, 修復(一些)錯誤:
- `yarn lint`, `yarn lint:fix`
### 生成更新項目
-`TAURI_PRIVATE_KEY` 添加到環境變數,其中包含私鑰的路徑。
-`TAURI_KEY_PASSWORD` 添加到環境變數,其中包含私鑰的密碼。
- `yarn build`
更新將生成在 `src-tauri/target/(release|debug)/msi/Cultivation_X.X.X_x64_xx-XX.msi.zip`
# 啟動器主題
完整的主題參考可以[在這裏找到!](/THEMES.md)
# 畫面
![image](https://user-images.githubusercontent.com/107363768/221495236-ca1e2f2e-0f85-4765-a5f3-8bdcea299612.png)
![image](https://user-images.githubusercontent.com/107363768/221495246-ea309640-f866-4f50-bda8-f9d916380f92.png)
![image](https://user-images.githubusercontent.com/107363768/221495249-5a1aac39-9e8a-4244-9642-72c2e7be8a69.png)
![image](https://user-images.githubusercontent.com/107363768/221495254-ffbfc24e-ef5d-4e72-9068-a02132381dcc.png)
## 成員
- [SpikeHD](https://github.com/SpikeHD): For originally creating **GrassClipper** and creating the amazing UI of Cultivation.
- [KingRainbow44](https://github.com/KingRainbow44): For building a proxy daemon from scratch and integrating it with Cultivation.
- [Benj](https://github.com/4Benj): For assistance in client patching.
- [lilmayofuksu](https://github.com/lilmayofuksu): For assistance in client patching.
- [Tauri](https://tauri.app): For providing an amazing, efficient, and simple desktop application framework/library.

View File

@@ -1,6 +1,6 @@
{
"name": "cultivation",
"version": "1.0.7",
"version": "1.0.23",
"private": true,
"dependencies": {
"@tauri-apps/api": "^1.0.0-rc.5",
@@ -20,14 +20,14 @@
"scripts": {
"start": "cross-env BROWSER=none react-scripts start",
"postbuild:windows": "xcopy /E /H /C /I /Y \".\\src-tauri\\lang\" \".\\src-tauri\\target\\release\\lang\"",
"postbuild:linux": "cp -r \".\\src-tauri\\lang\" \".\\lang\"",
"postbuild:linux": "cp -r \"./src-tauri/lang\" \"./lang\"",
"build:windows": "yarn tauri build",
"build:linux": "yarn tauri build",
"build": "react-scripts build && run-script-os",
"test": "react-scripts test",
"eject": "react-scripts eject",
"tauri": "tauri",
"start:dev": "tauri dev",
"start:dev": "tauri dev -- -- --no-admin",
"format": "cargo fmt --manifest-path ./src-tauri/Cargo.toml --all && yarn prettier --write --cache --loglevel warn .",
"lint": "cargo clippy --manifest-path ./src-tauri/Cargo.toml --no-default-features && yarn tsc --noEmit && yarn eslint src",
"lint:fix": "cargo clippy --manifest-path ./src-tauri/Cargo.toml --no-default-features --fix --allow-dirty && yarn tsc --noEmit && yarn eslint --fix src",

438
src-tauri/Cargo.lock generated
View File

@@ -20,7 +20,7 @@ version = "0.7.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9e8b47f52ea9bae42228d07ec09eb676433d7c4ed1ebdf0f1d1c29ed446f1ab8"
dependencies = [
"cfg-if 1.0.0",
"cfg-if",
"cipher",
"cpufeatures",
"opaque-debug",
@@ -82,7 +82,23 @@ version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "30ff05a702273012438132f449575dbc804e27b2f3cbe3069aa237d26c98fa33"
dependencies = [
"asn1-rs-derive",
"asn1-rs-derive 0.1.0",
"asn1-rs-impl",
"displaydoc",
"nom",
"num-traits 0.2.15",
"rusticata-macros",
"thiserror",
"time 0.3.11",
]
[[package]]
name = "asn1-rs"
version = "0.5.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cf6690c370453db30743b373a60ba498fc0d6d83b11f4abfd87a84a075db5dd4"
dependencies = [
"asn1-rs-derive 0.4.0",
"asn1-rs-impl",
"displaydoc",
"nom",
@@ -104,6 +120,18 @@ dependencies = [
"synstructure",
]
[[package]]
name = "asn1-rs-derive"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "726535892e8eae7e70657b4c8ea93d26b8553afb1ce617caee529ef96d7dee6c"
dependencies = [
"proc-macro2",
"quote",
"syn",
"synstructure",
]
[[package]]
name = "asn1-rs-impl"
version = "0.1.0"
@@ -281,6 +309,18 @@ dependencies = [
"memchr",
]
[[package]]
name = "bstr"
version = "1.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5ffdb39cb703212f3c11973452c2861b972f757b021158f3516ba10f2fa8b2c1"
dependencies = [
"memchr",
"once_cell",
"regex-automata",
"serde",
]
[[package]]
name = "bumpalo"
version = "3.10.0"
@@ -447,12 +487,6 @@ dependencies = [
"smallvec",
]
[[package]]
name = "cfg-if"
version = "0.1.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822"
[[package]]
name = "cfg-if"
version = "1.0.0"
@@ -592,7 +626,7 @@ version = "1.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b540bd8bc810d3885c6ea91e2018302f68baba2129ab3e88f32389ee9370880d"
dependencies = [
"cfg-if 1.0.0",
"cfg-if",
]
[[package]]
@@ -601,8 +635,8 @@ version = "0.5.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4c02a4d71819009c192cf4872265391563fd6a84c81ff2c0f2a7026ca4c1d85c"
dependencies = [
"cfg-if 1.0.0",
"crossbeam-utils 0.8.10",
"cfg-if",
"crossbeam-utils",
]
[[package]]
@@ -611,24 +645,9 @@ version = "0.8.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6455c0ca19f0d2fbf751b908d5c55c1f5cbc65e03c4225427254b46890bdde1e"
dependencies = [
"cfg-if 1.0.0",
"crossbeam-epoch 0.9.9",
"crossbeam-utils 0.8.10",
]
[[package]]
name = "crossbeam-epoch"
version = "0.8.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "058ed274caafc1f60c4997b5fc07bf7dc7cca454af7c6e81edffe5f33f70dace"
dependencies = [
"autocfg",
"cfg-if 0.1.10",
"crossbeam-utils 0.7.2",
"lazy_static",
"maybe-uninit",
"memoffset 0.5.6",
"scopeguard",
"cfg-if",
"crossbeam-epoch",
"crossbeam-utils",
]
[[package]]
@@ -638,31 +657,20 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "07db9d94cbd326813772c968ccd25999e5f8ae22f4f8d1b11effa37ef6ce281d"
dependencies = [
"autocfg",
"cfg-if 1.0.0",
"crossbeam-utils 0.8.10",
"memoffset 0.6.5",
"cfg-if",
"crossbeam-utils",
"memoffset",
"once_cell",
"scopeguard",
]
[[package]]
name = "crossbeam-utils"
version = "0.7.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c3c7c73a2d1e9fc0886a08b93e98eb643461230d5f1925e4036204d5f2e261a8"
dependencies = [
"autocfg",
"cfg-if 0.1.10",
"lazy_static",
]
[[package]]
name = "crossbeam-utils"
version = "0.8.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7d82ee10ce34d7bc12c2122495e7593a9c41347ecdd64185af4ecf72cb1a7f83"
dependencies = [
"cfg-if 1.0.0",
"cfg-if",
"once_cell",
]
@@ -713,6 +721,16 @@ dependencies = [
"syn",
]
[[package]]
name = "ctrlc"
version = "3.2.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1d91974fbbe88ec1df0c24a4f00f99583667a7e2e6272b2b92d294d81e462173"
dependencies = [
"nix",
"winapi",
]
[[package]]
name = "cty"
version = "0.2.2"
@@ -724,6 +742,7 @@ name = "cultivation"
version = "0.1.0"
dependencies = [
"cc",
"ctrlc",
"duct",
"file_diff",
"futures-util",
@@ -732,7 +751,7 @@ dependencies = [
"is_elevated",
"once_cell",
"open",
"rcgen",
"rcgen 0.9.3",
"regex",
"registry",
"reqwest",
@@ -746,9 +765,10 @@ dependencies = [
"tauri-build",
"tokio",
"tokio-rustls",
"tokio-tungstenite",
"tokio-tungstenite 0.17.2",
"tracing",
"unrar",
"windows-service",
"zip 0.6.2",
"zip-extract",
]
@@ -830,7 +850,21 @@ version = "7.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fe398ac75057914d7d07307bf67dc7f3f574a26783b4fc7805a20ffa9f506e82"
dependencies = [
"asn1-rs",
"asn1-rs 0.3.1",
"displaydoc",
"nom",
"num-bigint 0.4.3",
"num-traits 0.2.15",
"rusticata-macros",
]
[[package]]
name = "der-parser"
version = "8.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "42d4bc9b0db0a0df9ae64634ac5bdefb7afcb534e182275ca0beadbe486701c1"
dependencies = [
"asn1-rs 0.5.1",
"displaydoc",
"nom",
"num-bigint 0.4.3",
@@ -853,9 +887,9 @@ dependencies = [
[[package]]
name = "digest"
version = "0.10.3"
version = "0.10.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f2fb860ca6fafa5552fb6d0e816a69c8e49f0908bf524e30a90d97c85892d506"
checksum = "8168378f4e5023e7218c89c891c0fd8ecdb5e5e4f18cb78f38cf245dd021e76f"
dependencies = [
"block-buffer",
"crypto-common",
@@ -868,7 +902,7 @@ version = "2.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b98cf8ebf19c3d1b223e151f99a4f9f0690dca41414773390fc824184ac833e1"
dependencies = [
"cfg-if 1.0.0",
"cfg-if",
"dirs-sys-next",
]
@@ -964,7 +998,7 @@ version = "0.8.31"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9852635589dc9f9ea1b6fe9f05b50ef208c85c834a562f0c6abb1c475736ec2b"
dependencies = [
"cfg-if 1.0.0",
"cfg-if",
]
[[package]]
@@ -1006,7 +1040,7 @@ version = "0.3.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1e1c54951450cbd39f3dbcf1005ac413b49487dabf18a720ad2383eccfeffb92"
dependencies = [
"memoffset 0.6.5",
"memoffset",
"rustc_version 0.3.3",
]
@@ -1022,10 +1056,10 @@ version = "0.2.17"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e94a7bbaa59354bc20dd75b67f23e2797b4490e9d6928203fb105c79e448c86c"
dependencies = [
"cfg-if 1.0.0",
"cfg-if",
"libc",
"redox_syscall",
"windows-sys",
"windows-sys 0.36.1",
]
[[package]]
@@ -1299,7 +1333,7 @@ version = "0.1.16"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8fc3cb4d91f53b50155bdcfd23f6a4c39ae1969c2ae85982b135750cccaf5fce"
dependencies = [
"cfg-if 1.0.0",
"cfg-if",
"libc",
"wasi 0.9.0+wasi-snapshot-preview1",
]
@@ -1310,7 +1344,7 @@ version = "0.2.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4eb1a864a501629691edf6c15a593b7a51eebaa1e8468e9ddc623de7c9b58ec6"
dependencies = [
"cfg-if 1.0.0",
"cfg-if",
"libc",
"wasi 0.11.0+wasi-snapshot-preview1",
]
@@ -1403,7 +1437,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0a1e17342619edbc21a964c2afbeb6c820c6a2560032872f397bb97ea127bd0a"
dependencies = [
"aho-corasick",
"bstr",
"bstr 0.2.17",
"fnv",
"log",
"regex",
@@ -1552,9 +1586,9 @@ dependencies = [
[[package]]
name = "http"
version = "0.2.8"
version = "0.2.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "75f43d41e26995c17e71ee126451dd3941010b0514a81a9d11f3b341debc2399"
checksum = "bd6effc99afb63425aff9b05836f029929e345a6148a14b7ecd5ab67af944482"
dependencies = [
"bytes",
"fnv",
@@ -1592,12 +1626,13 @@ checksum = "c4a1e36c821dbe04574f602848a19f742f4fb3c98d40449f11bcad18d6b17421"
[[package]]
name = "hudsucker"
version = "0.17.2"
version = "0.19.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0b05c90565930259fbacb8f15d5e20132aa8056f3f73bc2b4aa6b7c9b4d89434"
checksum = "655326a6a1f44e52c64ee30a4fdfd86e4de893b2ed6757250391ec6c9a8f93a8"
dependencies = [
"async-compression",
"async-trait",
"bstr 1.3.0",
"bytes",
"futures",
"http",
@@ -1606,12 +1641,12 @@ dependencies = [
"hyper-tungstenite",
"moka",
"rand 0.8.5",
"rcgen",
"rcgen 0.10.0",
"thiserror",
"time 0.3.11",
"tokio",
"tokio-rustls",
"tokio-tungstenite",
"tokio-tungstenite 0.18.0",
"tokio-util",
"tracing",
]
@@ -1670,15 +1705,15 @@ dependencies = [
[[package]]
name = "hyper-tungstenite"
version = "0.6.0"
version = "0.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b2e9b2a23919b72e723692a5b81330ecdd640bba2f2d0fc0089b7abf0be67bf7"
checksum = "880b8b1c98a5ec2a505c7c90db6d3f6f1f480af5655d9c5b55facc9382a5a5b5"
dependencies = [
"hyper",
"pin-project",
"tokio",
"tokio-tungstenite",
"tungstenite",
"tokio-tungstenite 0.18.0",
"tungstenite 0.18.0",
]
[[package]]
@@ -1714,7 +1749,7 @@ version = "0.4.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "713f1b139373f96a2e0ce3ac931cd01ee973c3c5dd7c40c0c2efe96ad2b6751d"
dependencies = [
"crossbeam-utils 0.8.10",
"crossbeam-utils",
"globset",
"lazy_static",
"log",
@@ -1774,7 +1809,7 @@ version = "0.1.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7a5bbe824c507c5da5956355e86a746d82e0e1464f65d862cc5e71da70e94b2c"
dependencies = [
"cfg-if 1.0.0",
"cfg-if",
]
[[package]]
@@ -1910,9 +1945,9 @@ checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
[[package]]
name = "libc"
version = "0.2.126"
version = "0.2.132"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "349d5a591cd28b49e1d1037471617a32ddcda5731b99419008085f72d5a53836"
checksum = "8371e4e5341c3a96db127eb2465ac681ced4c433e01dd0e938adbef26ba93ba5"
[[package]]
name = "libdbus-sys"
@@ -1948,7 +1983,7 @@ version = "0.4.17"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "abb12e687cfb44aa40f41fc3978ef76448f9b6038cad6aef4259d3c095a2382e"
dependencies = [
"cfg-if 1.0.0",
"cfg-if",
]
[[package]]
@@ -1957,7 +1992,7 @@ version = "0.5.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ff50ecb28bb86013e935fb6683ab1f6d3a20016f123c76fd4c27470076ac30f5"
dependencies = [
"cfg-if 1.0.0",
"cfg-if",
"generator",
"scoped-tls",
"serde",
@@ -2032,27 +2067,12 @@ version = "0.1.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a3e378b66a060d48947b590737b30a1be76706c8dd7b8ba0f2fe3989c68a853f"
[[package]]
name = "maybe-uninit"
version = "2.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "60302e4db3a61da70c0cb7991976248362f30319e88850c487b9b95bbf059e00"
[[package]]
name = "memchr"
version = "2.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d"
[[package]]
name = "memoffset"
version = "0.5.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "043175f069eda7b85febe4a74abbaeff828d9f8b448515d3151a14a3542811aa"
dependencies = [
"autocfg",
]
[[package]]
name = "memoffset"
version = "0.6.5"
@@ -2092,25 +2112,26 @@ dependencies = [
"libc",
"log",
"wasi 0.11.0+wasi-snapshot-preview1",
"windows-sys",
"windows-sys 0.36.1",
]
[[package]]
name = "moka"
version = "0.8.6"
version = "0.10.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "975fa04238144061e7f8df9746b2e9cd93ef85881da5548d842a7c6a4b614415"
checksum = "2b6446f16d504e3d575df79cabb11bfbe9f24b17e9562d964a815db7b28ae3ec"
dependencies = [
"async-io",
"async-lock",
"crossbeam-channel",
"crossbeam-epoch 0.8.2",
"crossbeam-utils 0.8.10",
"crossbeam-epoch",
"crossbeam-utils",
"futures-util",
"num_cpus",
"once_cell",
"parking_lot 0.12.1",
"quanta",
"rustc_version 0.4.0",
"scheduled-thread-pool",
"skeptic",
"smallvec",
@@ -2172,6 +2193,18 @@ version = "1.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e4a24736216ec316047a1fc4252e27dabb04218aa4a3f37c6e7ddbf1f9782b54"
[[package]]
name = "nix"
version = "0.25.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e322c04a9e3440c327fca7b6c8a63e6890a32fa2ad689db972425f07e0d22abb"
dependencies = [
"autocfg",
"bitflags",
"cfg-if",
"libc",
]
[[package]]
name = "nodrop"
version = "0.1.14"
@@ -2402,14 +2435,23 @@ version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "38e20717fa0541f39bd146692035c37bedfa532b3e5071b35761082407546b2a"
dependencies = [
"asn1-rs",
"asn1-rs 0.3.1",
]
[[package]]
name = "oid-registry"
version = "0.6.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9bedf36ffb6ba96c2eb7144ef6270557b52e54b20c0a8e1eb2ff99a6c6959bff"
dependencies = [
"asn1-rs 0.5.1",
]
[[package]]
name = "once_cell"
version = "1.13.0"
version = "1.17.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "18a6dbe30758c9f83eb00cbea4ac95966305f5a7772f3f42ebfc7fc7eddbd8e1"
checksum = "b7e5500299e16ebb147ae15a00a942af264cf3688f47923b8fc2cd5858f23ad3"
[[package]]
name = "opaque-debug"
@@ -2424,7 +2466,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f23a407004a1033f53e93f9b45580d14de23928faad187384f891507c9b0c045"
dependencies = [
"pathdiff",
"windows-sys",
"windows-sys 0.36.1",
]
[[package]]
@@ -2434,7 +2476,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "618febf65336490dfcf20b73f885f5651a0c89c64c2d4a8c3662585a70bf5bd0"
dependencies = [
"bitflags",
"cfg-if 1.0.0",
"cfg-if",
"foreign-types",
"libc",
"once_cell",
@@ -2571,7 +2613,7 @@ version = "0.8.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d76e8e1493bcac0d2766c42737f34458f1c8c50c0d23bcb24ea953affb273216"
dependencies = [
"cfg-if 1.0.0",
"cfg-if",
"instant",
"libc",
"redox_syscall",
@@ -2585,11 +2627,11 @@ version = "0.9.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "09a279cbf25cb0757810394fbc1e359949b59e348145c643a939a525692e6929"
dependencies = [
"cfg-if 1.0.0",
"cfg-if",
"libc",
"redox_syscall",
"smallvec",
"windows-sys",
"windows-sys 0.36.1",
]
[[package]]
@@ -2831,7 +2873,7 @@ version = "2.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "685404d509889fade3e86fe3a5803bca2ec09b0c0778d5ada6ec8bf7a8de5259"
dependencies = [
"cfg-if 1.0.0",
"cfg-if",
"libc",
"log",
"wepoll-ffi",
@@ -2916,7 +2958,7 @@ version = "0.10.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bafd74c340a0a7e79415981ede3460df16b530fd071541901a57416eea950b17"
dependencies = [
"crossbeam-utils 0.8.10",
"crossbeam-utils",
"libc",
"mach",
"once_cell",
@@ -3082,7 +3124,7 @@ checksum = "258bcdb5ac6dad48491bb2992db6b7cf74878b0384908af124823d118c99683f"
dependencies = [
"crossbeam-channel",
"crossbeam-deque",
"crossbeam-utils 0.8.10",
"crossbeam-utils",
"num_cpus",
]
@@ -3095,7 +3137,20 @@ dependencies = [
"pem",
"ring",
"time 0.3.11",
"x509-parser",
"x509-parser 0.13.2",
"yasna",
]
[[package]]
name = "rcgen"
version = "0.10.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ffbe84efe2f38dea12e9bfc1f65377fdf03e53a18cb3b995faedf7934c7e785b"
dependencies = [
"pem",
"ring",
"time 0.3.11",
"x509-parser 0.14.0",
"yasna",
]
@@ -3260,7 +3315,7 @@ version = "0.18.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f6d5f2436026b4f6e79dc829837d467cc7e9a55ee40e750d716713540715a2df"
dependencies = [
"cfg-if 1.0.0",
"cfg-if",
"ordered-multimap",
]
@@ -3352,7 +3407,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "88d6731146462ea25d9244b2ed5fd1d716d25c52e4d54aa4fb0f3c4e9854dbe2"
dependencies = [
"lazy_static",
"windows-sys",
"windows-sys 0.36.1",
]
[[package]]
@@ -3566,11 +3621,11 @@ dependencies = [
[[package]]
name = "sha-1"
version = "0.10.0"
version = "0.10.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "028f48d513f9678cda28f6e4064755b3fbb2af6acd672f2c209b62323f7aea0f"
checksum = "f5058ada175748e33390e40e872bd0fe59a19f265d0158daa551c5a88a76009c"
dependencies = [
"cfg-if 1.0.0",
"cfg-if",
"cpufeatures",
"digest",
]
@@ -3581,7 +3636,7 @@ version = "0.10.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c77f4e7f65455545c2153c1253d25056825e77ee2533f0e41deb65a93a34852f"
dependencies = [
"cfg-if 1.0.0",
"cfg-if",
"cpufeatures",
"digest",
]
@@ -3592,7 +3647,7 @@ version = "0.10.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "55deaec60f81eefe3cce0dc50bda92d6d8e88f2a27df7c5033b42afeb1ed2676"
dependencies = [
"cfg-if 1.0.0",
"cfg-if",
"cpufeatures",
"digest",
]
@@ -3825,7 +3880,7 @@ version = "0.24.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0b6e19da72a8d75be4d40e4dd4686afca31507f26c3ffdf6bd3073278d9de0a0"
dependencies = [
"cfg-if 1.0.0",
"cfg-if",
"core-foundation-sys",
"libc",
"ntapi",
@@ -3925,9 +3980,9 @@ dependencies = [
[[package]]
name = "tauri"
version = "1.0.4"
version = "1.0.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "827f61bd3dd40276694be5c7ffc40d65b94ab00d9f8c1a4a4db07f2cdc306c83"
checksum = "f75c3e334088164e8ce14dc44e6568d2a7cda53c35ef8fca3258828d266d4bc6"
dependencies = [
"anyhow",
"attohttpc",
@@ -4105,7 +4160,7 @@ version = "3.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5cdb1ef4eaeeaddc8fbd371e5017057064af0911902ef36b39801f67cc6d79e4"
dependencies = [
"cfg-if 1.0.0",
"cfg-if",
"fastrand",
"libc",
"redox_syscall",
@@ -4247,13 +4302,25 @@ name = "tokio-tungstenite"
version = "0.17.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f714dd15bead90401d77e04243611caec13726c2408afd5b31901dfcdcb3b181"
dependencies = [
"futures-util",
"log",
"tokio",
"tungstenite 0.17.3",
]
[[package]]
name = "tokio-tungstenite"
version = "0.18.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "54319c93411147bced34cb5609a80e0a8e44c5999c93903a81cd866630ec0bfd"
dependencies = [
"futures-util",
"log",
"rustls",
"tokio",
"tokio-rustls",
"tungstenite",
"tungstenite 0.18.0",
"webpki",
"webpki-roots",
]
@@ -4293,7 +4360,7 @@ version = "0.1.35"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a400e31aa60b9d44a52a8ee0343b5b18566b03a8321e0d321f695cf56e940160"
dependencies = [
"cfg-if 1.0.0",
"cfg-if",
"log",
"pin-project-lite",
"tracing-attributes",
@@ -4384,11 +4451,30 @@ dependencies = [
"httparse",
"log",
"rand 0.8.5",
"rustls",
"sha-1",
"thiserror",
"url",
"utf-8",
]
[[package]]
name = "tungstenite"
version = "0.18.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "30ee6ab729cd4cf0fd55218530c4522ed30b7b6081752839b68fcec8d0960788"
dependencies = [
"base64",
"byteorder",
"bytes",
"http",
"httparse",
"log",
"rand 0.8.5",
"rustls",
"sha1",
"thiserror",
"url",
"utf-8",
"webpki",
]
@@ -4621,7 +4707,7 @@ version = "0.2.81"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7c53b543413a17a202f4be280a7e5c62a1c69345f5de525ee64f8cfdbc954994"
dependencies = [
"cfg-if 1.0.0",
"cfg-if",
"wasm-bindgen-macro",
]
@@ -4646,7 +4732,7 @@ version = "0.4.31"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "de9a9cec1733468a8c657e57fa2413d2ae2c0129b95e87c5b72b8ace4d13f31f"
dependencies = [
"cfg-if 1.0.0",
"cfg-if",
"js-sys",
"wasm-bindgen",
"web-sys",
@@ -4803,6 +4889,12 @@ dependencies = [
"cc",
]
[[package]]
name = "widestring"
version = "1.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "653f141f39ec16bba3c5abe400a0c60da7468261cc2cbf36805022876bc721a8"
[[package]]
name = "wildmatch"
version = "2.1.1"
@@ -4905,6 +4997,17 @@ version = "0.37.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4f33f2b90a6664e369c41ab5ff262d06f048fc9685d9bf8a0e99a47750bb0463"
[[package]]
name = "windows-service"
version = "0.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cd9db37ecb5b13762d95468a2fc6009d4b2c62801243223aabd44fca13ad13c8"
dependencies = [
"bitflags",
"widestring",
"windows-sys 0.45.0",
]
[[package]]
name = "windows-sys"
version = "0.36.1"
@@ -4918,12 +5021,42 @@ dependencies = [
"windows_x86_64_msvc 0.36.1",
]
[[package]]
name = "windows-sys"
version = "0.45.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "75283be5efb2831d37ea142365f009c02ec203cd29a3ebecbc093d52315b66d0"
dependencies = [
"windows-targets",
]
[[package]]
name = "windows-targets"
version = "0.42.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8e5180c00cd44c9b1c88adb3693291f1cd93605ded80c250a75d472756b4d071"
dependencies = [
"windows_aarch64_gnullvm",
"windows_aarch64_msvc 0.42.2",
"windows_i686_gnu 0.42.2",
"windows_i686_msvc 0.42.2",
"windows_x86_64_gnu 0.42.2",
"windows_x86_64_gnullvm",
"windows_x86_64_msvc 0.42.2",
]
[[package]]
name = "windows-tokens"
version = "0.37.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3263d25f1170419995b78ff10c06b949e8a986c35c208dc24333c64753a87169"
[[package]]
name = "windows_aarch64_gnullvm"
version = "0.42.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "597a5118570b68bc08d8d59125332c54f1ba9d9adeedeef5b99b02ba2b0698f8"
[[package]]
name = "windows_aarch64_msvc"
version = "0.32.0"
@@ -4942,6 +5075,12 @@ version = "0.37.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2623277cb2d1c216ba3b578c0f3cf9cdebeddb6e66b1b218bb33596ea7769c3a"
[[package]]
name = "windows_aarch64_msvc"
version = "0.42.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e08e8864a60f06ef0d0ff4ba04124db8b0fb3be5776a5cd47641e942e58c4d43"
[[package]]
name = "windows_i686_gnu"
version = "0.24.0"
@@ -4966,6 +5105,12 @@ version = "0.37.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d3925fd0b0b804730d44d4b6278c50f9699703ec49bcd628020f46f4ba07d9e1"
[[package]]
name = "windows_i686_gnu"
version = "0.42.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c61d927d8da41da96a81f029489353e68739737d3beca43145c8afec9a31a84f"
[[package]]
name = "windows_i686_msvc"
version = "0.24.0"
@@ -4990,6 +5135,12 @@ version = "0.37.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ce907ac74fe331b524c1298683efbf598bb031bc84d5e274db2083696d07c57c"
[[package]]
name = "windows_i686_msvc"
version = "0.42.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "44d840b6ec649f480a41c8d80f9c65108b92d89345dd94027bfe06ac444d1060"
[[package]]
name = "windows_x86_64_gnu"
version = "0.24.0"
@@ -5014,6 +5165,18 @@ version = "0.37.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2babfba0828f2e6b32457d5341427dcbb577ceef556273229959ac23a10af33d"
[[package]]
name = "windows_x86_64_gnu"
version = "0.42.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8de912b8b8feb55c064867cf047dda097f92d51efad5b491dfb98f6bbb70cb36"
[[package]]
name = "windows_x86_64_gnullvm"
version = "0.42.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "26d41b46a36d453748aedef1486d5c7a85db22e56aff34643984ea85514e94a3"
[[package]]
name = "windows_x86_64_msvc"
version = "0.24.0"
@@ -5038,6 +5201,12 @@ version = "0.37.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f4dd6dc7df2d84cf7b33822ed5b86318fb1781948e9663bacd047fc9dd52259d"
[[package]]
name = "windows_x86_64_msvc"
version = "0.42.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9aec5da331524158c6d1a4ac0ab1541149c0b9505fde06423b02f5ef0106b9f0"
[[package]]
name = "winreg"
version = "0.10.1"
@@ -5126,13 +5295,32 @@ version = "0.13.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9fb9bace5b5589ffead1afb76e43e34cff39cd0f3ce7e170ae0c29e53b88eb1c"
dependencies = [
"asn1-rs",
"asn1-rs 0.3.1",
"base64",
"data-encoding",
"der-parser",
"der-parser 7.0.0",
"lazy_static",
"nom",
"oid-registry",
"oid-registry 0.4.0",
"ring",
"rusticata-macros",
"thiserror",
"time 0.3.11",
]
[[package]]
name = "x509-parser"
version = "0.14.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e0ecbeb7b67ce215e40e3cc7f2ff902f94a223acf44995934763467e7b1febc8"
dependencies = [
"asn1-rs 0.5.1",
"base64",
"data-encoding",
"der-parser 8.1.0",
"lazy_static",
"nom",
"oid-registry 0.6.1",
"ring",
"rusticata-macros",
"thiserror",
@@ -5188,7 +5376,7 @@ dependencies = [
"bzip2",
"constant_time_eq",
"crc32fast",
"crossbeam-utils 0.8.10",
"crossbeam-utils",
"flate2",
"hmac",
"pbkdf2",

View File

@@ -24,7 +24,7 @@ sudo = "0.6.0"
[dependencies]
serde = { version = "1.0", features = ["derive"] }
tauri = { version = "1.0.0-rc.9", features = ["api-all"] }
tauri = { version = "1.0.7", features = ["api-all"] }
# Access system process info.
sysinfo = "0.24.6"
@@ -41,12 +41,15 @@ once_cell = "1.13.0"
open = "3.0.2"
duct = "0.13.5"
# Services
windows-service = "0.6.0"
# Serialization.
serde_json = "1"
# Dependencies for the HTTP(S) proxy.
http = "0.2"
hudsucker = "0.17.2"
hudsucker = "0.19.1"
tracing = "0.1.21"
tokio-rustls = "0.23.0"
tokio-tungstenite = "0.17.0"
@@ -62,6 +65,7 @@ regex = "1"
# other
file_diff = "1.0.0"
rust-ini = "0.18.0"
ctrlc = "3.2.3"
[features]
# by default Tauri runs in production mode

View File

@@ -1,7 +1,7 @@
<RSAKeyValue>
<Exponent>AQAB</Exponent>
<Modulus>yytg/H9lz7Lm0XcA8LMqIyXPVNApYTcSepT4VDLB4qqqFC3s
/Huv8vN7zA/P4uoREIu8KMenADFk7uwrZSxoMWwJgn6A7sbAt1cqAaUXB
9J4NzhL0x3AFTiHEQbw86hRvm2VGkbA5sWnr0NZw8SGBBY+EODwNIt51G
dBA7eoUQU=</Modulus>
<RSAKeyValue>
<Exponent>AQAB</Exponent>
<Modulus>yytg/H9lz7Lm0XcA8LMqIyXPVNApYTcSepT4VDLB4qqqFC3s
/Huv8vN7zA/P4uoREIu8KMenADFk7uwrZSxoMWwJgn6A7sbAt1cqAaUXB
9J4NzhL0x3AFTiHEQbw86hRvm2VGkbA5sWnr0NZw8SGBBY+EODwNIt51G
dBA7eoUQU=</Modulus>
</RSAKeyValue>

View File

@@ -14,8 +14,9 @@
"enabled": "已启用",
"disabled": "已禁用",
"game_path": "选择游戏安装路径",
"game_command": "启动游戏的命令行",
"game_executable": "选择游戏可执行文件",
"recover_metadata": "紧急情况下恢复元数据文件",
"recover_rsa": "紧急情况下删除补丁文件",
"grasscutter_jar": "选择 Grasscutter JAR 文件",
"toggle_encryption": "启用加密",
"install_certificate": "安装代理证书",
@@ -24,10 +25,14 @@
"language": "选择语言",
"background": "设置自定义背景(链接或文件)",
"theme": "设置主题",
"patch_metadata": "自动修改元数据",
"use_proxy": "使用内置代理"
"patch_rsa": "自动修改RSA",
"use_proxy": "使用内置代理",
"wipe_login": "清除登录缓存",
"horny_mode": "Horny 模式",
"auto_mongodb": "自动启动 MongoDB"
},
"downloads": {
"grasscutter_fullbuild": "下载 Grasscutter 一体化",
"grasscutter_stable_data": "下载 Grasscutter 稳定版数据",
"grasscutter_latest_data": "下载 Grasscutter 开发版数据",
"grasscutter_stable_data_update": "更新 Grasscutter 稳定版数据",
@@ -37,7 +42,9 @@
"grasscutter_stable_update": "更新 Grasscutter 稳定版",
"grasscutter_latest_update": "更新 Grasscutter 开发版",
"resources": "下载 Grasscutter 资源",
"game": "下载游戏"
"game": "下载游戏",
"aio_header": "多合一下载:",
"individual_header": "个别部分下载:"
},
"download_status": {
"downloading": "下载中",
@@ -50,6 +57,7 @@
"select_file": "选择文件或文件夹...",
"select_folder": "选择文件夹...",
"download": "下载",
"delete": "删除",
"install": "安装"
},
"news": {
@@ -60,13 +68,15 @@
"port_help_text": "确保这是 Dispatch 服务器端口,而不是游戏服务器端口。大部分服务器的端口都是 443。",
"game_help_text": "你不需要另外的游戏备份来使用 Grasscutter。这是给想要降级到 2.6 或没有安装游戏的人使用的。",
"gc_stable_jar": "下载当前的 Grasscutter 稳定版,包括 JAR 文件和数据。",
"gc_fullbuild": "下载完整的 Grasscutter 构建版本包括存储库、jar 和资源。 已完全设置,不需要从此菜单下载任何其他内容",
"gc_dev_jar": "下载最新的 Grasscutter 开发版,包括 JAR 文件和数据。",
"gc_stable_data": "下载当前的 Grasscutter 稳定版数据,不包括 JAR 文件。此选项在更新时有帮助。",
"gc_dev_data": "下载最新的 Grasscutter 开发版数据,不包括 JAR 文件。此选项在更新时有帮助。",
"encryption": "此项设置通常应该处于关闭状态。",
"resources": "资源文件在运行 Grasscutter 服务器时是必要的。此选项在已经存在资源文件时不可选。",
"emergency_metadata": "在出现意外情况时自动将元数据恢复原始版本",
"emergency_rsa": "在出现意外情况时自动将 RSA 恢复原始版本",
"use_proxy": "使用 Cultivation 的内置代理。除非你使用 Fiddler 等软件,否则应启用此项。",
"patch_metadata": "自动修改和恢复游戏元数据。除非要游玩旧版/非官方版本,抑或你已经手动修改了元数据,否则应启用此。"
"patch_rsa": "自动修改和恢复 RSA 补丁。 除非您玩的是旧版/非官方版本,或者您手动修改了 RSA,否则应启用此功能。"
},
"swag": {
"akebi_name": "Akebi",

View File

@@ -14,8 +14,9 @@
"enabled": "已啟用",
"disabled": "未啟用",
"game_path": "選擇遊戲安裝路徑",
"game_command": "遊戲啟動命令",
"game_executable": "選擇遊戲執行檔",
"recover_metadata": "緊急恢復Metadata",
"recover_rsa": "緊急恢復RSA",
"grasscutter_jar": "選擇伺服器JAR檔案",
"toggle_encryption": "設定加密",
"install_certificate": "安裝代理憑證",
@@ -24,10 +25,14 @@
"language": "語言",
"background": "選擇自定義背景(網址或檔案)",
"theme": "選擇主題",
"patch_metadata": "自動修補Metadata",
"use_proxy": "使用內建代理伺服器"
"patch_rsa": "自動修補RSA",
"use_proxy": "使用內建代理伺服器",
"wipe_login": "擦除登錄緩存",
"horny_mode": "Horny模式",
"auto_mongodb": "自動啟動 MongoDB"
},
"downloads": {
"grasscutter_fullbuild": "下載Grasscutter多合一下載",
"grasscutter_stable_data": "下載Grasscutter穩定版數據Data",
"grasscutter_latest_data": "下載Grasscutter開發板數據Data",
"grasscutter_stable_data_update": "更新Grasscutter穩定版數據Data",
@@ -37,7 +42,9 @@
"grasscutter_stable_update": "更新Grasscutter穩定版",
"grasscutter_latest_update": "更新Grasscutter開發板",
"resources": "下載Grasscutter資源Resources",
"game": "下載遊戲"
"game": "下載遊戲",
"aio_header": "多合一下載:",
"individual_header": "個別部分下載:"
},
"download_status": {
"downloading": "下載中",
@@ -50,6 +57,7 @@
"select_file": "選擇檔案或資料夾...",
"select_folder": "選擇資料夾...",
"download": "下載",
"delete": "刪除",
"install": "安裝"
},
"news": {
@@ -60,14 +68,15 @@
"port_help_text": "確保這是Dispatch伺服器端口不是遊戲伺服器端口。 大部分伺服器的端口都是443。",
"game_help_text": "您不需要另外一個遊戲備份來使用Grasscutter。這是給想要降級到2.6或者還沒安裝遊戲的人使用的。",
"gc_stable_jar": "下載當前的Grasscutter穩定版本包括JAR答案還有資料文件。",
"gc_fullbuild": "下載完整的 Grasscutter 構建版本包括存儲庫、jar 和資源。 已完全設置,不需要從此菜單下載任何其他內容",
"gc_dev_jar": "下載當前的Grasscutter穩定版本資料文件其中不會附帶JAR文件。這個選項在更新時很有用。",
"gc_stable_data": "下載當前最新的Grasscutter開發版本資料文件其中不會附帶JAR文件。這個選項在更新時很有用。",
"gc_dev_data": "下載當前最新的Grasscutter開發版本的資料文件其中不會附帶JAR文件。這個選項在更新時很有用。",
"encryption": "在正常情況下,此選項應該被關閉。",
"resources": "資源文件在架設一個Grasscutter伺服器時是必要的。 這個選項會在您已經有裡面有檔案的資源資料夾時不可選。",
"emergency_metadata": "一旦有東西出了問題,此選項可以把您的Metadata恢復成官方版本。",
"emergency_rsa": "一旦有東西出了問題,此選項可以把您的rsa恢復成官方版本。",
"use_proxy": "使用Cultivation內建的代理伺服器。此選項應該被啟用除非你使用其他的代理伺服器。",
"patch_metadata": "自動修補和恢復Metadata。除非您的遊戲版本是舊的或者是非官方的,此選項應該被啟用。"
"patch_rsa": "自動修補和恢復RSA。除非您的遊戲版本是舊的或者是非官方的,此選項應該被啟用。"
},
"swag": {
"akebi_name": "Akebi",

View File

@@ -15,17 +15,23 @@
"disabled": "Deaktiviert",
"game_path": "Spielpfad",
"game_executable": "Spiel Datei auswählen",
"recover_metadata": "Notfall Wiederherstellung der Metadaten",
"recover_rsa": "Notfall Wiederherstellung der RSA",
"grasscutter_jar": "Grasscuter JAR auswählen",
"toggle_encryption": "Verschlüsselung umschalten",
"install_certificate": "Installeer proxy certificaat",
"java_path": "Benutzerdefinierten Java Pfad setzen",
"grasscutter_with_game": "Grasscutter automatisch mit dem Spiel starten",
"language": "Sprache auswählen",
"background": "Benutzerdefinierten Hintergrund festlegen (link oder bild)",
"theme": "Theme auswählen",
"patch_metadata": "Metadaten automatisch patchen"
"patch_rsa": "RSA automatisch patchen",
"use_proxy": "Gebruik interne proxy",
"wipe_login": "Wis de inlogcache",
"horny_mode": "Geile modus",
"auto_mongodb": "Start automatisch MongoDB"
},
"downloads": {
"grasscutter_fullbuild": "Alles in Einem Grasscutter Daten herunterladen",
"grasscutter_stable_data": "Stabile Grasscutter Daten herunterladen",
"grasscutter_latest_data": "Aktuellste Grasscutter Daten herunterladen",
"grasscutter_stable_data_update": "Stabile Grasscutter Daten aktualisieren",
@@ -35,7 +41,9 @@
"grasscutter_stable_update": "Stabile Grasscutter Version aktualisieren",
"grasscutter_latest_update": "Aktuellste Grasscutter Version aktualisieren",
"resources": "Grasscutter Ressourcen herunterladen",
"game": "Spiel herunterladen"
"game": "Spiel herunterladen",
"aio_header": "Alles in Einem herunterladen",
"individual_header": "Einzelne Teile herunterladen:"
},
"download_status": {
"downloading": "Lädt herunter",
@@ -48,6 +56,7 @@
"select_file": "Datei oder Ordner auswählen...",
"select_folder": "Ordner auswählen...",
"download": "Herunterladen",
"delete": "Löschen",
"install": "Installieren"
},
"news": {
@@ -58,13 +67,14 @@
"port_help_text": "Vergewissern Sie sich, dass es sich um den Port des Dispatch-Servers handelt, nicht um den Port des Spiel-Servers. Dieser ist fast immer '443'.",
"game_help_text": "Sie müssen keine separate Kopie verwenden, um mit Grasscutter zu spielen. Dies ist entweder für ein Downgrade auf die Version 2.6 oder wenn Sie das Spiel nicht installiert haben.",
"gc_stable_jar": "Laden Sie den aktuellen stabilen Grasscutter-Build herunter, der eine Jar-Datei und Datendateien enthält.",
"gc_fullbuild": "Download een volledige Grasscutter-build, inclusief repo, jar en bronnen. Is volledig ingesteld en vereist geen andere downloads uit dit menu",
"gc_dev_jar": "Laden Sie die neueste Grasscutter-Entwicklungsversion herunter, welche eine Jar-Datei und Datendateien enthält.",
"gc_stable_data": "Laden Sie die stabilen Grasscutter Daten herunter, welche keine Jar-Datei enthalten. Dies ist nützlich zum Aktualisieren.",
"gc_dev_data": "Laden Sie die neuesten Grasscutter-Entwicklungsdateien herunter, welche keine Jar-Datei enthält. Dies ist nützlich zum Aktualisieren.",
"resources": "Diese werden auch benötigt, um einen Grasscutter-Server auszuführen. Diese Schaltfläche ist grau, wenn Sie einen bestehenden Ressourcenordner mit Inhalten haben",
"emergency_metadata": "Im Fall, dass etwas schief laufen sollte, kannst du deine Metadaten auf die letzte offizielle Version zurücksetzen",
"emergency_rsa": "Im Fall, dass etwas schief laufen sollte, kannst du deine RSA auf die letzte offizielle Version zurücksetzen",
"use_proxy": "Nutze den internen Proxy von Cultivation. Du solltest dies aktivieren, es sei denn du nutzt Programme wie Fiddler",
"patch_metadata": "Patche und aktualisiere deine Metadaten automatisch. Solange du nicht mit einer alten/nicht offiziellen Version spielst oder deine Metadaten manuell gepatcht hast, sollte dies aktiviert sein."
"patch_rsa": "Patche und aktualisiere deine RSA automatisch. Solange du nicht mit einer alten/nicht offiziellen Version spielst oder deine RSA manuell gepatcht hast, sollte dies aktiviert sein."
},
"swag": {
"akebi_name": "Akebi",

View File

@@ -3,7 +3,7 @@
"main": {
"title": "Cultivation",
"launch_button": "Launch",
"gc_enable": "Connect via Grasscutter",
"gc_enable": "Connect to Grasscutter",
"https_enable": "Use HTTPS",
"ip_placeholder": "Server Address...",
"port_placeholder": "Port...",
@@ -14,8 +14,9 @@
"enabled": "Enabled",
"disabled": "Disabled",
"game_path": "Set Game Install Path",
"game_command": "Game Launch Command",
"game_executable": "Set Game Executable",
"recover_metadata": "Emergency Metadata Recovery",
"recover_rsa": "Emergency Delete RSA",
"grasscutter_jar": "Set Grasscutter JAR",
"toggle_encryption": "Toggle Encryption",
"install_certificate": "Install Proxy Certificate",
@@ -24,10 +25,14 @@
"language": "Select Language",
"background": "Set Custom Background (link or image file)",
"theme": "Set Theme",
"patch_metadata": "Automatically Patch Metadata",
"use_proxy": "Use Internal Proxy"
"patch_rsa": "Automatically Patch RSA",
"use_proxy": "Use Internal Proxy",
"wipe_login": "Wipe Login Cache",
"horny_mode": "Horny Mode",
"auto_mongodb": "Automatically Start MongoDB"
},
"downloads": {
"grasscutter_fullbuild": "Download Grasscutter 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",
@@ -37,7 +42,9 @@
"grasscutter_stable_update": "Update Grasscutter Stable",
"grasscutter_latest_update": "Update Grasscutter Latest",
"resources": "Download Grasscutter Resources",
"game": "Download Game"
"game": "Download Game",
"aio_header": "All-in-One Downloads:",
"individual_header": "Individual downloads:"
},
"download_status": {
"downloading": "Downloading",
@@ -50,6 +57,7 @@
"select_file": "Select file or folder...",
"select_folder": "Select folder...",
"download": "Download",
"delete": "Delete",
"install": "Install"
},
"news": {
@@ -60,20 +68,21 @@
"port_help_text": "Ensure this is the Dispatch server port, not the Game server port. This is almost always '443'.",
"game_help_text": "You do not need to use a separate copy to play with Grasscutter. This is for either downgrading to 2.6 or if you do not have the game installed.",
"gc_stable_jar": "Download the current stable Grasscutter build, which includes jar file and data files.",
"gc_fullbuild": "Download a full Grasscutter build, including repo, jar, and resources. Is fully set up and does not require any other downloads from this menu.",
"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.",
"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.",
"emergency_rsa": "In case something went wrong, force delete RSA patch.",
"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."
"patch_rsa": "Patch and unpatch your game RSA automatically. Unless playing with old/non-official versions (3.0 and older), this should be enabled."
},
"swag": {
"akebi_name": "Akebi",
"migoto_name": "Migoto",
"reshade_name": "Reshade",
"akebi": "Set Akebi Executable",
"akebi": "Set Akebi/Acrepi Executable",
"migoto": "Set 3DMigoto Executable",
"reshade": "Set Reshade Injector"
}

View File

@@ -14,8 +14,9 @@
"enabled": "Activado",
"disabled": "Desactivado",
"game_path": "Ruta de instalación del juego",
"game_command": "Comando de lanzamiento del juego",
"game_executable": "Establecer ejecutable del juego",
"recover_metadata": "Recuperación de Metadatos de Emergencia",
"recover_rsa": "Recuperación de RSA de Emergencia",
"grasscutter_jar": "Establecer JAR de Grasscutter",
"toggle_encryption": "Alternar Cifrado",
"install_certificate": "Instalar Certificado Proxie",
@@ -23,9 +24,15 @@
"grasscutter_with_game": "Iniciar automáticamente Grasscutter con el juego",
"language": "Seleccionar Idioma",
"background": "Establecer Fondo Personalizado (link o archivo de imagen)",
"theme": "Establecer Tema"
"patch_rsa": "Parchear RSA automáticamente",
"theme": "Establecer Tema",
"use_proxy": "Usar proxy interno",
"wipe_login": "Borrar caché de inicio de sesión",
"horny_mode": "Modo cachondo",
"auto_mongodb": "Iniciar automáticamente MongoDB"
},
"downloads": {
"grasscutter_fullbuild": "Descargar Datos todo en uno de Grasscutter",
"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",
@@ -35,7 +42,9 @@
"grasscutter_stable_update": "Actualizar Grasscutter Estable",
"grasscutter_latest_update": "Actualizar Grasscutter más reciente",
"resources": "Descargar Recursos de Grasscutter",
"game": "Descarga el juego"
"game": "Descarga el juego",
"aio_header": "Descargas todo en uno:",
"individual_header": "Descargas de piezas individuales:"
},
"download_status": {
"downloading": "Descargando",
@@ -48,6 +57,7 @@
"select_file": "Seleccionar el archivo o carpeta...",
"select_folder": "Seleccionar la carpeta...",
"download": "Descargar",
"delete": "Borrar",
"install": "Instalar"
},
"news": {
@@ -58,6 +68,7 @@
"port_help_text": "Asegúrese de que este sea el Dispatch server port, no el Game server port. Este es casi siempre '443'.",
"game_help_text": "No necesitas usar una copia separada para jugar con Grasscutter. Esto es para cambiar a 2.6 o si no tienes el juego instalado.",
"gc_stable_jar": "Descargue la versión Estable actual de Grasscutter, que incluye el archivo jar y los archivos de datos.",
"gc_fullbuild": "Descarga una compilación completa de Grasscutter, incluyendo repo, jar y recursos. Está totalmente configurado y no requiere ninguna otra descarga desde este menú.",
"gc_dev_jar": "Descargue la última versión de Desarrollo de Grasscutter, que incluye archivos jar y archivos de datos.",
"gc_stable_data": "Descargue los archivos de Datos Estables actuales de Grasscutter, que no vienen con un archivo jar. Esto es útil para actualizar.",
"gc_dev_data": "Descargue los últimos archivos de Datos de Desarrollo de Grasscutter, que no vienen con un archivo jar. Esto es útil para actualizar.",

View File

@@ -13,16 +13,26 @@
"options": {
"enabled": "active",
"disabled": "desactiver",
"game_path": "Définir le chemin d'installation du jeu",
"game_command": "Commande de lancement du jeu",
"game_executable": "definir l'executable du jeu",
"recover_rsa": "Récupération d'urgence des RSA",
"grasscutter_jar": "definir le Jar Grasscutter",
"toggle_encryption": "activer l'encryption",
"install_certificate": "Installer le certificat du proxy",
"java_path": "definir un chemin java personnalise",
"grasscutter_with_game": "Lancer Grasscutter automatiquement avec le jeu",
"language": "Choisir la langue",
"background": "definir un arriere plan personnalise (lien ou fichier image)",
"theme": "definir un theme"
"theme": "definir un theme",
"patch_rsa": "Corriger automatiquement les RSA",
"use_proxy": "Utiliser un proxy interne",
"wipe_login": "Effacer le cache de connexion",
"horny_mode": "Mode excitation",
"auto_mongodb": "Démarrer automatiquement MongoDB"
},
"downloads": {
"grasscutter_fullbuild": "Telecharger Grasscutter tout-en-un",
"grasscutter_stable_data": "Telecharger les donnees de Grasscutter (version stable)",
"grasscutter_latest_data": "Telecharger les donnees de Grasscutter (derniere version)",
"grasscutter_stable_data_update": "Mettre a jour les donnees de Grasscutter (version stable)",
@@ -31,7 +41,9 @@
"grasscutter_latest": "Telecharger Grasscutter (derniere version)",
"grasscutter_stable_update": "Mettre a jour Grasscutter (version stable)",
"grasscutter_latest_update": "Mettre a jour Grasscutter (derniere version)",
"resources": "Telecharger les ressources logicielles de Grasscutter"
"resources": "Telecharger les ressources logicielles de Grasscutter",
"aio_header": "Telechargements tout-en-un:",
"individual_header": "Telechargements de pièces individuelles:"
},
"download_status": {
"downloading": "Telechargement",
@@ -43,7 +55,9 @@
"components": {
"select_file": "choisir fichier ou dossier...",
"select_folder": "choisir dossier...",
"download": "Telecharger"
"download": "Telecharger",
"delete": "Supprimer",
"install": "Installer"
},
"news": {
"latest_commits": "Recents Commits",
@@ -53,6 +67,7 @@
"port_help_text": "Assurez-vous que c'est le port serveur Dispatch, et non le port du serveur de jeu. C'est presque toujours '433'.",
"game_help_text": "Vous n'avez pas besoin d'une copie differente du jeu pour jouer avec Grasscutter. Cela est ou pour retrograder en 2.6 ou si vous n'avez pas le jeu d'installe",
"gc_stable_jar": "Telecharger le dernier build stable de Grasscutter, ce qui inclut le fichier jar et les fichiers de donnees",
"gc_fullbuild": "Téléchargez un build complet de Grasscutter, incluant le repo, le jar et les ressources. Il est entièrement configuré et ne nécessite aucun autre téléchargement à partir de ce menu.",
"gc_dev_jar": "Telecharger le dernier build en development de Grasscutter, ce qui inclut le fichier jar et les fichiers de donnees",
"gc_stable_data": "Telecharger le dernier build stable de Grasscutter, ce qui n'inclut pasle fichier jar. Cela est utile pour mettre a jour",
"gc_dev_data": "Telecharger le dernier build en development de Grasscutter, ce qui n'inclut pasle fichier jar. Cela est utile pour mettre a jour",

View File

@@ -10,15 +10,28 @@
"files_extracting": "MengExtract File: "
},
"options": {
"game_executable": "Set Game Executable",
"enabled": "Diaktifkan",
"disabled": "Dinonaktifkan",
"game_path": "Mengatur jalur penginstalan game",
"game_command": "Perintah peluncuran game",
"game_executable": "Mengatur game yang dapat dieksekusi",
"recover_rsa": "Pemulihan RSA darurat",
"grasscutter_jar": "Path ke Grasscutter JAR",
"toggle_encryption": "Alihkan enkripsi",
"install_certificate": "Instal sertifikat proxy",
"java_path": "Atur kustom Java path",
"grasscutter_with_game": "Otomatis Menjalankan Grasscutter Dengan Game",
"language": "Pilih Bahasa",
"background": "Atur Kustom Latar Belakang (link atau gambar file)",
"theme": "Atur Tema"
"theme": "Atur Tema",
"patch_rsa": "Automatically Patch RSA",
"use_proxy": "Gunakan Proxy Internal",
"wipe_login": "Menghapus Cache Login",
"horny_mode": "Mode Terangsang",
"auto_mongodb": "Mulai MongoDB secara otomatis"
},
"downloads": {
"grasscutter_fullbuild": "Sedang Mendownload Grasscutter 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",
@@ -27,7 +40,9 @@
"grasscutter_latest": "Download Grasscutter Terbaru Version",
"grasscutter_stable_update": "Sedang MengUpdate Grasscutter Stabil",
"grasscutter_latest_update": "Sedang MengUpdate Grasscutter Terbaru",
"resources": "Mendownload Grasscutter Resources"
"resources": "Mendownload Grasscutter Resources",
"aio_header": "Unduhan Semua Dalam Satu:",
"individual_header": "Unduhan Bagian Individual:"
},
"download_status": {
"downloading": "Sedang Mendownload",
@@ -39,7 +54,8 @@
"components": {
"select_file": "Pilih File Atau Folder...",
"select_folder": "Pilih Folder...",
"download": "download"
"download": "download",
"delete": "Menghapus"
},
"news": {
"latest_commits": "Commit Terbaru",
@@ -49,6 +65,7 @@
"port_help_text": "Pastikan ini adalah port server port, bukan port server Game. ini Hampir Selalu '443'.",
"game_help_text": "Anda tidak perlu menggunakan salinan Genshin Impact untuk bermain dengan Grasscutter. Ini untuk menurunkan versi ke 2.6 atau jika Anda belum menginstal game..",
"gc_stable_jar": "Unduh Build Stabil Grasscutter Saat ini, Dimana Ada Jar File Dan Data File.",
"gc_fullbuild": "Unduh versi lengkap Grasscutter, termasuk repo, jar, dan sumber daya. Sudah diatur sepenuhnya dan tidak memerlukan unduhan lain dari menu ini.",
"gc_dev_jar": "Unduh Build Development Grasscutter saat ini, Dimana Ada Jar File Dan Data File.",
"gc_stable_data": "Unduh file data Grasscutter stabil saat ini, dimana Tidak Ada JAR file. Ini Berguna Untuk memperbarui.",
"gc_dev_data": "Unduh file data Grasscutter Development saat ini, dimana Tidak Ada JAR file. Ini Berguna Untuk memperbarui.",

89
src-tauri/lang/ko.json Normal file
View File

@@ -0,0 +1,89 @@
{
"lang_name": "한국어",
"main": {
"title": "Cultivation",
"launch_button": "게임 시작",
"gc_enable": "Grasscutter 연결",
"https_enable": "HTTPS 사용",
"ip_placeholder": "서버 주소",
"port_placeholder": "포트",
"files_downloading": "파일 다운로드 중: ",
"files_extracting": "파일 추출 중: "
},
"options": {
"enabled": "활성",
"disabled": "비활성",
"game_path": "게임 설치 경로 설정",
"game_command": "게임 시작 명령",
"game_executable": "게임 실행 파일 설정",
"recover_rsa": "긴급 RSA 복원",
"grasscutter_jar": "그래스커터 JAR 설정",
"toggle_encryption": "암호화 전환",
"install_certificate": "프록시 인증서 설치",
"java_path": "사용자 지정 Java 경로 설정",
"grasscutter_with_game": "게임에서 자동으로 그래스커터 실행",
"language": "언어 선택",
"background": "사용자 지정 배경 설정(링크 또는 이미지 파일)",
"theme": "테마 설정",
"patch_rsa": "RSA 패치 자동 적용",
"use_proxy": "내부 프록시 사용",
"wipe_login": "로그인 캐시 지우기",
"horny_mode": "Horny 모드",
"auto_mongodb": "MongoDB 자동 시작"
},
"downloads": {
"grasscutter_fullbuild": "올인원 Grasscutter 다운로드",
"grasscutter_stable_data": "안정적인 데이터 다운로드",
"grasscutter_latest_data": "최신 데이터 다운로드",
"grasscutter_stable_data_update": "안정적 데이터 업데이트",
"grasscutter_latest_data_update": "최신 데이터 업데이트",
"grasscutter_stable": "안정 다운로드",
"grasscutter_latest": "최신 다운로드",
"grasscutter_stable_update": "안정 업데이트",
"grasscutter_latest_update": "최신 업데이트",
"resources": "리소스 다운로드",
"game": "게임 다운로드",
"aio_header": "올인원 다운로드",
"individual_header": "개별 부품 다운로드:"
},
"download_status": {
"downloading": "다운로드 중",
"extracting": "추출 중",
"error": "오류",
"finished": "완료",
"stopped": "중지"
},
"components": {
"select_file": "파일 또는 폴더 선택...",
"select_folder": "폴더 선택...",
"download": "다운로드",
"delete": "삭제",
"install": "설치"
},
"news": {
"latest_commits": "공지 사항",
"latest_version": "최신 버전"
},
"help": {
"port_help_text": "게임 서버 포트가 아닌 패치 서버 포트인지 확인하십시오. (기본 포트: 443)",
"game_help_text": "그래스커터를 사용하기 위해 별도의 복사본을 사용할 필요가 없습니다. 이는 2.6으로 다운그레이드 하거나 게임을 설치하지 않은 경우에 적용됩니다.",
"gc_stable_jar": "jar 파일과 데이터 파일이 포함된 현재 안정적인 Grasscuter 빌드를 다운로드합니다.",
"gc_fullbuild": "저장소, 저장소, 리소스를 포함한 Grasscutter 정식 버전을 다운로드하세요. 모든 준비가 완료되었으며 이 메뉴에서 다른 다운로드는 필요하지 않습니다.",
"gc_dev_jar": "jar 파일 및 데이터 파일이 포함된 최신 개발 Grasscuter 빌드를 다운로드하십시오.",
"gc_stable_data": "jar 파일과 함께 제공되지 않는 현재 안정적인 Grasscuter 데이터 파일을 다운로드합니다. 이것은 업데이트하는 데 유용합니다.",
"gc_dev_data": "jar 파일과 함께 제공되지 않는 최신 개발 Grasscuter 데이터 파일을 다운로드합니다. 이것은 업데이트하는 데 유용합니다.",
"encryption": "일반적으로 이 기능을 사용하지 않도록 설정해야 합니다.",
"resources": "또한 Grasscutter 서버를 실행하는 데도 필요합니다. 내용이 포함된 기존 리소스 폴더가 있는 경우 이 버튼은 회색으로 표시됩니다",
"emergency_rsa": "문제가 있는 경우 RSA 패치를 제거하십시오.",
"use_proxy": "Culturation 내부 프록시를 사용합니다. 피들러와 같은 것을 사용하지 않는 한 이 기능을 활성화해야 합니다",
"patch_rsa": "게임 RSA를 자동으로 패치 및 패치 해제합니다. 이전/비공식 버전을 사용하거나 RSA를 수동으로 패치하지 않은 경우 이 기능을 활성화해야 합니다."
},
"swag": {
"akebi_name": "Akebi",
"migoto_name": "Migoto",
"reshade_name": "Reshade",
"akebi": "Akebi 실행 파일 설정",
"migoto": "3DMigoto 실행 파일 설정",
"reshade": "Reshade 인젝터 설정"
}
}

View File

@@ -14,15 +14,23 @@
"enabled": "Iespējots",
"disabled": "Atspējots",
"game_executable": "Iestatīt spēles izpildāmu",
"recover_rsa": "Avārijas RSA atjaunošana",
"grasscutter_jar": "Iestatiet Grasscutter JAR",
"toggle_encryption": "Pārslēgt Šifrēšanu",
"install_certificate": "Proxy sertifikāta instalēšana",
"java_path": "Iestatiet pielāgotu Java ceļu",
"grasscutter_with_game": "Automātiski palaidiet Grasscutter ar spēli",
"language": "Izvēlēties valodu",
"background": "Iestatīt pielāgotu fonu (saite vai attēla fails)",
"theme": "Iestatīt tēmu"
"theme": "Iestatīt tēmu",
"patch_rsa": "Automātiski ielāpot RSA",
"use_proxy": "Izmantot iekšējo starpniekserveri",
"wipe_login": "Noslaucīt pieteikšanās kešatmiņu",
"horny_mode": "Uzbudināts režīms",
"auto_mongodb": "Automātiski startējiet MongoDB"
},
"downloads": {
"grasscutter_fullbuild": "Lejupielādējiet Grasscutter 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",
@@ -31,7 +39,9 @@
"grasscutter_latest": "Lejupielādēt Grasscutter jaunāko",
"grasscutter_stable_update": "Atjauniet Grasscutter stabilo",
"grasscutter_latest_update": "Atjauniet Grasscutter jaunāko",
"resources": "Lejupielādējiet Grasscutter resursi"
"resources": "Lejupielādējiet Grasscutter resursi",
"aio_header": "Lejupielādes viss vienā",
"individual_header": "Atsevišķu daļu lejupielādes:"
},
"download_status": {
"downloading": "Notiek lejupielāde",
@@ -43,7 +53,8 @@
"components": {
"select_file": "Izvēlēties failu vai mapu...",
"select_folder": "Izvēlēties mapu...",
"download": "Lejupielādēt"
"download": "Lejupielādēt",
"delete": "Dzēst"
},
"news": {
"latest_commits": "Nesen kommitus",
@@ -53,6 +64,7 @@
"port_help_text": "Pārliecinieties, vai tas ir Dispatch-servera ports, nevis spēļu servera ports. Tas gandrīz vienmēr ir '443'.",
"game_help_text": "Lai spēlētu ar Grasscutter, jums nav jāizmanto atsevišķa kopija. Tas ir izveidots, lai pazeminātu versiju uz 2.6 vai ja jums nav instalēta spēle.",
"gc_stable_jar": "Lejupielādējiet pašreizējo stabilo Grasscutter versiju, kuram ir jar failu un datu failus.",
"gc_fullbuild": "Lejupielādējiet pilnu Grasscutter versiju, tostarp repozitorijus, krātuves un resursus. Viss ir gatavs, un no šīs izvēlnes nav nepieciešama nekāda cita lejupielāde.",
"gc_dev_jar": "Lejupielādējiet jaunāko izstrāde Grasscutter versiju, kuram ir jar failu un datu failus.",
"gc_stable_data": "Lejupielādējiet pašreizējos stabilos Grasscutter datu failus, kuriem nav jar fails. Tas ir noderīgi atjaunināšanai.",
"gc_dev_data": "Lejupielādējiet jaunāko izstrāde Grasscutter datu failus, kuriem nav pievienots jar fails. Tas ir noderīgi atjaunināšanai.",

88
src-tauri/lang/nl.json Normal file
View File

@@ -0,0 +1,88 @@
{
"lang_name": "Nederlands",
"main": {
"title": "Cultivation",
"launch_button": "Start",
"gc_enable": "Verbind Met Grasscutter",
"https_enable": "Gebruik HTTPS",
"ip_placeholder": "Server Address...",
"port_placeholder": "Poort...",
"files_downloading": "Bestanden Aan Downloaden: ",
"files_extracting": "Bestanden Uitpakken: "
},
"options": {
"enabled": "Ingeschakeld",
"disabled": "Uitgeschakeld",
"game_path": "Spel Installatie Pad Instellen",
"game_executable": "Stel De Exe Van Het Spel In",
"recover_rsa": "Noodherstel Van De RSA",
"grasscutter_jar": "Stel De Grasscutter JAR In",
"toggle_encryption": "Versleuteling Inschakelen",
"install_certificate": "Proxy-certificaat installeren",
"java_path": "Aangepast Java-pad Instellen",
"grasscutter_with_game": "Start Automatisch Grasscutter Met Spel",
"language": "Selecteer Taal",
"background": "Aangepaste Achtergrond Instellen (link of afbeeldingsbestand)",
"theme": "Thema instellen",
"patch_rsa": "RSA Automatisch Bijwerken",
"use_proxy": "Gebruik Interne Proxy",
"wipe_login": "Login cache wissen",
"horny_mode": "Geile modus",
"auto_mongodb": "Start automatisch MongoDB"
},
"downloads": {
"grasscutter_fullbuild": "Grasscutter Alles-in-één 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",
"grasscutter_latest_data_update": "Nieuwste gegevens Van Grasscutter bijwerken",
"grasscutter_stable": "Download Stabiele Versie Van Grasscutter",
"grasscutter_latest": "Download De Nieuwste Versie Van Grasscutter",
"grasscutter_stable_update": "Update Grasscutter Naar De Stabiele Versie",
"grasscutter_latest_update": "Update Grasscutter Naar De Nieuwste Versie",
"resources": "Download Grasscutter bronnen",
"game": "Download Spel",
"aio_header": "Alles-in-één Downloads:",
"individual_header": "Downloads van afzonderlijke onderdelen:"
},
"download_status": {
"downloading": "Aan Het Downloading",
"extracting": "Uitpakken",
"error": "Fout",
"finished": "Voltooid",
"stopped": "Gestopt"
},
"components": {
"select_file": "Select file or folder...",
"select_folder": "Select folder...",
"download": "Download",
"delete": "Verwijder",
"install": "Install"
},
"news": {
"latest_commits": "Recente Opdrachten",
"latest_version": "Nieuwste Versie"
},
"help": {
"port_help_text": "Zorg ervoor dat dit de Dispatch server poort is, niet de Game server poort. Dit is bijna altijd '443'.",
"game_help_text": "U hoeft geen aparte kopie te gebruiken om met Grasscutter te spelen. Dit is voor downgraden naar 2.6 of als u het spel niet geinstalleerd heeft.",
"gc_stable_jar": "Download de huidige stabiele Grasscutter build, die jar file en data bestanden bevat.",
"gc_fullbuild": "Download een volledige Grasscutter build, inclusief repo, jar en resources. Is volledig ingesteld en vereist geen andere downloads uit dit menu.",
"gc_dev_jar": "Download de laatste ontwikkeling Grasscutter build, die jar file en data bestanden bevat.",
"gc_stable_data": "Download de huidige stabiele versie van de Grasscutter data bestanden, die niet met een jar file komen. Dit is handig voor het bijwerken.",
"gc_dev_data": "Download de nieuwste versie van de Grasscutter data bestanden, die niet met een jar file komen. Dit is handig voor het bijwerken.",
"encryption": "Dit wordt meestal uitgeschakeld.",
"resources": "Deze zijn ook nodig om een Grasscutter server te draaien. Deze knop zal grijs zijn als u een bestaande resources map heeft met inhoud erin",
"emergency_rsa": "Voor het geval er iets fout is gegaan, herstel uw rsa naar de laatste offici<63>le versies rsa.",
"use_proxy": "Gebruik de Cultivation interne proxy. U zou dit ingeschakeld moeten hebben, tenzij u iets als Fiddler gebruikt",
"patch_rsa": "Patch en unpatch je spel rsa automatisch. Tenzij je met oude/niet-offici<63>le versies speelt, of je hebt je rsa handmatig gepatcht, zou dit ingeschakeld moeten zijn."
},
"swag": {
"akebi_name": "Akebi",
"migoto_name": "Migoto",
"reshade_name": "Reshade",
"akebi": "Stel Akebi Exe Bestand In",
"migoto": "Stel 3DMigoto Exe Bestand In",
"reshade": "Stel Reshade Injector In"
}
}

View File

@@ -15,7 +15,7 @@
"disabled": "Выключено",
"game_path": "Установить путь к файлам игры",
"game_executable": "Установить исполняемый файл игры",
"recover_metadata": "Принудительное восстановление Метаданных",
"recover_rsa": "Принудительное удаление RSA",
"grasscutter_jar": "Установить Grasscutter JAR",
"toggle_encryption": "Переключить шифрование",
"install_certificate": "Установить сертификат для работы Прокси",
@@ -24,10 +24,14 @@
"language": "Установить язык",
"background": "Установить свой фон (ссылка или файл)",
"theme": "Установить тему",
"patch_metadata": "Автоматический патч Метаданных при запуске",
"use_proxy": "Использовать встроенный Прокси"
"patch_rsa": "Автоматическое исправление RSA",
"use_proxy": "Использовать встроенный Прокси",
"wipe_login": "Очистить кэш входа в систему",
"horny_mode": "роговой режим",
"auto_mongodb": "Автоматически запускать MongoDB"
},
"downloads": {
"grasscutter_fullbuild": "Скачать все в одном Grasscutter",
"grasscutter_stable_data": "Скачать стабильные данные Grasscutter",
"grasscutter_latest_data": "Скачать последние данные Grasscutter",
"grasscutter_stable_data_update": "Обновить стабильные данные Grasscutter",
@@ -37,7 +41,9 @@
"grasscutter_stable_update": "Обновить стабильную версию Grasscutter",
"grasscutter_latest_update": "Обновить последнюю версию Grasscutter",
"resources": "Скачать ресурсы Grasscutter",
"game": "Скачать Игру"
"game": "Скачать Игру",
"aio_header": "Все в одной загрузке:",
"individual_header": "загрузка отдельных частей:"
},
"download_status": {
"downloading": "Скачивание",
@@ -50,7 +56,8 @@
"select_file": "Выберите файл или папку...",
"select_folder": "Выберите папку...",
"download": "Скачать",
"install": "Установить"
"delete": "Удалить",
"install": "Установить"
},
"news": {
"latest_commits": "Последние коммиты",
@@ -60,14 +67,15 @@
"port_help_text": "Убедитесь, что это порт Dispatch-сервера, не порт игрового сервера. Обычно это '443'.",
"game_help_text": "Вам не нужно устанавливать еще одну копию, что бы играть с Grascutter. Это нужно или для версии 2.6, или если у Вас не установлена игра.",
"gc_stable_jar": "Скачать последнюю стабильную версию Grasscutter, которая содержит jar файл и данные.",
"gc_fullbuild": "Загрузите полную сборку Grasscutter, включая репо, jar и ресурсы. Полностью настроена и не требует других загрузок из этого меню.",
"gc_dev_jar": "Скачать последнюю версию для разработки Grasscutter, которая содержит jar файл и данные.",
"gc_stable_data": "Скачать стабильные данные Grasscutter, в которой нету jar файла. Это полезно для обновления.",
"gc_dev_data": "Скачать последнюю версию для разработки Grasscutter, в которой нету jar файла. Это полезно для обновления.",
"encryption": "Обычно это должно быть выключено.",
"resources": "Это необходимо для запуска сервера Grasscutter. Эта кнопка будет серой, если у Вас уже есть не пустая папка с ресурсами.",
"emergency_metadata": "Если что-то пошло не так, восстановит Метаданные до последней официальной версии.",
"emergency_rsa": "Если что-то пошло не так, восстановит RSA до последней официальной версии.",
"use_proxy": "Использовать встроенный Прокси. Отключите если используете Fiddler или подобную программу",
"patch_metadata": "Патчит и восстанавливает Метаданные автоматически. Если вы не играете на старых/модифицированых версиях, или сами в ручную патчите Метаданные, эта опция должна быть включена."
"patch_rsa": "Патчит и восстанавливает RSA автоматически. Если вы не играете на старых/модифицированых версиях, или сами в ручную патчите Метаданные, эта опция должна быть включена."
},
"swag": {
"akebi_name": "Akebi",

View File

@@ -14,20 +14,25 @@
"enabled": "Bật",
"disabled": "Tắt",
"game_path": "Đường dẫn cài game",
"game_command": "Lệnh khởi chạy game",
"game_executable": "Tập tin thực thi game",
"recover_metadata": "Khôi phục Metadata khẩn cấp",
"recover_rsa": "Khôi phục RSA khẩn cấp",
"grasscutter_jar": "Tập tin JAR Grasscutter",
"toggle_encryption": "Bật/tắt mã hóa",
"install_certificate": "Cài chứng chỉ proxy",
"java_path": "Đường dẫn Java tùy chỉnh",
"grasscutter_with_game": "Tự động chạy Grasscutter cùng với game",
"language": "Chọn ngôn ngữ",
"language": "Ngôn ngữ",
"background": "Hình nền tùy chỉnh (liên kết hoặc tập tin hình ảnh)",
"theme": "Giao diện",
"patch_metadata": "Tự động sửa Metadata",
"use_proxy": "Sử dụng proxy nội bộ"
"patch_rsa": "Tự động vá RSA",
"use_proxy": "Sử dụng proxy nội bộ",
"wipe_login": "Tẩy sạch cache đăng nhập",
"horny_mode": "Chế độ hứng tình",
"auto_mongodb": "Tự động khởi động MongoDB"
},
"downloads": {
"grasscutter_fullbuild": "Tải Grasscutter 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",
@@ -37,20 +42,23 @@
"grasscutter_stable_update": "Cập nhật Grasscutter bản ổn định",
"grasscutter_latest_update": "Cập nhật Grasscutter bản mới nhất",
"resources": "Tải tài nguyên Grasscutter",
"game": "Tải game"
"game": "Tải game",
"aio_header": "Tải xuống tất cả trong một:",
"individual_header": "Tải xuống từng phần:"
},
"download_status": {
"downloading": "Đang tải xuống",
"downloading": "Đang tải",
"extracting": "Đang giải nén",
"error": "Lỗi",
"finished": "Hoàn thành",
"finished": "Đã hoàn thành",
"stopped": "Đã dừng"
},
"components": {
"select_file": "Chọn tập tin hoặc thư mục...",
"select_folder": "Chọn thư mục...",
"download": "Tải xuống",
"install": "Cài đặt"
"download": "Tải",
"delete": "Xóa bỏ",
"install": "Cài"
},
"news": {
"latest_commits": "Thay Đổi Gần Đây",
@@ -60,13 +68,22 @@
"port_help_text": "Hãy đảm bảo đây là cổng của máy chủ Dispatch, không phải cổng của máy chủ game. Thường sẽ là '443'.",
"game_help_text": "Bạn không cần phải sử dụng một bản sao riêng để chơi với Grasscutter. Việc này chỉ xảy ra nếu bạn hạ phiên bản xuống 2.6 hoặc chưa cài game.",
"gc_stable_jar": "Tải xuống phiên bản ổn định của Grasscutter, bao gồm tập tin jar và các tệp dữ liệu.",
"gc_fullbuild": "Tải xuống bản dựng Grasscutter đầy đủ, bao gồm repo, jar và tài nguyên. Được thiết lập đầy đủ và không yêu cầu bất kỳ tải xuống nào khác từ menu này",
"gc_dev_jar": "Tải xuống phiên bản phát triển mới nhất của Grasscutter, bao gồm tập tin jar và các tệp dữ liệu.",
"gc_stable_data": "Tải xuống tệp dữ liệu phiên bản ổn định hiện hành của Grasscutter. Bản này không đi kèm với tập tin jar, hữu dụng khi muốn cập nhật.",
"gc_dev_data": "Tải xuống tệp dữ liệu phiên bản mới nhất của Grasscutter. Bản này không đi kèm với tập tin jar, hữu dụng khi muốn cập nhật.",
"resources": "Chúng được yêu cầu để chạy máy chủ Grasscutter. Nút này sẽ có màu xám nếu bạn đã có sẵn một thư mục tài nguyên có nội dung bên trong"
"gc_dev_data": "Tải xuống tệp dữ liệu phiên bản mới nhất của Grasscutter. Bản này không đi kèm với tập tin jar, hữu dụng khi muốn cập nhật.",
"encryption": "Mục này nên được tắt.",
"resources": "Tài nguyên được yêu cầu để chạy máy chủ Grasscutter. Nút này sẽ có màu xám nếu bạn đã có sẵn một thư mục tài nguyên (resources) có nội dung bên trong",
"emergency_rsa": "Trong trường hợp gặp vấn đề, khôi phục lại RSA về phiên bản chính thức mới nhất.",
"use_proxy": "Sử dụng proxy nội bộ của Cultivation. Nên bật tùy chọn này trừ khi bạn đang sử dụng một ứng dụng khác, như Fiddler",
"patch_rsa": "Tự động vá và sửa lại RSA của game. Tùy chọn này nên được bật trừ khi bạn đang sử dụng phiên bản cũ, phiên bản không chính thức hoặc bạn đã tự vá rsa rồi."
},
"swag": {
"akebi_name": "Akebi",
"migoto_name": "Migoto",
"reshade_name": "Reshade",
"akebi": "Tập tin thực thi Akebi",
"migoto": "Tập tin thực thi 3dMigoto"
"migoto": "Tập tin thực thi 3dMigoto",
"reshade": "Tập tin inject Reshade"
}
}

BIN
src-tauri/patch/version.dll Normal file

Binary file not shown.

22
src-tauri/src/admin.rs Normal file
View File

@@ -0,0 +1,22 @@
#[cfg(windows)]
pub fn reopen_as_admin() {
use std::process::{exit, Command};
let install = std::env::current_exe().unwrap();
println!("Opening as admin: {}", install.to_str().unwrap());
Command::new("powershell.exe")
.arg("powershell")
.arg(format!(
"-command \"&{{Start-Process -filepath '{}' -verb runas}}\"",
install.to_str().unwrap()
))
.spawn()
.expect("Error starting exec as admin");
exit(0);
}
#[cfg(target_os = "linux")]
pub fn reopen_as_admin() {}

View File

@@ -1,6 +1,7 @@
use file_diff::diff;
use std::fs;
use std::io::{Read, Write};
use std::path::PathBuf;
#[tauri::command]
pub fn rename(path: String, new_name: String) {
@@ -16,9 +17,9 @@ pub fn rename(path: String, new_name: String) {
new_path = path.replace('\\', "/");
}
let path_replaced = &path.replace(&new_path.split('/').last().unwrap(), &new_name);
let path_replaced = &path.replace(new_path.split('/').last().unwrap(), &new_name);
match fs::rename(&path, &path_replaced) {
match fs::rename(&path, path_replaced) {
Ok(_) => {
println!("Renamed {} to {}", &path, path_replaced);
}
@@ -35,19 +36,19 @@ pub fn dir_create(path: String) {
#[tauri::command]
pub fn dir_exists(path: &str) -> bool {
let path_buf = std::path::PathBuf::from(path);
let path_buf = PathBuf::from(path);
fs::metadata(path_buf).is_ok()
}
#[tauri::command]
pub fn dir_is_empty(path: &str) -> bool {
let path_buf = std::path::PathBuf::from(path);
let path_buf = PathBuf::from(path);
fs::read_dir(path_buf).unwrap().count() == 0
}
#[tauri::command]
pub fn dir_delete(path: &str) {
let path_buf = std::path::PathBuf::from(path);
let path_buf = PathBuf::from(path);
fs::remove_dir_all(path_buf).unwrap();
}
@@ -59,16 +60,15 @@ pub fn are_files_identical(path1: &str, path2: &str) -> bool {
#[tauri::command]
pub fn copy_file(path: String, new_path: String) -> bool {
let filename = &path.split('/').last().unwrap();
let mut new_path_buf = std::path::PathBuf::from(&new_path);
let path_buf = std::path::PathBuf::from(&path);
let path_buf = PathBuf::from(&path);
// If the new path doesn't exist, create it.
if !dir_exists(new_path_buf.pop().to_string().as_str()) {
if !dir_exists(PathBuf::from(&new_path).pop().to_string().as_str()) {
std::fs::create_dir_all(&new_path).unwrap();
}
// Copy old to new
match std::fs::copy(&path_buf, format!("{}/{}", new_path, filename)) {
match std::fs::copy(path_buf, format!("{}/{}", new_path, filename)) {
Ok(_) => true,
Err(e) => {
println!("Failed to copy file: {}", e);
@@ -81,11 +81,11 @@ pub fn copy_file(path: String, new_path: String) -> bool {
#[tauri::command]
pub fn copy_file_with_new_name(path: String, new_path: String, new_name: String) -> bool {
let mut new_path_buf = std::path::PathBuf::from(&new_path);
let path_buf = std::path::PathBuf::from(&path);
let mut new_path_buf = PathBuf::from(&new_path);
let path_buf = PathBuf::from(&path);
// If the new path doesn't exist, create it.
if !dir_exists(new_path_buf.pop().to_string().as_str()) {
if !dir_exists(PathBuf::from(&new_path).pop().to_string().as_str()) {
match std::fs::create_dir_all(&new_path) {
Ok(_) => {}
Err(e) => {
@@ -95,8 +95,10 @@ pub fn copy_file_with_new_name(path: String, new_path: String, new_name: String)
};
}
new_path_buf.push(new_name);
// Copy old to new
match std::fs::copy(&path_buf, format!("{}/{}", new_path, new_name)) {
match std::fs::copy(path_buf, &new_path_buf) {
Ok(_) => true,
Err(e) => {
println!("Failed to copy file: {}", e);
@@ -109,7 +111,7 @@ pub fn copy_file_with_new_name(path: String, new_path: String, new_name: String)
#[tauri::command]
pub fn delete_file(path: String) -> bool {
let path_buf = std::path::PathBuf::from(&path);
let path_buf = PathBuf::from(&path);
match std::fs::remove_file(path_buf) {
Ok(_) => true,
@@ -124,13 +126,18 @@ pub fn delete_file(path: String) -> bool {
#[tauri::command]
pub fn read_file(path: String) -> String {
let path_buf = std::path::PathBuf::from(&path);
let path_buf = PathBuf::from(&path);
let mut file = match fs::File::open(path_buf) {
Ok(file) => file,
Err(e) => {
println!("Failed to open file: {}", e);
return String::new();
if path.contains("config") {
// Server.ts won't print the error so handle the message here for the user
println!("Server config not found or invalid. Be sure to run the server at least once to generate it before making edits.");
} else {
println!("Failed to open file: {}", e);
}
return String::new(); // Send back error for handling by the caller
}
};
@@ -142,10 +149,10 @@ pub fn read_file(path: String) -> String {
#[tauri::command]
pub fn write_file(path: String, contents: String) {
let path_buf = std::path::PathBuf::from(&path);
let path_buf = PathBuf::from(&path);
// Create file if it exists, otherwise just open and rewrite
let mut file = match fs::File::create(&path_buf) {
let mut file = match fs::File::create(path_buf) {
Ok(file) => file,
Err(e) => {
println!("Failed to open file: {}", e);

View File

@@ -9,23 +9,19 @@ static SITE_URL: &str = "https://gamebanana.com";
#[tauri::command]
pub async fn get_download_links(mod_id: String) -> String {
let res = web::query(format!("{}/apiv9/Mod/{}/DownloadPage", SITE_URL, mod_id).as_str()).await;
res
web::query(format!("{}/apiv9/Mod/{}/DownloadPage", SITE_URL, mod_id).as_str()).await
}
#[tauri::command]
pub async fn list_submissions(mode: String, page: String) -> String {
let res = web::query(
web::query(
format!(
"{}/apiv9/Util/Game/Submissions?_idGameRow=8552&_nPage={}&_nPerpage=50&_sMode={}",
SITE_URL, page, mode
)
.as_str(),
)
.await;
res
.await
}
#[tauri::command]

View File

@@ -9,7 +9,7 @@ pub async fn get_lang(window: tauri::Window, lang: String) -> String {
let lang_path: PathBuf = [&install_location(), "lang", &format!("{}.json", lang)]
.iter()
.collect();
match std::fs::read_to_string(&lang_path) {
match std::fs::read_to_string(lang_path) {
Ok(x) => x,
Err(e) => {
emit_lang_err(window, format!("Failed to read language file: {}", e));

View File

@@ -3,76 +3,141 @@
windows_subsystem = "windows"
)]
use file_helpers::dir_exists;
use once_cell::sync::Lazy;
use std::fs;
use std::io::Write;
use std::{collections::HashMap, sync::Mutex};
use system_helpers::is_elevated;
use tauri::api::path::data_dir;
use tauri::async_runtime::block_on;
use std::thread;
use structs::APIQuery;
use sysinfo::{System, SystemExt};
use sysinfo::{Pid, ProcessExt, System, SystemExt};
use crate::admin::reopen_as_admin;
mod admin;
mod downloader;
mod file_helpers;
mod gamebanana;
mod lang;
mod metadata_patcher;
mod proxy;
mod structs;
mod system_helpers;
mod unzip;
mod web;
static WATCH_GAME_PROCESS: Lazy<Mutex<String>> = Lazy::new(|| Mutex::new(String::new()));
static WATCH_GRASSCUTTER_PROCESS: Lazy<Mutex<String>> = Lazy::new(|| Mutex::new(String::new()));
static GC_PID: std::sync::Mutex<usize> = Mutex::new(696969);
fn try_flush() {
std::io::stdout().flush().unwrap_or(())
}
fn has_arg(args: &[String], arg: &str) -> bool {
args.contains(&arg.to_string())
}
async fn arg_handler(args: &[String]) {
if has_arg(args, "--proxy") {
let mut pathbuf = tauri::api::path::data_dir().unwrap();
pathbuf.push("cultivation");
pathbuf.push("ca");
connect(8035, pathbuf.to_str().unwrap().to_string()).await;
}
}
fn main() {
let args: Vec<String> = std::env::args().collect();
if !is_elevated() && !has_arg(&args, "--no-admin") {
println!("===============================================================================");
println!("You running as a non-elevated user. Some stuff will almost definitely not work.");
println!("===============================================================================");
reopen_as_admin();
}
// Setup datadir/cultivation just in case something went funky and it wasn't made
if !dir_exists(data_dir().unwrap().join("cultivation").to_str().unwrap()) {
fs::create_dir_all(data_dir().unwrap().join("cultivation")).unwrap();
}
// Always set CWD to the location of the executable.
let mut exe_path = std::env::current_exe().unwrap();
exe_path.pop();
std::env::set_current_dir(&exe_path).unwrap();
tauri::Builder::default()
.invoke_handler(tauri::generate_handler![
enable_process_watcher,
connect,
disconnect,
req_get,
get_bg_file,
is_game_running,
get_theme_list,
system_helpers::run_command,
system_helpers::run_program,
system_helpers::run_program_relative,
system_helpers::run_jar,
system_helpers::open_in_browser,
system_helpers::install_location,
system_helpers::is_elevated,
system_helpers::set_migoto_target,
proxy::set_proxy_addr,
proxy::generate_ca_files,
unzip::unzip,
file_helpers::rename,
file_helpers::dir_create,
file_helpers::dir_exists,
file_helpers::dir_is_empty,
file_helpers::dir_delete,
file_helpers::copy_file,
file_helpers::copy_file_with_new_name,
file_helpers::delete_file,
file_helpers::are_files_identical,
file_helpers::read_file,
file_helpers::write_file,
downloader::download_file,
downloader::stop_download,
lang::get_lang,
lang::get_languages,
web::valid_url,
web::web_get,
gamebanana::get_download_links,
gamebanana::list_submissions,
gamebanana::list_mods,
metadata_patcher::patch_metadata
])
.run(tauri::generate_context!())
.expect("error while running tauri application");
block_on(arg_handler(&args));
// For disabled GUI
ctrlc::set_handler(|| {
disconnect();
std::process::exit(0);
})
.unwrap_or(());
if !has_arg(&args, "--no-gui") {
tauri::Builder::default()
.invoke_handler(tauri::generate_handler![
enable_process_watcher,
enable_grasscutter_watcher,
connect,
disconnect,
req_get,
is_game_running,
is_grasscutter_running,
restart_grasscutter,
get_theme_list,
system_helpers::run_command,
system_helpers::run_program,
system_helpers::run_program_relative,
system_helpers::start_service,
system_helpers::service_status,
system_helpers::stop_service,
system_helpers::run_jar,
system_helpers::open_in_browser,
system_helpers::install_location,
system_helpers::is_elevated,
system_helpers::set_migoto_target,
system_helpers::wipe_registry,
system_helpers::get_platform,
proxy::set_proxy_addr,
proxy::generate_ca_files,
unzip::unzip,
file_helpers::rename,
file_helpers::dir_create,
file_helpers::dir_exists,
file_helpers::dir_is_empty,
file_helpers::dir_delete,
file_helpers::copy_file,
file_helpers::copy_file_with_new_name,
file_helpers::delete_file,
file_helpers::are_files_identical,
file_helpers::read_file,
file_helpers::write_file,
downloader::download_file,
downloader::stop_download,
lang::get_lang,
lang::get_languages,
web::valid_url,
web::web_get,
gamebanana::get_download_links,
gamebanana::list_submissions,
gamebanana::list_mods
])
.run(tauri::generate_context!())
.expect("error while running tauri application");
} else {
try_flush();
println!("Press enter or CTRL-C twice to quit...");
std::io::stdin().read_line(&mut String::new()).unwrap();
}
// Always disconnect upon closing the program
disconnect();
}
#[tauri::command]
@@ -100,7 +165,8 @@ fn enable_process_watcher(window: tauri::Window, process: String) {
let mut system = System::new_all();
loop {
thread::sleep(std::time::Duration::from_secs(5));
// Shorten loop timer to avoid user closing Cultivation before unpatching/proxy disconnecting
thread::sleep(std::time::Duration::from_secs(2));
// Refresh system info
system.refresh_all();
@@ -127,6 +193,104 @@ fn enable_process_watcher(window: tauri::Window, process: String) {
});
}
#[tauri::command]
fn is_grasscutter_running() -> bool {
// Grab the grasscutter process name
let proc = WATCH_GRASSCUTTER_PROCESS.lock().unwrap().to_string();
!proc.is_empty()
}
#[tauri::command]
fn restart_grasscutter(window: tauri::Window) -> bool {
let pid: usize = *GC_PID.lock().unwrap();
let system = System::new_all();
// Get the process
if let Some(process) = system.process(Pid::from(pid)) {
// Kill it
if process.kill() {
// Also kill the cmd it was open in
if let Some(parent) = system.process(Pid::from(process.parent().unwrap())) {
parent.kill();
}
for process_gc in system.processes_by_name("java") {
if process_gc.cmd().last().unwrap().contains(&"grasscutter") {
process_gc.kill();
}
}
window.emit("disable_grasscutter_watcher", &()).unwrap();
thread::sleep(std::time::Duration::from_secs(2));
// Start again
window.emit("start_grasscutter", &()).unwrap();
true
} else {
false
}
} else {
false
}
}
#[tauri::command]
fn enable_grasscutter_watcher(window: tauri::Window, process: String) {
let grasscutter_name = process.clone();
let mut gc_pid = Pid::from(696969);
*WATCH_GRASSCUTTER_PROCESS.lock().unwrap() = process;
window.listen("disable_grasscutter_watcher", |_e| {
*WATCH_GRASSCUTTER_PROCESS.lock().unwrap() = "".to_string();
});
println!("Starting grasscutter watcher...");
thread::spawn(move || {
// Initial sleep for 1 second while Grasscutter opens
std::thread::sleep(std::time::Duration::from_secs(3));
let mut system = System::new_all();
for process_gc in system.processes_by_name("java") {
if process_gc.cmd().last().unwrap().contains(&grasscutter_name) {
gc_pid = process_gc.pid();
*GC_PID.lock().unwrap() = gc_pid.into();
window
.emit("grasscutter_started", gc_pid.to_string())
.unwrap();
}
}
loop {
// Shorten loop timer to avoid user closing Cultivation before automatic stuff
thread::sleep(std::time::Duration::from_secs(2));
// Refresh system info
system.refresh_all();
// Grab the grasscutter process name
let proc = WATCH_GRASSCUTTER_PROCESS.lock().unwrap().to_string();
if !proc.is_empty() {
let mut exists = true;
if system.process(gc_pid).is_none() {
exists = false;
}
// If the grasscutter process closes.
if !exists {
println!("Grasscutter closed");
*WATCH_GRASSCUTTER_PROCESS.lock().unwrap() = "".to_string();
window.emit("grasscutter_closed", &()).unwrap();
break;
}
}
}
});
}
#[tauri::command]
async fn connect(port: u16, certificate_path: String) {
// Log message to console.
@@ -189,54 +353,3 @@ async fn get_theme_list(data_dir: String) -> Vec<HashMap<String, String>> {
themes
}
#[tauri::command]
// TODO: Replace with downloading the background file & saving it.
async fn get_bg_file(bg_path: String, appdata: String) -> String {
let copy_loc = appdata;
let query = web::query("https://api.grasscutter.io/cultivation/query").await;
let response_data: APIQuery = match serde_json::from_str(&query) {
Ok(data) => data,
Err(e) => {
println!("Failed to get bg file response: {}", e);
println!("^ please stop reporting this as an error it's so annoying LMFAO");
return "".to_string();
}
};
let file_name = response_data.bg_file.to_string();
// First we see if the file already exists in our local bg folder.
if file_helpers::dir_exists(format!("{}\\bg\\{}", copy_loc, file_name).as_str()) {
return format!("{}\\{}", copy_loc, response_data.bg_file.as_str());
}
// Now we check if the bg folder, which is one directory above the game_path, exists.
let bg_img_path = format!("{}\\{}", &bg_path, &file_name);
// If it doesn't, then we do not have backgrounds to grab.
if !file_helpers::dir_exists(&bg_path) {
return "".to_string();
}
// BG folder does exist, lets see if the image exists.
if !file_helpers::dir_exists(&bg_img_path) {
// Image doesn't exist
return "".to_string();
}
// The image exists, lets copy it to our local '\bg' folder.
let bg_img_path_local = format!("{}\\bg\\{}", copy_loc, file_name.as_str());
match std::fs::copy(bg_img_path, bg_img_path_local) {
Ok(_) => {
// Copy was successful, lets return true.
format!("{}\\{}", copy_loc, response_data.bg_file)
}
Err(e) => {
// Copy failed, lets return false
println!("Failed to copy background image: {}", e);
"".to_string()
}
}
}

View File

@@ -1,149 +0,0 @@
use regex::Regex;
use std::fs::File;
use std::fs::OpenOptions;
use std::io::Read;
use std::io::Write;
// For these two functions, a non-zero return value indicates failure.
extern "C" {
fn decrypt_global_metadata(data: *mut u8, size: usize) -> i32;
fn encrypt_global_metadata(data: *mut u8, size: usize) -> i32;
}
#[tauri::command]
pub fn patch_metadata(metadata_folder: &str) -> bool {
let metadata_file = &(metadata_folder.to_owned() + "\\global-metadata-unpatched.dat");
println!("Patching metadata file: {}", metadata_file);
let decrypted = decrypt_metadata(metadata_file);
if do_vecs_match(&decrypted, &Vec::new()) {
println!("Failed to decrypt metadata file.");
return false;
}
let modified = replace_keys(&decrypted);
if do_vecs_match(&modified, &Vec::new()) {
println!("Failed to replace keys in metadata file.");
return false;
}
let encrypted = encrypt_metadata(&modified);
if do_vecs_match(&encrypted, &Vec::new()) {
println!("Failed to re-encrypt metadata file.");
return false;
}
//write encrypted to file
let mut file = match OpenOptions::new()
.create(true)
.write(true)
.open(&(metadata_folder.to_owned() + "\\global-metadata-patched.dat"))
{
Ok(file) => file,
Err(e) => {
println!("Failed to open global-metadata: {}", e);
return false;
}
};
file.write_all(&encrypted).unwrap();
true
}
fn decrypt_metadata(file_path: &str) -> Vec<u8> {
let mut file = match File::open(file_path) {
Ok(file) => file,
Err(e) => {
println!("Failed to open global-metadata: {}", e);
return Vec::new();
}
};
let mut data = Vec::new();
// Read metadata file
match file.read_to_end(&mut data) {
Ok(_) => (),
Err(e) => {
println!("Failed to read global-metadata: {}", e);
return Vec::new();
}
}
// Decrypt metadata file
let success = unsafe { decrypt_global_metadata(data.as_mut_ptr(), data.len()) } == 0;
if success {
println!("Successfully decrypted global-metadata");
data
} else {
println!("Failed to decrypt global-metadata");
Vec::new()
}
}
fn replace_keys(data: &[u8]) -> Vec<u8> {
let mut new_data = String::new();
unsafe {
let data_str = String::from_utf8_unchecked(data.to_vec());
let re = Regex::new(r"<RSAKeyValue>((.|\n|\r)*?)</RSAKeyValue>").unwrap();
let matches = re.find_iter(&data_str);
// dispatch key is index 3
// password key is index 2
for (i, rmatch) in matches.enumerate() {
let key = rmatch.as_str();
if i == 2 {
println!("Replacing password key");
new_data = replace_rsa_key(&data_str, key, "passwordKey.txt");
} else if i == 3 {
println!("Replacing dispatch key");
new_data = replace_rsa_key(&new_data, key, "dispatchKey.txt");
}
}
}
return new_data.as_bytes().to_vec();
}
fn replace_rsa_key(old_data: &str, to_replace: &str, file_name: &str) -> String {
// Read dispatch key file
unsafe {
// Get key folder from exe path
let mut exe_path = std::env::current_exe().unwrap();
exe_path.pop();
let key_folder = exe_path.to_str().unwrap().to_string();
let mut new_key_file = match File::open(format!("{}/keys/{}", key_folder, file_name)) {
Ok(file) => file,
Err(e) => {
println!("Failed to open keys/{}: {}", file_name, e);
return String::new();
}
};
let mut key_data = Vec::new();
new_key_file.read_to_end(&mut key_data).unwrap();
let new_key = String::from_utf8_unchecked(key_data.to_vec());
// Replace old key with new key
old_data.replace(to_replace, &new_key)
}
}
fn encrypt_metadata(old_data: &[u8]) -> Vec<u8> {
let mut data = old_data.to_vec();
let success = unsafe { encrypt_global_metadata(data.as_mut_ptr(), data.len()) } == 0;
if success {
println!("Successfully encrypted global-metadata");
data
} else {
println!("Failed to encrypt global-metadata");
Vec::new()
}
}
fn do_vecs_match<T: PartialEq>(a: &Vec<T>, b: &Vec<T>) -> bool {
a == b
}

View File

@@ -3,13 +3,16 @@
* https://github.com/omjadas/hudsucker/blob/main/examples/log.rs
*/
#[cfg(target_os = "linux")]
use crate::system_helpers::run_command;
use once_cell::sync::Lazy;
use std::{str::FromStr, sync::Mutex};
use std::{path::PathBuf, str::FromStr, sync::Mutex};
use hudsucker::{
async_trait::async_trait,
certificate_authority::RcgenAuthority,
hyper::{Body, Request, Response},
hyper::{Body, Request, Response, StatusCode},
*,
};
use rcgen::*;
@@ -19,7 +22,7 @@ use std::net::SocketAddr;
use std::path::Path;
use rustls_pemfile as pemfile;
use tauri::http::Uri;
use tauri::{api::path::data_dir, http::Uri};
#[cfg(windows)]
use registry::{Data, Hive, Security};
@@ -45,21 +48,33 @@ pub fn set_proxy_addr(addr: String) {
impl HttpHandler for ProxyHandler {
async fn handle_request(
&mut self,
_context: &HttpContext,
mut request: Request<Body>,
_ctx: &HttpContext,
mut req: Request<Body>,
) -> RequestOrResponse {
let uri = request.uri().to_string();
let uri_path = request.uri().path();
let uri = req.uri().to_string();
if uri.contains("hoyoverse.com") || uri.contains("mihoyo.com") || uri.contains("yuanshen.com") {
// Create new URI.
let new_uri =
Uri::from_str(format!("{}{}", SERVER.lock().unwrap(), uri_path).as_str()).unwrap();
// Set request URI to the new one.
*request.uri_mut() = new_uri;
// Handle CONNECTs
if req.method().as_str() == "CONNECT" {
let builder = Response::builder()
.header("DecryptEndpoint", "Created")
.status(StatusCode::OK);
let res = builder.body(()).unwrap();
// Respond to CONNECT
*res.body()
} else {
let uri_path_and_query = req.uri().path_and_query().unwrap().as_str();
// Create new URI.
let new_uri =
Uri::from_str(format!("{}{}", SERVER.lock().unwrap(), uri_path_and_query).as_str())
.unwrap();
// Set request URI to the new one.
*req.uri_mut() = new_uri;
}
}
RequestOrResponse::Request(request)
req.into()
}
async fn handle_response(
@@ -69,17 +84,42 @@ impl HttpHandler for ProxyHandler {
) -> Response<Body> {
response
}
async fn should_intercept(&mut self, _ctx: &HttpContext, _req: &Request<Body>) -> bool {
true
}
}
/**
* Starts an HTTP(S) proxy server.
*/
pub async fn create_proxy(proxy_port: u16, certificate_path: String) {
let cert_path = PathBuf::from(certificate_path);
let pk_path = cert_path.join("private.key");
let ca_path = cert_path.join("cert.crt");
// Get the certificate and private key.
let mut private_key_bytes: &[u8] =
&fs::read(format!("{}\\private.key", certificate_path)).expect("Could not read private key");
let mut ca_cert_bytes: &[u8] =
&fs::read(format!("{}\\cert.crt", certificate_path)).expect("Could not read certificate");
let mut private_key_bytes: &[u8] = &match fs::read(&pk_path) {
// Try regenerating the CA stuff and read it again. If that doesn't work, quit.
Ok(b) => b,
Err(e) => {
println!("Encountered {}. Regenerating CA cert and retrying...", e);
generate_ca_files(&data_dir().unwrap().join("cultivation"));
fs::read(&pk_path).expect("Could not read private key")
}
};
let mut ca_cert_bytes: &[u8] = &match fs::read(&ca_path) {
// Try regenerating the CA stuff and read it again. If that doesn't work, quit.
Ok(b) => b,
Err(e) => {
println!("Encountered {}. Regenerating CA cert and retrying...", e);
generate_ca_files(&data_dir().unwrap().join("cultivation"));
fs::read(&ca_path).expect("Could not read certificate")
}
};
// Parse the private key and certificate.
let private_key = rustls::PrivateKey(
@@ -138,9 +178,29 @@ pub fn connect_to_proxy(proxy_port: u16) {
println!("Connected to the proxy.");
}
#[cfg(not(windows))]
#[cfg(unix)]
pub fn connect_to_proxy(proxy_port: u16) {
// Edit /etc/environment to set $http_proxy and $https_proxy
let mut env_file = match fs::read_to_string("/etc/environment") {
Ok(f) => f,
Err(e) => {
println!("Error opening /etc/environment: {}", e);
return;
}
};
// Append the proxy configuration.
// We will not remove the current proxy config if it exists, so we can just remove these last lines when we disconnect
env_file += format!("\nhttps_proxy=127.0.0.1:{}", proxy_port).as_str();
env_file += format!("\nhttp_proxy=127.0.0.1:{}", proxy_port).as_str();
// Save
fs::write("/etc/environment", env_file).unwrap();
}
#[cfg(target_od = "macos")]
pub fn connect_to_proxy(_proxy_port: u16) {
println!("Connecting to the proxy is not implemented on this platform.");
println!("No Mac support yet. Someone mail me a Macbook and I will do it B)")
}
/**
@@ -162,7 +222,26 @@ pub fn disconnect_from_proxy() {
println!("Disconnected from proxy.");
}
#[cfg(not(windows))]
#[cfg(target_os = "linux")]
pub fn disconnect_from_proxy() {
println!("Re-writing environment variables");
let regexp = regex::Regex::new(
// This has to be specific as possible or we risk fuckin up their environment LOL
r"(https|http)_proxy=.*127.0.0.1:.*",
)
.unwrap();
let environment = &fs::read_to_string("/etc/environment").expect("Failed to open environment");
let new_environment = regexp.replace_all(environment, "").to_string();
// Write new environment
fs::write("/etc/environment", new_environment.trim_end()).expect(
"Could not write environment, remove proxy declarations manually if they are still set",
);
}
#[cfg(target_os = "macos")]
pub fn disconnect_from_proxy() {}
/*
@@ -260,11 +339,27 @@ pub fn install_ca_files(cert_path: &Path) {
"/Library/Keychains/System.keychain",
cert_path.to_str().unwrap(),
],
None,
);
println!("Installed certificate.");
}
#[cfg(not(any(windows, target_os = "macos")))]
// If this is borked on non-debian platforms, so be it
#[cfg(target_os = "linux")]
pub fn install_ca_files(cert_path: &Path) {
let usr_certs = PathBuf::from("/usr/local/share/ca-certificates");
let usr_cert_path = usr_certs.join("cultivation.crt");
// Create dir if it doesn't exist
fs::create_dir_all(&usr_certs).expect("Unable to create local certificate directory");
fs::copy(cert_path, usr_cert_path).expect("Unable to copy cert to local certificate directory");
run_command("update-ca-certificates", vec![], None);
println!("Installed certificate.");
}
#[cfg(not(any(windows, target_os = "macos", target_os = "linux")))]
pub fn install_ca_files(_cert_path: &Path) {
println!("Certificate installation is not supported on this platform.");
}

View File

@@ -1,8 +0,0 @@
#![allow(non_snake_case)]
use serde::Deserialize;
#[derive(Deserialize)]
pub(crate) struct APIQuery {
pub bg_file: String,
}

View File

@@ -1,11 +1,17 @@
use duct::cmd;
use ini::Ini;
use std::ffi::OsStr;
use std::path::PathBuf;
use windows_service::service::{ServiceAccess, ServiceState::Stopped};
use windows_service::service_manager::{ServiceManager, ServiceManagerAccess};
#[cfg(windows)]
use registry::{Data, Hive, Security};
#[tauri::command]
pub fn run_program(path: String, args: Option<String>) {
// Without unwrap_or, this can crash when UAC prompt is denied
open::that(format!("{} {}", &path, &args.unwrap_or("".into()))).unwrap_or(());
open::that(format!("{} {}", &path, &args.unwrap_or_else(|| "".into()))).unwrap_or(());
}
#[tauri::command]
@@ -21,10 +27,10 @@ pub fn run_program_relative(path: String, args: Option<String>) {
std::env::set_current_dir(&path_buf).unwrap();
// Without unwrap_or, this can crash when UAC prompt is denied
open::that(format!("{} {}", &path, args.unwrap_or("".into()))).unwrap_or(());
open::that(format!("{} {}", &path, args.unwrap_or_else(|| "".into()))).unwrap_or(());
// Restore the original working directory
std::env::set_current_dir(&cwd).unwrap();
std::env::set_current_dir(cwd).unwrap();
}
#[tauri::command]
@@ -49,7 +55,7 @@ pub fn run_command(program: &str, args: Vec<&str>, relative: Option<bool>) {
cmd(prog, args).run().unwrap();
// Restore the original working directory
std::env::set_current_dir(&cwd).unwrap();
std::env::set_current_dir(cwd).unwrap();
});
}
@@ -91,8 +97,7 @@ pub fn install_location() -> String {
}
#[tauri::command]
pub fn set_migoto_target(path: String, migoto_path: String) -> bool {
let pathbuf = PathBuf::from(path);
pub fn set_migoto_target(migoto_path: String) -> bool {
let mut migoto_pathbuf = PathBuf::from(migoto_path);
migoto_pathbuf.pop();
@@ -112,7 +117,7 @@ pub fn set_migoto_target(path: String, migoto_path: String) -> bool {
// Set options
conf
.with_section(Some("Loader"))
.set("target", pathbuf.to_str().unwrap());
.set("target", "GenshinImpact.exe");
// Write file
match conf.write_to_file(&migoto_pathbuf) {
@@ -127,6 +132,99 @@ pub fn set_migoto_target(path: String, migoto_path: String) -> bool {
}
}
#[cfg(windows)]
#[tauri::command]
pub fn wipe_registry(exec_name: String) {
// Fetch the 'Internet Settings' registry key.
let settings =
match Hive::CurrentUser.open(format!("Software\\miHoYo\\{}", exec_name), Security::Write) {
Ok(s) => s,
Err(e) => {
println!("Error getting registry setting: {}", e);
return;
}
};
// Wipe login cache
match settings.set_value(
"MIHOYOSDK_ADL_PROD_OVERSEA_h1158948810",
&Data::String("".parse().unwrap()),
) {
Ok(_) => (),
Err(e) => println!("Error wiping registry: {}", e),
}
}
#[cfg(windows)]
#[tauri::command]
pub fn service_status(service: String) -> bool {
let manager = match ServiceManager::local_computer(None::<&str>, ServiceManagerAccess::CONNECT) {
Ok(manager) => manager,
Err(_e) => return false,
};
let my_service = match manager.open_service(service.clone(), ServiceAccess::QUERY_STATUS) {
Ok(my_service) => my_service,
Err(_e) => {
println!("{} service not found! Not installed?", service);
return false;
}
};
let status_result = my_service.query_status();
if status_result.is_ok() {
let status = status_result.unwrap();
println!("{} service status: {:?}", service, status.current_state);
if status.current_state == Stopped {
// Start the service if it is stopped
start_service(service);
}
true
} else {
false
}
}
#[cfg(windows)]
#[tauri::command]
pub fn start_service(service: String) -> bool {
println!("Starting service: {}", service);
let manager = match ServiceManager::local_computer(None::<&str>, ServiceManagerAccess::CONNECT) {
Ok(manager) => manager,
Err(_e) => return false,
};
let my_service = match manager.open_service(service, ServiceAccess::START) {
Ok(my_service) => my_service,
Err(_e) => return false,
};
match my_service.start(&[OsStr::new("Started service!")]) {
Ok(_s) => true,
Err(_e) => return false,
};
true
}
#[cfg(windows)]
#[tauri::command]
pub fn stop_service(service: String) -> bool {
println!("Stopping service: {}", service);
let manager = match ServiceManager::local_computer(None::<&str>, ServiceManagerAccess::CONNECT) {
Ok(manager) => manager,
Err(_e) => return false,
};
let my_service = match manager.open_service(service, ServiceAccess::STOP) {
Ok(my_service) => my_service,
Err(_e) => return false,
};
match my_service.stop() {
Ok(_s) => true,
Err(_e) => return false,
};
true
}
#[cfg(unix)]
#[tauri::command]
pub fn wipe_registry(_exec_name: String) {}
#[cfg(windows)]
#[tauri::command]
pub fn is_elevated() -> bool {
@@ -138,3 +236,8 @@ pub fn is_elevated() -> bool {
pub fn is_elevated() -> bool {
sudo::check() == sudo::RunningAs::Root
}
#[tauri::command]
pub fn get_platform() -> &'static str {
std::env::consts::OS
}

View File

@@ -58,7 +58,7 @@ pub fn unzip(
}
};
full_path = new_path.clone();
full_path = new_path;
}
println!("Is rar file? {}", zipfile.ends_with(".rar"));
@@ -78,7 +78,7 @@ pub fn unzip(
// Get the name of the inenr file in the zip file
let mut zip = zip::ZipArchive::new(&f).unwrap();
let file = zip.by_index(0).unwrap();
name = file.name().to_string().clone();
name = file.name().to_string();
}
if !success {
@@ -96,38 +96,42 @@ pub fn unzip(
.unwrap();
}
// If downloading full build, emit that the jar was extracted with it
if zipfile.contains("GrasscutterCulti") {
window
.emit("jar_extracted", destpath.to_string() + "grasscutter.jar")
.unwrap();
}
// Delete zip file
match std::fs::remove_file(&zipfile) {
Ok(_) => {
// Get any new directory that could have been created
let mut new_dir: String = String::new();
for entry in read_dir(&write_path).unwrap() {
let entry = entry.unwrap();
let entry_path = entry.path();
if entry_path.is_dir() && !dirs.contains(&entry_path) {
new_dir = entry_path.to_str().unwrap().to_string();
}
}
let mut res_hash = std::collections::HashMap::new();
res_hash.insert("file", zipfile.to_string());
res_hash.insert("new_folder", new_dir);
window.emit("extract_end", &res_hash).unwrap();
println!("Deleted zip file: {}", zipfile);
}
Err(e) => {
println!("Failed to delete zip file: {}", e);
}
};
// Get any new directory that could have been created
let mut new_dir: String = String::new();
for entry in read_dir(&write_path).unwrap() {
let entry = entry.unwrap();
let entry_path = entry.path();
if entry_path.is_dir() {
if !dirs.contains(&entry_path) {
new_dir = entry_path.to_str().unwrap().to_string();
}
}
}
let mut res_hash = std::collections::HashMap::new();
res_hash.insert("file", zipfile.to_string());
res_hash.insert("new_folder", new_dir.to_string());
window.emit("extract_end", &res_hash).unwrap();
});
}
fn extract_rar(rarfile: &String, _f: &File, full_path: &path::PathBuf, _top_level: bool) -> bool {
let archive = Archive::new(rarfile.clone());
fn extract_rar(rarfile: &str, _f: &File, full_path: &path::Path, _top_level: bool) -> bool {
let archive = Archive::new(rarfile.to_string());
let mut open_archive = archive
.extract_to(full_path.to_str().unwrap().to_string())
@@ -149,7 +153,7 @@ fn extract_rar(rarfile: &String, _f: &File, full_path: &path::PathBuf, _top_leve
}
}
fn extract_zip(_zipfile: &String, f: &File, full_path: &path::PathBuf, top_level: bool) -> bool {
fn extract_zip(_zipfile: &str, f: &File, full_path: &path::Path, top_level: bool) -> bool {
match zip_extract::extract(f, full_path, top_level) {
Ok(_) => {
println!(

View File

@@ -9,8 +9,13 @@ pub(crate) async fn query(site: &str) -> String {
.header(CONTENT_TYPE, "application/json")
.send()
.await
.unwrap();
response.text().await.unwrap()
.ok();
if response.is_some() {
response.unwrap().text().await.unwrap()
} else {
false.to_string()
}
}
#[tauri::command]
@@ -23,9 +28,13 @@ pub(crate) async fn valid_url(url: String) -> bool {
.header(USER_AGENT, "cultivation")
.send()
.await
.unwrap();
.ok();
response.status().as_str() == "200"
if response.is_some() {
return response.unwrap().status().as_str() == "200";
} else {
false
}
}
#[tauri::command]

View File

@@ -7,7 +7,7 @@
},
"package": {
"productName": "Cultivation",
"version": "1.0.7"
"version": "1.0.23"
},
"tauri": {
"allowlist": {
@@ -40,7 +40,7 @@
"providerShortName": null,
"signingIdentity": null
},
"resources": ["lang/*.json", "keys/*"],
"resources": ["lang/*.json", "keys/*", "patch/*"],
"targets": "all",
"windows": {
"allowDowngrades": false,

Binary file not shown.

After

Width:  |  Height:  |  Size: 642 KiB

View File

@@ -5,10 +5,12 @@ import DownloadHandler from '../utils/download'
import { getConfigOption } from '../utils/configuration'
import { getTheme, loadTheme } from '../utils/themes'
import { convertFileSrc, invoke } from '@tauri-apps/api/tauri'
import { dataDir } from '@tauri-apps/api/path'
import { Main } from './Main'
import { Mods } from './Mods'
// From https://www.pixiv.net/en/artworks/93995273
import FALLBACK_BG from '../resources/icons/FALLBACK_BG.jpg'
interface IState {
page: string
bgFile: string
@@ -28,10 +30,6 @@ class App extends React.Component<Readonly<unknown>, IState> {
}
async componentDidMount() {
const game_exe = await getConfigOption('game_install_path')
const game_path = game_exe?.substring(0, game_exe.replace(/\\/g, '/').lastIndexOf('/')) || ''
const root_path = game_path?.substring(0, game_path.replace(/\\/g, '/').lastIndexOf('/')) || ''
// Load a theme if it exists
const theme = await getConfigOption('theme')
if (theme && theme !== 'default') {
@@ -42,23 +40,7 @@ class App extends React.Component<Readonly<unknown>, IState> {
// Get custom bg AFTER theme is loaded !! important !!
const custom_bg = await getConfigOption('customBackground')
if (!custom_bg || !/png|jpg|jpeg$/.test(custom_bg)) {
if (game_path) {
// Get the bg by invoking, then set the background to that bg.
const bgLoc: string = await invoke('get_bg_file', {
bgPath: root_path,
appdata: await dataDir(),
})
bgLoc &&
this.setState(
{
bgFile: bgLoc,
},
this.forceUpdate
)
}
} else {
if (custom_bg) {
const isUrl = /^http(s)?:\/\//gm.test(custom_bg)
if (!isUrl) {
@@ -85,6 +67,18 @@ class App extends React.Component<Readonly<unknown>, IState> {
this.forceUpdate
)
}
} else {
// Check if api bg is accessible
const isDefaultValid = await invoke('valid_url', {
url: DEFAULT_BG,
})
this.setState(
{
bgFile: isDefaultValid ? DEFAULT_BG : FALLBACK_BG,
},
this.forceUpdate
)
}
window.addEventListener('changePage', (e) => {

View File

@@ -17,7 +17,7 @@ import { invoke } from '@tauri-apps/api'
import { listen } from '@tauri-apps/api/event'
import { dataDir } from '@tauri-apps/api/path'
import { appWindow } from '@tauri-apps/api/window'
import { unpatchGame } from '../utils/metadata'
import { unpatchGame } from '../utils/rsa'
import DownloadHandler from '../utils/download'
// Graphics
@@ -65,21 +65,28 @@ export class Main extends React.Component<IProps, IState> {
setConfigOption('grasscutter_path', payload)
})
// Emitted for metadata replacing-purposes
// Emitted for rsa replacing-purposes
listen('game_closed', async () => {
const wasPatched = await getConfigOption('patch_metadata')
const wasPatched = await getConfigOption('patch_rsa')
if (wasPatched) {
const unpatched = await unpatchGame()
if (!unpatched) {
alert(
`Could not unpatch game! (You should be able to find your metadata backup in ${await dataDir()}\\cultivation\\)`
)
if (unpatched) {
alert(`Could not unpatch game! (Delete version.dll in your game folder)`)
}
}
})
// Emitted for automatic processes
listen('grasscutter_closed', async () => {
const autoService = await getConfigOption('auto_mongodb')
if (autoService) {
await invoke('stop_service', { service: 'MongoDB' })
}
})
let min = false
// periodically check if we need to min/max based on whether the game is open
@@ -114,6 +121,10 @@ export class Main extends React.Component<IProps, IState> {
await setConfigOption('cert_generated', true)
}
// Ensure old configs are updated to use RSA
const updatedConfig = await getConfigOption('patch_rsa')
await setConfigOption('patch_rsa', updatedConfig)
// Period check to only show progress bar when downloading files
setInterval(() => {
this.setState({

View File

@@ -12,8 +12,9 @@ import Plus from '../../resources/icons/plus.svg'
import './ServerLaunchSection.css'
import { dataDir } from '@tauri-apps/api/path'
import { getGameExecutable } from '../../utils/game'
import { patchGame, unpatchGame } from '../../utils/metadata'
import { getGameExecutable, getGameVersion, getGrasscutterJar } from '../../utils/game'
import { patchGame, unpatchGame } from '../../utils/rsa'
import { listen } from '@tauri-apps/api/event'
interface IProps {
openExtras: (playGame: () => void) => void
@@ -25,6 +26,7 @@ interface IState {
checkboxLabel: string
ip: string
port: string
launchServer: (proc_name?: string) => void
ipPlaceholder: string
portPlaceholder: string
@@ -54,6 +56,9 @@ export default class ServerLaunchSection extends React.Component<IProps, IState>
portHelpText: '',
httpsLabel: '',
httpsEnabled: false,
launchServer: () => {
alert('Error launching grasscutter')
},
swag: false,
akebiSet: false,
migotoSet: false,
@@ -64,6 +69,11 @@ export default class ServerLaunchSection extends React.Component<IProps, IState>
this.setIp = this.setIp.bind(this)
this.setPort = this.setPort.bind(this)
this.toggleHttps = this.toggleHttps.bind(this)
this.launchServer = this.launchServer.bind(this)
listen('start_grasscutter', async () => {
this.launchServer()
})
}
async componentDidMount() {
@@ -109,11 +119,31 @@ export default class ServerLaunchSection extends React.Component<IProps, IState>
// Connect to proxy
if (config.toggle_grasscutter) {
if (config.patch_metadata) {
if (config.patch_rsa) {
const gameVersion = await getGameVersion()
console.log(gameVersion)
if (gameVersion == null) {
alert(
'Game version could not be determined. Please make sure you have the game correctly selected and try again.'
)
return
}
if (gameVersion?.major == 2 && gameVersion?.minor < 9) {
alert('Game version is too old for RSA patching. Please disable RSA patching in the settings and try again.')
return
}
if (gameVersion?.major == 3 && gameVersion?.minor < 1) {
alert('Game version is too old for RSA patching. Please disable RSA patching in the settings and try again.')
return
}
const patched = await patchGame()
if (!patched) {
alert('Could not patch game!')
alert('Could not patch! Try launching again, or patching manually.')
return
}
}
@@ -139,26 +169,18 @@ export default class ServerLaunchSection extends React.Component<IProps, IState>
// Open server as well if the options are set
if (config.grasscutter_with_game) {
const jarFolderArr = config.grasscutter_path.replace(/\\/g, '/').split('/')
jarFolderArr.pop()
const jarFolder = jarFolderArr.join('/')
await invoke('run_jar', {
path: config.grasscutter_path,
executeIn: jarFolder,
javaPath: config.java_path || '',
})
this.launchServer()
}
} else {
const unpatched = await unpatchGame()
await unpatchGame()
}
if (!unpatched) {
alert(
`Could not unpatch game, aborting launch! (You can find your metadata backup in ${await dataDir()}\\cultivation\\)`
)
return
}
if (config.wipe_login) {
// First wipe registry if we have to
await invoke('wipe_registry', {
// The exe is always PascalCase so we can get the dir using regex
execName: (await getGameExecutable())?.split('.exe')[0].replace(/([a-z\d])([A-Z])/g, '$1 $2'),
})
}
// Launch the program
@@ -170,11 +192,24 @@ export default class ServerLaunchSection extends React.Component<IProps, IState>
else alert('Game not found! At: ' + (exe || config.game_install_path))
}
async launchServer() {
async launchServer(proc_name?: string) {
if (await invoke('is_grasscutter_running')) {
alert('Grasscutter already running!')
return
}
const config = await getConfig()
if (!config.grasscutter_path) return alert('Grasscutter not installed or set!')
if (config.auto_mongodb) {
const grasscutter_jar = await getGrasscutterJar()
await invoke('enable_grasscutter_watcher', {
process: proc_name || grasscutter_jar,
})
// Check if MongoDB is running and start it if not
await invoke('service_status', { service: 'MongoDB' })
}
let jarFolder = config.grasscutter_path
if (jarFolder.includes('/')) {

View File

@@ -17,6 +17,16 @@
height: 100%;
}
.HeaderText {
font-size: 18px;
display: flex;
align-items: center;
justify-content: center;
height: 100%;
margin-bottom: 10px;
text-decoration-line: underline;
}
.DownloadValue .BigButton {
height: 100%;
min-height: 30px;

View File

@@ -13,11 +13,12 @@ import { invoke } from '@tauri-apps/api'
import { listen } from '@tauri-apps/api/event'
import HelpButton from '../common/HelpButton'
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 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 STABLE_DOWNLOAD = 'https://nightly.link/Grasscutters/Grasscutter/workflows/build/stable/Grasscutter.zip'
const DEV_DOWNLOAD = 'https://nightly.link/Grasscutters/Grasscutter/workflows/build/development/Grasscutter.zip'
const RESOURCES_DOWNLOAD = 'https://gitlab.com/yukiz/GrasscutterResources/-/archive/3.0/GrasscutterResources-3.0.zip'
const RESOURCES_DOWNLOAD = 'https://gitlab.com/YuukiPS/GC-Resources/-/archive/3.5/GC-Resources-3.5.zip' // Use Yuuki res as grasscutter crepe res are broken
interface IProps {
closeFn: () => void
@@ -25,6 +26,7 @@ interface IProps {
}
interface IState {
fullbuild_downloading: boolean
grasscutter_downloading: boolean
resources_downloading: boolean
repo_downloading: boolean
@@ -37,6 +39,7 @@ export default class Downloads extends React.Component<IProps, IState> {
super(props)
this.state = {
fullbuild_downloading: this.props.downloadManager.downloadingFullBuild(),
grasscutter_downloading: this.props.downloadManager.downloadingJar(),
resources_downloading: this.props.downloadManager.downloadingResources(),
repo_downloading: this.props.downloadManager.downloadingRepo(),
@@ -45,6 +48,7 @@ export default class Downloads extends React.Component<IProps, IState> {
}
this.getGrasscutterFolder = this.getGrasscutterFolder.bind(this)
this.downloadGrasscutterFullBuild = this.downloadGrasscutterFullBuild.bind(this)
this.downloadGrasscutterStableRepo = this.downloadGrasscutterStableRepo.bind(this)
this.downloadGrasscutterDevRepo = this.downloadGrasscutterDevRepo.bind(this)
this.downloadGrasscutterStable = this.downloadGrasscutterStable.bind(this)
@@ -109,6 +113,16 @@ export default class Downloads extends React.Component<IProps, IState> {
return folderPath
}
async downloadGrasscutterFullBuild() {
const folder = await this.getGrasscutterFolder()
this.props.downloadManager.addDownload(FULL_BUILD_DOWNLOAD, folder + '\\GrasscutterCulti.zip', async () => {
await unzip(folder + '\\GrasscutterCulti.zip', folder + '\\', true)
this.toggleButtons()
})
this.toggleButtons()
}
async downloadGrasscutterStableRepo() {
const folder = await this.getGrasscutterFolder()
this.props.downloadManager.addDownload(STABLE_REPO_DOWNLOAD, folder + '\\grasscutter_repo.zip', async () => {
@@ -158,6 +172,11 @@ export default class Downloads extends React.Component<IProps, IState> {
async downloadResources() {
const folder = await this.getGrasscutterFolder()
this.props.downloadManager.addDownload(RESOURCES_DOWNLOAD, folder + '\\resources.zip', async () => {
// Tell the user this takes some time
alert(
'Extracting resources can take time! If your resources appear to be "stuck" extracting for less than 15-20 mins, they likely still are extracting.'
)
// Delete the existing folder if it exists
if (
await invoke('dir_exists', {
@@ -187,6 +206,7 @@ export default class Downloads extends React.Component<IProps, IState> {
// Set states since we know we are downloading something if this is called
this.setState({
fullbuild_downloading: this.props.downloadManager.downloadingFullBuild(),
grasscutter_downloading: this.props.downloadManager.downloadingJar(),
resources_downloading: this.props.downloadManager.downloadingResources(),
repo_downloading: this.props.downloadManager.downloadingRepo(),
@@ -197,7 +217,30 @@ export default class Downloads extends React.Component<IProps, IState> {
render() {
return (
<Menu closeFn={this.props.closeFn} className="Downloads" heading="Downloads">
<div className="DownloadMenuSection" id="downloadMenuContainerGCStable">
<Divider />
<div className="HeaderText" id="downloadMenuAIOHeader">
<Tr text="downloads.aio_header" />
</div>
<div className="DownloadMenuSection" id="downloadMenuContainerGCFullBuild">
<div className="DownloadLabel" id="downloadMenuLabelGCFullBuild">
<Tr text={'downloads.grasscutter_fullbuild'} />
<HelpButton contents="help.gc_fullbuild" />
</div>
<div className="DownloadValue" id="downloadMenuButtonGCFullBuild">
<BigButton
disabled={this.state.grasscutter_downloading}
onClick={this.downloadGrasscutterFullBuild}
id="grasscutterFullBuildBtn"
>
<Tr text="components.download" />
</BigButton>
</div>
</div>
<Divider />
{/* <div className="DownloadMenuSection" id="downloadMenuContainerGCStable">
<div className="DownloadLabel" id="downloadMenuLabelGCStable">
<Tr
text={this.state.grasscutter_set ? 'downloads.grasscutter_stable' : 'downloads.grasscutter_stable_update'}
@@ -213,6 +256,9 @@ export default class Downloads extends React.Component<IProps, IState> {
<Tr text="components.download" />
</BigButton>
</div>
</div> */}
<div className="HeaderText" id="downloadMenuIndividualHeader">
<Tr text="downloads.individual_header" />
</div>
<div className="DownloadMenuSection" id="downloadMenuContainerGCDev">
<div className="DownloadLabel" id="downloadMenuLabelGCDev">
@@ -232,9 +278,7 @@ export default class Downloads extends React.Component<IProps, IState> {
</div>
</div>
<Divider />
<div className="DownloadMenuSection" id="downloadMenuContainerGCStableData">
{/* <div className="DownloadMenuSection" id="downloadMenuContainerGCStableData">
<div className="DownloadLabel" id="downloadMenuLabelGCStableData">
<Tr
text={
@@ -254,7 +298,7 @@ export default class Downloads extends React.Component<IProps, IState> {
<Tr text="components.download" />
</BigButton>
</div>
</div>
</div> */}
<div className="DownloadMenuSection" id="downloadMenuContainerGCDevData">
<div className="DownloadLabel" id="downloadMenuLabelGCDevData">
<Tr
@@ -277,8 +321,6 @@ export default class Downloads extends React.Component<IProps, IState> {
</div>
</div>
<Divider />
<div className="DownloadMenuSection" id="downloadMenuContainerResources">
<div className="DownloadLabel" id="downloadMenuLabelResources">
<Tr text="downloads.resources" />

View File

@@ -4,7 +4,7 @@ import { dataDir } from '@tauri-apps/api/path'
import DirInput from '../common/DirInput'
import Menu from './Menu'
import Tr, { getLanguages, translate } from '../../../utils/language'
import { setConfigOption, getConfig, getConfigOption } from '../../../utils/configuration'
import { setConfigOption, getConfig, getConfigOption, Configuration } from '../../../utils/configuration'
import Checkbox from '../common/Checkbox'
import Divider from './Divider'
import { getThemeList } from '../../../utils/themes'
@@ -13,8 +13,9 @@ import * as server from '../../../utils/server'
import './Options.css'
import BigButton from '../common/BigButton'
import DownloadHandler from '../../../utils/download'
import * as meta from '../../../utils/metadata'
import * as meta from '../../../utils/rsa'
import HelpButton from '../common/HelpButton'
import TextInput from '../common/TextInput'
interface IProps {
closeFn: () => void
@@ -32,9 +33,13 @@ interface IState {
themes: string[]
theme: string
encryption: boolean
patch_metadata: boolean
patch_rsa: boolean
use_internal_proxy: boolean
wipe_login: boolean
horny_mode: boolean
auto_mongodb: boolean
swag: boolean
platform: string
// Swag stuff
akebi_path: string
@@ -57,9 +62,13 @@ export default class Options extends React.Component<IProps, IState> {
themes: ['default'],
theme: '',
encryption: false,
patch_metadata: false,
patch_rsa: false,
use_internal_proxy: false,
wipe_login: false,
horny_mode: false,
swag: false,
auto_mongodb: false,
platform: '',
// Swag stuff
akebi_path: '',
@@ -75,20 +84,21 @@ export default class Options extends React.Component<IProps, IState> {
this.toggleGrasscutterWithGame = this.toggleGrasscutterWithGame.bind(this)
this.setCustomBackground = this.setCustomBackground.bind(this)
this.toggleEncryption = this.toggleEncryption.bind(this)
this.restoreMetadata = this.restoreMetadata.bind(this)
this.toggleMetadata = this.toggleMetadata.bind(this)
this.toggleProxy = this.toggleProxy.bind(this)
this.removeRSA = this.removeRSA.bind(this)
}
async componentDidMount() {
const config = await getConfig()
const languages = await getLanguages()
const platform: string = await invoke('get_platform')
// Remove jar from path
const path = config.grasscutter_path.replace(/\\/g, '/')
const folderPath = path.substring(0, path.lastIndexOf('/'))
const encEnabled = await server.encryptionEnabled(folderPath + '/config.json')
console.log(platform)
this.setState({
game_install_path: config.game_install_path || '',
grasscutter_path: config.grasscutter_path || '',
@@ -100,9 +110,13 @@ 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'),
patch_metadata: config.patch_metadata || false,
patch_rsa: config.patch_rsa || false,
use_internal_proxy: config.use_internal_proxy || false,
wipe_login: config.wipe_login || false,
horny_mode: config.horny_mode || false,
swag: config.swag_mode || false,
auto_mongodb: config.auto_mongodb || false,
platform,
// Swag stuff
akebi_path: config.akebi_path || '',
@@ -116,17 +130,40 @@ export default class Options extends React.Component<IProps, IState> {
setGameExecutable(value: string) {
setConfigOption('game_install_path', value)
// I hope this stops people setting launcher.exe because oml it's annoying
if (value.endsWith('launcher.exe')) {
const pathArr = value.replace(/\\/g, '/').split('/')
pathArr.pop()
const path = pathArr.join('/') + '/Genshin Impact Game/'
alert(
`You have set your game execuatable to "launcher.exe". You should not do this. Your game executable is located in:\n\n${path}`
)
}
this.setState({
game_install_path: value,
})
}
setGrasscutterJar(value: string) {
async setGrasscutterJar(value: string) {
setConfigOption('grasscutter_path', value)
this.setState({
grasscutter_path: value,
})
const config = await getConfig()
const path = config.grasscutter_path.replace(/\\/g, '/')
const folderPath = path.substring(0, path.lastIndexOf('/'))
const encEnabled = await server.encryptionEnabled(folderPath + '/config.json')
// Update encryption button when setting new jar
this.setState({
encryption: await translate(encEnabled ? 'options.enabled' : 'options.disabled'),
})
window.location.reload()
}
setJavaPath(value: string) {
@@ -154,7 +191,6 @@ export default class Options extends React.Component<IProps, IState> {
// Set game exe in Migoto ini
invoke('set_migoto_target', {
path: this.state.game_install_path,
migotoPath: value,
})
}
@@ -230,10 +266,16 @@ export default class Options extends React.Component<IProps, IState> {
(await server.encryptionEnabled(folderPath + '/config.json')) ? 'options.enabled' : 'options.disabled'
),
})
// Check if Grasscutter is running, and restart if so to apply changes
if (await invoke('is_grasscutter_running')) {
alert('Automatically restarting Grasscutter to apply encryption changes!')
await invoke('restart_grasscutter')
}
}
async restoreMetadata() {
await meta.restoreMetadata(this.props.downloadManager)
async removeRSA() {
await meta.unpatchGame()
}
async installCert() {
@@ -242,55 +284,57 @@ export default class Options extends React.Component<IProps, IState> {
})
}
async toggleMetadata() {
const changedVal = !(await getConfigOption('patch_metadata'))
async toggleOption(opt: keyof Configuration) {
const changedVal = !(await getConfigOption(opt))
await setConfigOption('patch_metadata', changedVal)
await setConfigOption(opt, changedVal)
// @ts-expect-error shut up bitch
this.setState({
patch_metadata: changedVal,
})
}
async toggleProxy() {
const changedVal = !(await getConfigOption('use_internal_proxy'))
await setConfigOption('use_internal_proxy', changedVal)
this.setState({
use_internal_proxy: changedVal,
[opt]: changedVal,
})
}
render() {
return (
<Menu closeFn={this.props.closeFn} className="Options" heading="Options">
<div className="OptionSection" id="menuOptionsContainerGamePath">
<div className="OptionLabel" id="menuOptionsLabelGamePath">
<Tr text="options.game_path" />
{!this.state.platform || this.state.platform === 'windows' ? (
<div className="OptionSection" id="menuOptionsContainerGamePath">
<div className="OptionLabel" id="menuOptionsLabelGamePath">
<Tr text="options.game_path" />
</div>
<div className="OptionValue" id="menuOptionsDirGamePath">
<DirInput onChange={this.setGameExecutable} value={this.state?.game_install_path} extensions={['exe']} />
</div>
</div>
<div className="OptionValue" id="menuOptionsDirGamePath">
<DirInput onChange={this.setGameExecutable} value={this.state?.game_install_path} extensions={['exe']} />
) : (
<div className="OptionSection" id="menuOptionsContainerGameCommand">
<div className="OptionLabel" id="menuOptionsLabelGameCommand">
<Tr text="options.game_command" />
</div>
<div className="OptionValue" id="menuOptionsGameCommand">
<TextInput />
</div>
</div>
</div>
)}
<div className="OptionSection" id="menuOptionsContainermetaDownload">
<div className="OptionLabel" id="menuOptionsLabelmetaDownload">
<Tr text="options.recover_metadata" />
<HelpButton contents="help.emergency_metadata" />
<Tr text="options.recover_rsa" />
<HelpButton contents="help.emergency_rsa" />
</div>
<div className="OptionValue" id="menuOptionsButtonmetaDownload">
<BigButton onClick={this.restoreMetadata} id="metaDownload">
<Tr text="components.download" />
<BigButton onClick={this.removeRSA} id="metaDownload">
<Tr text="components.delete" />
</BigButton>
</div>
</div>
<div className="OptionSection" id="menuOptionsContainerPatchMeta">
<div className="OptionLabel" id="menuOptionsLabelPatchMeta">
<Tr text="options.patch_metadata" />
<HelpButton contents="help.patch_metadata" />
<Tr text="options.patch_rsa" />
<HelpButton contents="help.patch_rsa" />
</div>
<div className="OptionValue" id="menuOptionsCheckboxPatchMeta">
<Checkbox onChange={this.toggleMetadata} checked={this.state?.patch_metadata} id="patchMeta" />
<Checkbox onChange={() => this.toggleOption('patch_rsa')} checked={this.state?.patch_rsa} id="patchMeta" />
</div>
</div>
<div className="OptionSection" id="menuOptionsContainerUseProxy">
@@ -299,7 +343,35 @@ export default class Options extends React.Component<IProps, IState> {
<HelpButton contents="help.use_proxy" />
</div>
<div className="OptionValue" id="menuOptionsCheckboxUseProxy">
<Checkbox onChange={this.toggleProxy} checked={this.state?.use_internal_proxy} id="useProxy" />
<Checkbox
onChange={() => this.toggleOption('use_internal_proxy')}
checked={this.state?.use_internal_proxy}
id="useProxy"
/>
</div>
</div>
<div className="OptionSection" id="menuOptionsContainerWipeLogin">
<div className="OptionLabel" id="menuOptionsLabelWipeLogin">
<Tr text="options.wipe_login" />
</div>
<div className="OptionValue" id="menuOptionsCheckboxWipeLogin">
<Checkbox
onChange={() => this.toggleOption('wipe_login')}
checked={this.state?.wipe_login}
id="wipeLogin"
/>
</div>
</div>
<div className="OptionSection" id="menuOptionsContainerAutoMongodb">
<div className="OptionLabel" id="menuOptionsLabelAutoMongodb">
<Tr text="options.auto_mongodb" />
</div>
<div className="OptionValue" id="menuOptionsCheckboxAutoMongodb">
<Checkbox
onChange={() => this.toggleOption('auto_mongodb')}
checked={this.state?.auto_mongodb}
id="autoMongodb"
/>
</div>
</div>
@@ -372,12 +444,26 @@ export default class Options extends React.Component<IProps, IState> {
</div>
<div className="OptionValue" id="menuOptionsCheckboxGCWGame">
<Checkbox
onChange={this.toggleGrasscutterWithGame}
onChange={() => this.toggleOption('grasscutter_with_game')}
checked={this.state?.grasscutter_with_game}
id="gcWithGame"
/>
</div>
</div>
{this.state.swag ? (
<div className="OptionSection" id="menuOptionsContainerHorny">
<div className="OptionLabel" id="menuOptionsLabelHorny">
<Tr text="options.horny_mode" />
</div>
<div className="OptionValue" id="menuOptionsCheckboxHorny">
<Checkbox
onChange={() => this.toggleOption('horny_mode')}
checked={this.state?.horny_mode}
id="hornyMode"
/>
</div>
</div>
) : null}
<Divider />

View File

@@ -1,4 +1,5 @@
import React from 'react'
import { getConfigOption } from '../../../utils/configuration'
import { getInstalledMods, getMods, ModData, PartialModData } from '../../../utils/gamebanana'
import { LoadingCircle } from './LoadingCircle'
@@ -11,6 +12,7 @@ interface IProps {
}
interface IState {
horny: boolean
modList: ModData[] | null
installedList:
| {
@@ -25,6 +27,7 @@ export class ModList extends React.Component<IProps, IState> {
super(props)
this.state = {
horny: false,
modList: null,
installedList: null,
}
@@ -60,8 +63,10 @@ export class ModList extends React.Component<IProps, IState> {
}
const mods = await getMods(this.props.mode)
const horny = await getConfigOption('horny_mode')
this.setState({
horny,
modList: mods,
})
}
@@ -78,10 +83,16 @@ export class ModList extends React.Component<IProps, IState> {
<div className="ModListInner">
{this.props.mode === 'installed'
? this.state.installedList?.map((mod) => (
<ModTile path={mod.path} mod={mod.info} key={mod.info.name} onClick={this.downloadMod} />
<ModTile
horny={this.state.horny}
path={mod.path}
mod={mod.info}
key={mod.info.name}
onClick={this.downloadMod}
/>
))
: this.state.modList?.map((mod: ModData) => (
<ModTile mod={mod} key={mod.id} onClick={this.downloadMod} />
<ModTile horny={this.state.horny} mod={mod} key={mod.id} onClick={this.downloadMod} />
))}
</div>
) : (

View File

@@ -12,6 +12,7 @@ import { disableMod, enableMod, modIsEnabled } from '../../../utils/mods'
interface IProps {
mod: ModData | PartialModData
horny?: boolean
path?: string
onClick: (mod: ModData) => void
}
@@ -107,7 +108,9 @@ export class ModTile extends React.Component<IProps, IState> {
))}
<img
src={mod.images[0]}
className={`ModImageInner ${'id' in mod && mod.nsfw ? 'nsfw' : ''} ${this.state.hover ? 'blur' : ''}`}
className={`ModImageInner ${'id' in mod && !this.props.horny && mod.nsfw ? 'nsfw' : ''} ${
this.state.hover ? 'blur' : ''
}`}
/>
</div>
<div className="ModInner">

View File

@@ -50,6 +50,7 @@ export default class NewsSection extends React.Component<IProps, IState> {
componentDidMount() {
// Call showNews off the bat
this.showNews()
this.setSelected('commits')
}
setSelected(item: string) {
@@ -59,8 +60,10 @@ export default class NewsSection extends React.Component<IProps, IState> {
}
async showLatestCommits() {
if (!this.state.commitList) {
const response: string = await invoke('req_get', { url: 'https://api.grasscutter.io/cultivation/query' })
// Just use official API
const response: string = await invoke('req_get', {
url: 'https://api.github.com/repos/Grasscutters/Grasscutter/commits',
})
let grasscutterApiResponse: GrasscutterAPIResponse | null = null
try {
@@ -71,7 +74,7 @@ export default class NewsSection extends React.Component<IProps, IState> {
let commits: CommitResponse[]
if (grasscutterApiResponse?.commits == null) {
// If it didn't work, use official API
// If it didn't work, try again anyways
const response: string = await invoke('req_get', {
url: 'https://api.github.com/repos/Grasscutters/Grasscutter/commits',
})
@@ -102,7 +105,6 @@ export default class NewsSection extends React.Component<IProps, IState> {
commitList: commitsListHtml,
news: <>{commitsListHtml}</>,
})
}
return this.state.commitList
}
@@ -122,7 +124,7 @@ export default class NewsSection extends React.Component<IProps, IState> {
case 'latest_version':
news = (
<tr>
<td>Latest version</td>
<td>Latest version: Grasscutter 1.4.6 - Cultivation 1.0.10</td>
</tr>
)
break

View File

@@ -5,7 +5,7 @@ let configFilePath: string
let defaultConfig: Configuration
;(async () => {
defaultConfig = {
toggle_grasscutter: false,
toggle_grasscutter: true,
game_install_path: 'C:\\Program Files\\Genshin Impact\\Genshin Impact game\\GenshinImpact.exe',
grasscutter_with_game: false,
grasscutter_path: '',
@@ -20,8 +20,11 @@ let defaultConfig: Configuration
theme: 'default',
https_enabled: false,
debug_enabled: false,
patch_metadata: true,
patch_rsa: true,
use_internal_proxy: true,
wipe_login: false,
horny_mode: false,
auto_mongodb: false,
}
})()
@@ -44,9 +47,12 @@ export interface Configuration {
theme: string
https_enabled: boolean
debug_enabled: boolean
patch_metadata: boolean
patch_rsa: boolean
use_internal_proxy: boolean
wipe_login: boolean
horny_mode: boolean
swag_mode?: boolean
auto_mongodb: boolean
// Swag stuff
akebi_path?: string

View File

@@ -77,17 +77,20 @@ export default class DownloadHandler {
// Extraction events
listen('extract_start', ({ payload }) => {
// Find the download that is no extracting and set it's status as such
// @ts-expect-error Too lazy to make an interface for payloads rn
const index = this.downloads.findIndex((download) => download.path === payload.file)
// Find the download that is extracting and set it's status as such
const index = this.downloads.findIndex((download) => download.path === payload)
this.downloads[index].status = 'extracting'
})
listen('extract_end', ({ payload }) => {
console.log(payload)
// Find the download that is no extracting and set it's status as such
// @ts-expect-error Too lazy to make an interface for payloads rn
const index = this.downloads.findIndex((download) => download.path === payload.file)
// @ts-expect-error shut up typescript
const obj: {
file: string
new_folder: string
} = payload
// Find the download that is not extracting and set it's status as such
const index = this.downloads.findIndex((download) => download.path === obj.file || obj.new_folder)
this.downloads[index].status = 'finished'
})
}
@@ -101,6 +104,11 @@ export default class DownloadHandler {
return this.downloads.some((d) => d.path.includes('grasscutter.zip'))
}
downloadingFullBuild() {
// Kinda hacky but it works
return this.downloads.some((d) => d.path.includes('GrasscutterCulti'))
}
downloadingResources() {
// Kinda hacky but it works
return this.downloads.some((d) => d.path.includes('resources'))

View File

@@ -1,3 +1,4 @@
import { invoke } from '@tauri-apps/api'
import { getConfig } from './configuration'
export async function getGameExecutable() {
@@ -11,6 +12,17 @@ export async function getGameExecutable() {
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()
@@ -25,3 +37,36 @@ export async function getGameFolder() {
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() {
const GameData = await getGameDataFolder()
if (!GameData) {
return null
}
const settings = JSON.parse(
await invoke('read_file', {
path: GameData + '\\StreamingAssets\\asb_settings.json',
})
)
const versionRaw = settings.variance.split('.')
const version = {
major: parseInt(versionRaw[0]),
minor: parseInt(versionRaw[1].split('_')[0]),
release: parseInt(versionRaw[1].split('_')[1]),
}
return version
}

View File

@@ -1,234 +0,0 @@
import { invoke } from '@tauri-apps/api'
import { dataDir } from '@tauri-apps/api/path'
import DownloadHandler from './download'
import { getGameExecutable, getGameFolder } from './game'
export async function patchMetadata() {
const metadataExists = await invoke('dir_exists', {
path: (await getGameMetadataPath()) + '\\global-metadata.dat',
})
if (!metadataExists) {
return false
}
console.log('Copying unpatched metadata to backup location')
// Copy unpatched metadata to backup location
const copiedMeta = await invoke('copy_file_with_new_name', {
path: (await getGameMetadataPath()) + '\\global-metadata.dat',
newPath: await getBackupMetadataPath(),
newName: 'global-metadata-unpatched.dat',
})
if (!copiedMeta) {
console.log(await getBackupMetadataPath())
return false
}
// backup was successful! Time to patch
console.log('Patching backedup metadata')
const patchedMeta = await invoke('patch_metadata', {
metadataFolder: await getBackupMetadataPath(),
})
if (!patchedMeta) {
return false
}
// Patch also worked! Time to replace
console.log('Replacing unpatched game metadata with patched metadata')
const replacedMeta = await invoke('copy_file_with_new_name', {
path: (await getBackupMetadataPath()) + '\\global-metadata-patched.dat',
newPath: await getGameMetadataPath(),
newName: 'global-metadata.dat',
})
if (!replacedMeta) {
return false
}
console.log('Replacement successful!')
return true
}
export async function patchGame() {
const backupExists = await invoke('dir_exists', {
path: (await getBackupMetadataPath()) + '\\global-metadata-unpatched.dat',
})
if (!backupExists) {
// No backup found? Patching creates one
const patched = await patchMetadata()
if (!patched) {
return false
}
}
// Do we have a patch already?
const patchedExists = await invoke('dir_exists', {
path: (await getBackupMetadataPath()) + '\\global-metadata-patched.dat',
})
if (!patchedExists) {
// No patch found? Patching creates one
const patched = await patchMetadata()
if (!patched) {
return false
}
}
// Are we already patched? If so, that's fine, just continue as normal
const gameIsPatched = await invoke('are_files_identical', {
path1: (await getBackupMetadataPath()) + '\\global-metadata-patched.dat',
path2: (await getGameMetadataPath()) + '\\global-metadata.dat',
})
if (gameIsPatched) {
return true
}
// Is the current backup the same as the games current metadata?
const backupIsCurrent = await invoke('are_files_identical', {
path1: (await getBackupMetadataPath()) + '\\global-metadata-unpatched.dat',
path2: (await getGameMetadataPath()) + '\\global-metadata.dat',
})
// Game has probably been updated. We need to repatch the game...
if (!backupIsCurrent) {
const deletedOldBackup = await invoke('delete_file', {
path: (await getBackupMetadataPath()) + '\\global-metadata-unpatched.dat',
})
const deletedOldPatched = await invoke('delete_file', {
path: (await getBackupMetadataPath()) + '\\global-metadata-patched.dat',
})
// It's fine if these deletes fail. The game will be replaced anyway.
if (!deletedOldBackup) {
console.log('Warning: Failed to delete old backup!')
}
if (!deletedOldPatched) {
console.log('Warning: Failed to delete old patched metadata!')
}
console.log('Patching Metadata')
const patched = await patchMetadata()
if (!patched) {
return false
}
return true
}
console.log('Metadata is not patched')
console.log('Replacing unpatched metadata')
// Finally, replace the unpatched metadata with the patched one
const replaced = await invoke('copy_file_with_new_name', {
path: (await getBackupMetadataPath()) + '\\global-metadata-patched.dat',
newPath: await getGameMetadataPath(),
newName: 'global-metadata.dat',
})
if (!replaced) {
return false
}
return true
}
export async function unpatchGame() {
const backupExists = await invoke('dir_exists', {
path: (await getBackupMetadataPath()) + '\\global-metadata-unpatched.dat',
})
if (!backupExists) {
// Let's just hope the game isn't on a patched metadata since we don't have a backup...
return true
}
const replaced = await invoke('copy_file_with_new_name', {
path: (await getBackupMetadataPath()) + '\\global-metadata-unpatched.dat',
newPath: await getGameMetadataPath(),
newName: 'global-metadata.dat',
})
return replaced
}
export async function getGameMetadataPath() {
const gameExec = await getGameExecutable()
if (!gameExec) {
return null
}
return ((await getGameFolder()) + '\\' + gameExec.replace('.exe', '_Data') + '\\Managed\\Metadata').replace(
/\\/g,
'/'
)
}
export async function getBackupMetadataPath() {
return (await dataDir()) + 'cultivation\\metadata'
}
export async function globalMetadataLink() {
const versionAPIUrl =
'https://sdk-os-static.mihoyo.com/hk4e_global/mdk/launcher/api/resource?channel_id=1&key=gcStgarh&launcher_id=10&sub_channel_id=0'
// Get versions from API
const versions = JSON.parse(
await invoke('web_get', {
url: versionAPIUrl,
})
)
if (!versions || versions.retcode !== 0) {
console.log('Failed to get versions from API')
return null
}
// Get latest version
const latest = versions.data.game.latest
return (latest.decompressed_path as string) + '/GenshinImpact_Data/Managed/Metadata/global-metadata.dat'
}
export async function restoreMetadata(manager: DownloadHandler) {
const metaLink = await globalMetadataLink()
if (!metaLink) {
console.log('Could not get global metadata link!')
return false
}
// Should make sure metadata path exists since the user may have deleted it
await invoke('dir_create', {
path: await getBackupMetadataPath(),
})
// It is possible the unpatched backup is mistakenly patched
await invoke('delete_file', {
path: (await getBackupMetadataPath()) + '\\global-metadata-unpatched.dat',
})
// Download the file
manager.addDownload(metaLink, (await getBackupMetadataPath()) + '\\global-metadata-unpatched.dat', () => {
unpatchGame()
})
console.log('Restoring backedup metadata')
await unpatchGame()
return true
}

85
src/utils/rsa.ts Normal file
View File

@@ -0,0 +1,85 @@
import { invoke } from '@tauri-apps/api'
import { dataDir } from '@tauri-apps/api/path'
import { getGameFolder } from './game'
export async function patchGame() {
// Do we have a patch already?
const patchedExists = await invoke('dir_exists', {
path: (await getBackupRSAPath()) + '\\version.dll',
})
if (!patchedExists) {
// No patch found? Patching creates one
const patched = await downloadRSA()
if (!patched) {
return false
}
}
// Are we already patched with mhypbase? If so, that's fine, just continue as normal
const gameIsPatched = await invoke('are_files_identical', {
path1: (await getBackupRSAPath()) + '\\version.dll',
path2: (await getGameRSAPath()) + '\\mhypbase.dll',
})
// Tell user they won't be unpatched with manual mhypbase patch
if (gameIsPatched) {
console.log('You are already patched using mhypbase, so you will not be auto patched and unpatched!')
return true
}
// Copy the patch to game files
const replaced = await invoke('copy_file_with_new_name', {
path: (await getBackupRSAPath()) + '\\version.dll',
newPath: await getGameRSAPath(),
newName: 'version.dll',
})
if (!replaced) {
return false
}
return true
}
export async function unpatchGame() {
// Just delete patch since it's not replacing any existing file
const deleted = await invoke('delete_file', {
path: (await getGameRSAPath()) + '\\version.dll',
})
return deleted
}
export async function getGameRSAPath() {
const gameData = await getGameFolder()
if (!gameData) {
return null
}
return (gameData + '\\').replace(/\\/g, '/')
}
export async function getBackupRSAPath() {
return (await dataDir()) + 'cultivation\\rsa'
}
export async function downloadRSA() {
// Patch file from: https://github.com/34736384/RSAPatch/
// Should make sure rsa path exists
await invoke('dir_create', {
path: await getBackupRSAPath(),
})
// Copy patch from local for offline compatibility
await invoke('copy_file_with_new_name', {
path: (await invoke('install_location')) + '\\patch\\version.dll',
newPath: await getBackupRSAPath(),
newName: 'version.dll',
})
return true
}

View File

@@ -10,7 +10,7 @@ export async function toggleEncryption(path: string) {
})
)
} catch (e) {
console.log(`Server config at ${path} not found or invalid`)
console.log(`Server config at ${path} not found or invalid. Be sure to run the server at least once to generate it`)
return
}
@@ -36,7 +36,7 @@ export async function encryptionEnabled(path: string) {
})
)
} catch (e) {
console.log(`Server config at ${path} not found or invalid`)
console.log(`Server config at ${path} not found or invalid. Be sure to run the server at least once to generate it`)
return false
}

View File

@@ -6002,9 +6002,9 @@ loader-runner@^4.2.0:
integrity sha512-3R/1M+yS3j5ou80Me59j7F9IMs4PXs3VqRrm0TU3AbKPxlmpoY1TNscJV/oGJXo8qCatFGTfDbY6W6ipGOYXfg==
loader-utils@^2.0.0:
version "2.0.2"
resolved "https://registry.yarnpkg.com/loader-utils/-/loader-utils-2.0.2.tgz#d6e3b4fb81870721ae4e0868ab11dd638368c129"
integrity sha512-TM57VeHptv569d/GKh6TAYdzKblwDNiumOdkFnejjD0XwTH87K90w3O7AiJRqdQoXygvi1VQTJTLGhJl7WqA7A==
version "2.0.4"
resolved "https://registry.yarnpkg.com/loader-utils/-/loader-utils-2.0.4.tgz#8b5cb38b5c34a9a018ee1fc0e6a066d1dfcc528c"
integrity sha512-xXqpXoINfFhgua9xiqD8fPFHgkoq1mmmpE92WlDbm9rNRd/EbRb+Gqf908T2DMfuHjjJlksiK2RbHVOdD/MqSw==
dependencies:
big.js "^5.2.2"
emojis-list "^3.0.0"