mirror of
https://github.com/Grasscutters/Cultivation.git
synced 2025-12-14 16:14:48 +01:00
Compare commits
213 Commits
dev
...
v1.1.1-alp
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
87b97c6824 | ||
|
|
8b779bcb6b | ||
|
|
fc7fb739e7 | ||
|
|
044be37e9e | ||
|
|
6f40e1789b | ||
|
|
fe974a1ae1 | ||
|
|
b5e48b6998 | ||
|
|
ed9fb9a644 | ||
|
|
be3a1c6f6f | ||
|
|
158c82d959 | ||
|
|
4cb8a27a9c | ||
|
|
9d26f6f146 | ||
|
|
277f009883 | ||
|
|
05caa7c7a3 | ||
|
|
681c1fce3e | ||
|
|
06e1ca23e1 | ||
|
|
053b4760b1 | ||
|
|
79aa4ca61d | ||
|
|
bf4c1d87e8 | ||
|
|
3205c96a62 | ||
|
|
d551344f39 | ||
|
|
84a6267c65 | ||
|
|
4c9e70a8f8 | ||
|
|
87897aabec | ||
|
|
acb9061d1c | ||
|
|
a954fcfc1c | ||
|
|
13dcbcd361 | ||
|
|
302754d486 | ||
|
|
c2cbf4049e | ||
|
|
554bda4643 | ||
|
|
d1018c249e | ||
|
|
0df78f35bd | ||
|
|
fb60889857 | ||
|
|
9516c78871 | ||
|
|
2a45f35062 | ||
|
|
ba12ab6586 | ||
|
|
0c37493d2a | ||
|
|
c8a368235c | ||
|
|
c2587cc923 | ||
|
|
00929a3efe | ||
|
|
ce78bf69aa | ||
|
|
732e020eb3 | ||
|
|
114a1d2023 | ||
|
|
3ed197b4b4 | ||
|
|
eed59e7883 | ||
|
|
5b9af3fcce | ||
|
|
b9b8632992 | ||
|
|
878dfed932 | ||
|
|
15e6958527 | ||
|
|
36b3c2f841 | ||
|
|
c6abf53880 | ||
|
|
9dcaea7e5b | ||
|
|
f4b8cdf732 | ||
|
|
ed743b2ff9 | ||
|
|
776aee0265 | ||
|
|
f508b3f0cf | ||
|
|
5bad4d05ee | ||
|
|
f53d903a8b | ||
|
|
47b15fc2ff | ||
|
|
bdb0fd3eb7 | ||
|
|
4631a6d38d | ||
|
|
b34f71a301 | ||
|
|
dea373a0d5 | ||
|
|
cab19e64c4 | ||
|
|
5cdde7d391 | ||
|
|
95e7949f18 | ||
|
|
5600269d7e | ||
|
|
b46a1d1fcf | ||
|
|
31f77355f1 | ||
|
|
7a4c28b501 | ||
|
|
96c4e4b886 | ||
|
|
17fed553da | ||
|
|
5374e2b0b9 | ||
|
|
d13c8e8861 | ||
|
|
26d1df2927 | ||
|
|
6e711073ad | ||
|
|
e38467f054 | ||
|
|
df48f888ff | ||
|
|
b78479a293 | ||
|
|
3969c26a58 | ||
|
|
9d9bc43119 | ||
|
|
2568694ac1 | ||
|
|
c735b8936a | ||
|
|
c88e3ce3a7 | ||
|
|
e5d151f512 | ||
|
|
361737c00d | ||
|
|
0f08bb5fbf | ||
|
|
df674abd94 | ||
|
|
c21c5ac0b1 | ||
|
|
6ac0182784 | ||
|
|
2fa203163d | ||
|
|
75c6481778 | ||
|
|
12b60d3b2a | ||
|
|
88b5b40300 | ||
|
|
45ecfcbeb3 | ||
|
|
e6492825dc | ||
|
|
3141bcea41 | ||
|
|
64a04e927c | ||
|
|
4bcfd7ec09 | ||
|
|
a67eca49e5 | ||
|
|
48dff50a5d | ||
|
|
5480af7835 | ||
|
|
829b9822cb | ||
|
|
a6716e80f4 | ||
|
|
6962518ced | ||
|
|
09d0d8287f | ||
|
|
7bfa5e8e11 | ||
|
|
5de80f1655 | ||
|
|
33ce547bb2 | ||
|
|
864f9f199c | ||
|
|
5d8e4d7311 | ||
|
|
9f0567da6a | ||
|
|
56453ff55b | ||
|
|
61c5cc8d57 | ||
|
|
7bbc7e3c10 | ||
|
|
5b7c1307d9 | ||
|
|
096992572c | ||
|
|
68f0cce154 | ||
|
|
19db69646f | ||
|
|
82f40186fe | ||
|
|
d7b2aa25cc | ||
|
|
3356bddb42 | ||
|
|
db11cf7907 | ||
|
|
891dbb41aa | ||
|
|
cf6ec3da82 | ||
|
|
b2d6f390fb | ||
|
|
a920d70e53 | ||
|
|
d2e7759eec | ||
|
|
77380e357c | ||
|
|
33ddc36741 | ||
|
|
bbd18d33e9 | ||
|
|
b66f01232b | ||
|
|
b6a2e4dbc0 | ||
|
|
45ee8fbb23 | ||
|
|
78c2a8d0c8 | ||
|
|
fe420e9837 | ||
|
|
57576f399b | ||
|
|
8d45b3d11d | ||
|
|
1ff96f9c7e | ||
|
|
e834022ca7 | ||
|
|
c72e502603 | ||
|
|
5d6bd72083 | ||
|
|
ef3ba2a045 | ||
|
|
90b86b42d0 | ||
|
|
01d12178ba | ||
|
|
b67e0992f1 | ||
|
|
a949754dce | ||
|
|
cf536b853a | ||
|
|
19c8537e91 | ||
|
|
420e437788 | ||
|
|
8a62a5a8c7 | ||
|
|
d56c6bc069 | ||
|
|
ea73feb8e9 | ||
|
|
abf7a428f6 | ||
|
|
7bd3493366 | ||
|
|
20a80e5625 | ||
|
|
4a8db8eb0f | ||
|
|
f44dfeb79d | ||
|
|
098ba63066 | ||
|
|
b8255eb5bc | ||
|
|
812aa64c5e | ||
|
|
b3aaf5c5f7 | ||
|
|
9e6029faf3 | ||
|
|
b18f7a0cf6 | ||
|
|
62a97d86cb | ||
|
|
55fa0a8d3c | ||
|
|
2aac7fc022 | ||
|
|
ddaf24590c | ||
|
|
44f5cac2bf | ||
|
|
e1a519cc5f | ||
|
|
3dd1ec2674 | ||
|
|
438596f4e2 | ||
|
|
af5c9229d4 | ||
|
|
fb842ccc86 | ||
|
|
27d30a183c | ||
|
|
182943fd69 | ||
|
|
b352b16dc9 | ||
|
|
b1b18c75a1 | ||
|
|
7c53669fc4 | ||
|
|
249f07927d | ||
|
|
090e1a888d | ||
|
|
5dfa98c9ea | ||
|
|
088d007816 | ||
|
|
d49fdacf2a | ||
|
|
8efd601eda | ||
|
|
f2e3c69c0f | ||
|
|
85a16e3407 | ||
|
|
596c5b85b1 | ||
|
|
1115283df8 | ||
|
|
696a8a1dec | ||
|
|
1be06b0a6c | ||
|
|
7f71549831 | ||
|
|
08d9db05a5 | ||
|
|
cd2b8aee12 | ||
|
|
ec611f9d7d | ||
|
|
d6b1c7a613 | ||
|
|
9b1d3594ed | ||
|
|
3848c39743 | ||
|
|
7831bd48dc | ||
|
|
86d15cd335 | ||
|
|
bf880ac297 | ||
|
|
e1637cebef | ||
|
|
d7f3218657 | ||
|
|
0dede9b17c | ||
|
|
ec7b0904d2 | ||
|
|
937faf85e2 | ||
|
|
6f1f55663b | ||
|
|
f3f5dc95ae | ||
|
|
470ddb0598 | ||
|
|
6c40044931 | ||
|
|
cf267657dd | ||
|
|
3d2425fb3d | ||
|
|
f7c8059f4c |
4
.github/workflows/build.yml
vendored
4
.github/workflows/build.yml
vendored
@@ -39,7 +39,7 @@ jobs:
|
|||||||
- name: Compress build
|
- name: Compress build
|
||||||
uses: vimtor/action-zip@v1
|
uses: vimtor/action-zip@v1
|
||||||
with:
|
with:
|
||||||
files: src-tauri/target/debug/lang/ src-tauri/target/debug/keys/ src-tauri/target/debug/Cultivation.exe src-tauri/target/debug/bundle/msi/
|
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
|
recursive: true
|
||||||
dest: Cultivation.zip
|
dest: Cultivation.zip
|
||||||
|
|
||||||
@@ -74,7 +74,7 @@ jobs:
|
|||||||
- name: Compress build
|
- name: Compress build
|
||||||
uses: vimtor/action-zip@v1
|
uses: vimtor/action-zip@v1
|
||||||
with:
|
with:
|
||||||
files: src-tauri/target/debug/lang/ src-tauri/target/debug/keys/ src-tauri/target/debug/cultivation
|
files: src-tauri/target/debug/lang/ src-tauri/target/debug/keys/ src-tauri/target/debug/patch/ src-tauri/target/debug/cultivation
|
||||||
recursive: true
|
recursive: true
|
||||||
dest: Cultivation.zip
|
dest: Cultivation.zip
|
||||||
|
|
||||||
|
|||||||
2
.gitignore
vendored
2
.gitignore
vendored
@@ -10,7 +10,6 @@
|
|||||||
|
|
||||||
# production
|
# production
|
||||||
/build
|
/build
|
||||||
.idea
|
|
||||||
|
|
||||||
# misc
|
# misc
|
||||||
.DS_Store
|
.DS_Store
|
||||||
@@ -18,6 +17,7 @@
|
|||||||
.env.development.local
|
.env.development.local
|
||||||
.env.test.local
|
.env.test.local
|
||||||
.env.production.local
|
.env.production.local
|
||||||
|
.vs
|
||||||
|
|
||||||
npm-debug.log*
|
npm-debug.log*
|
||||||
yarn-debug.log*
|
yarn-debug.log*
|
||||||
|
|||||||
8
.idea/.gitignore
generated
vendored
Normal file
8
.idea/.gitignore
generated
vendored
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
# Default ignored files
|
||||||
|
/shelf/
|
||||||
|
/workspace.xml
|
||||||
|
# Editor-based HTTP Client requests
|
||||||
|
/httpRequests/
|
||||||
|
# Datasource local storage ignored files
|
||||||
|
/dataSources/
|
||||||
|
/dataSources.local.xml
|
||||||
14
.idea/cultivation.iml
generated
Normal file
14
.idea/cultivation.iml
generated
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<module type="WEB_MODULE" version="4">
|
||||||
|
<component name="NewModuleRootManager">
|
||||||
|
<content url="file://$MODULE_DIR$">
|
||||||
|
<sourceFolder url="file://$MODULE_DIR$/src-tauri/src" isTestSource="false" />
|
||||||
|
<excludeFolder url="file://$MODULE_DIR$/temp" />
|
||||||
|
<excludeFolder url="file://$MODULE_DIR$/.tmp" />
|
||||||
|
<excludeFolder url="file://$MODULE_DIR$/tmp" />
|
||||||
|
<excludeFolder url="file://$MODULE_DIR$/src-tauri/target" />
|
||||||
|
</content>
|
||||||
|
<orderEntry type="inheritedJdk" />
|
||||||
|
<orderEntry type="sourceFolder" forTests="false" />
|
||||||
|
</component>
|
||||||
|
</module>
|
||||||
7
.idea/discord.xml
generated
Normal file
7
.idea/discord.xml
generated
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="DiscordProjectSettings">
|
||||||
|
<option name="show" value="PROJECT_FILES" />
|
||||||
|
<option name="description" value="" />
|
||||||
|
</component>
|
||||||
|
</project>
|
||||||
40
.idea/inspectionProfiles/Project_Default.xml
generated
Normal file
40
.idea/inspectionProfiles/Project_Default.xml
generated
Normal file
@@ -0,0 +1,40 @@
|
|||||||
|
<component name="InspectionProjectProfileManager">
|
||||||
|
<profile version="1.0">
|
||||||
|
<option name="myName" value="Project Default" />
|
||||||
|
<inspection_tool class="Eslint" enabled="true" level="WARNING" enabled_by_default="true" />
|
||||||
|
<inspection_tool class="HtmlUnknownBooleanAttribute" enabled="false" level="WARNING" enabled_by_default="false" />
|
||||||
|
<inspection_tool class="HttpUrlsUsage" enabled="true" level="WEAK WARNING" enabled_by_default="true">
|
||||||
|
<option name="ignoredUrls">
|
||||||
|
<list>
|
||||||
|
<option value="http://localhost" />
|
||||||
|
<option value="http://127.0.0.1" />
|
||||||
|
<option value="http://0.0.0.0" />
|
||||||
|
<option value="http://www.w3.org/" />
|
||||||
|
<option value="http://json-schema.org/draft" />
|
||||||
|
<option value="http://java.sun.com/" />
|
||||||
|
<option value="http://xmlns.jcp.org/" />
|
||||||
|
<option value="http://javafx.com/javafx/" />
|
||||||
|
<option value="http://javafx.com/fxml" />
|
||||||
|
<option value="http://maven.apache.org/xsd/" />
|
||||||
|
<option value="http://maven.apache.org/POM/" />
|
||||||
|
<option value="http://www.springframework.org/schema/" />
|
||||||
|
<option value="http://www.springframework.org/tags" />
|
||||||
|
<option value="http://www.springframework.org/security/tags" />
|
||||||
|
<option value="http://www.thymeleaf.org" />
|
||||||
|
<option value="http://www.jboss.org/j2ee/schema/" />
|
||||||
|
<option value="http://www.jboss.com/xml/ns/" />
|
||||||
|
<option value="http://www.ibm.com/webservices/xsd" />
|
||||||
|
<option value="http://activemq.apache.org/schema/" />
|
||||||
|
<option value="http://schema.cloudfoundry.org/spring/" />
|
||||||
|
<option value="http://schemas.xmlsoap.org/" />
|
||||||
|
<option value="http://cxf.apache.org/schemas/" />
|
||||||
|
<option value="http://primefaces.org/ui" />
|
||||||
|
<option value="http://tiles.apache.org/" />
|
||||||
|
<option value="http://api.grasscutter.io" />
|
||||||
|
<option value="http://api.grasscutters.xyz" />
|
||||||
|
</list>
|
||||||
|
</option>
|
||||||
|
</inspection_tool>
|
||||||
|
<inspection_tool class="JSIgnoredPromiseFromCall" enabled="false" level="WEAK WARNING" enabled_by_default="false" />
|
||||||
|
</profile>
|
||||||
|
</component>
|
||||||
8
.idea/modules.xml
generated
Normal file
8
.idea/modules.xml
generated
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="ProjectModuleManager">
|
||||||
|
<modules>
|
||||||
|
<module fileurl="file://$PROJECT_DIR$/.idea/cultivation.iml" filepath="$PROJECT_DIR$/.idea/cultivation.iml" />
|
||||||
|
</modules>
|
||||||
|
</component>
|
||||||
|
</project>
|
||||||
6
.idea/vcs.xml
generated
Normal file
6
.idea/vcs.xml
generated
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="VcsDirectoryMappings">
|
||||||
|
<mapping directory="$PROJECT_DIR$" vcs="Git" />
|
||||||
|
</component>
|
||||||
|
</project>
|
||||||
3
.vscode/settings.json
vendored
Normal file
3
.vscode/settings.json
vendored
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
{
|
||||||
|
"rust-analyzer.linkedProjects": [".\\src-tauri\\Cargo.toml"]
|
||||||
|
}
|
||||||
40
README.md
40
README.md
@@ -9,6 +9,7 @@ A game launcher designed to easily proxy traffic from anime game to private serv
|
|||||||
- [Client Patching Notice](#client-patching-notice)
|
- [Client Patching Notice](#client-patching-notice)
|
||||||
- [Download](#download)
|
- [Download](#download)
|
||||||
- [Setup](#setup)
|
- [Setup](#setup)
|
||||||
|
- [Troubleshooting](#troubleshooting)
|
||||||
- [Developer Quick-start](#developer-quickstart)
|
- [Developer Quick-start](#developer-quickstart)
|
||||||
- [Setup](#setup)
|
- [Setup](#setup)
|
||||||
- [Building](#building)
|
- [Building](#building)
|
||||||
@@ -18,9 +19,9 @@ A game launcher designed to easily proxy traffic from anime game to private serv
|
|||||||
- [Screenshots](#screenshots)
|
- [Screenshots](#screenshots)
|
||||||
- [Credits](#credits)
|
- [Credits](#credits)
|
||||||
|
|
||||||
# Client Patching Notice
|
# Client Patching Notice - RSA
|
||||||
|
|
||||||
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.
|
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
|
# Download
|
||||||
|
|
||||||
@@ -28,7 +29,7 @@ For game versions 2.8 and above, Cultivation automatically makes a small patch t
|
|||||||
|
|
||||||
Download and open the MSI, and once installed, run Cultivation as administrator. Refer below for more [detailed setup instructions](#setup).
|
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`.
|
**Windows 7 Users:** You will need to download [WebView2](https://developer.microsoft.com/en-us/microsoft-edge/webview2/#download-section) manually, and download the `.zip` instead of the `.msi`.
|
||||||
|
|
||||||
# Setup
|
# Setup
|
||||||
|
|
||||||
@@ -36,8 +37,8 @@ Download and open the MSI, and once installed, run Cultivation as administrator.
|
|||||||
|
|
||||||
- Download Cultivation
|
- Download Cultivation
|
||||||
- If you are on Windows 10 or 11, use the MSI
|
- 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 Windows 7, or the MSI doesn't work, use the zip and download [WebView2](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)
|
- If you are on GNU/Linux or MacOS, [help us port Windows-specific system calls to GNU/Linux and MacOS!](https://github.com/Grasscutters/Cultivation/issues/7)
|
||||||
- Install or extract Cultivation
|
- Install or extract Cultivation
|
||||||
- Open Cultivation **_as administrator_**
|
- Open Cultivation **_as administrator_**
|
||||||
- Before clicking randomly on stuff, in options (top right cog icon), set your Game Install Path.
|
- Before clicking randomly on stuff, in options (top right cog icon), set your Game Install Path.
|
||||||
@@ -47,9 +48,8 @@ Download and open the MSI, and once installed, run Cultivation as administrator.
|
|||||||
- 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 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)
|
- If you are getting System Error, or 4214, ask the [Discord support channels](https://discord.gg/grasscutter)
|
||||||
- Open the "Downloads" menu (top right)
|
- Open the "Downloads" menu (top right)
|
||||||
- Download "latest grasscutter" (second from the top)
|
- Download "Grasscutter All-in-One" (top of the list)
|
||||||
- Download "resources" (very bottom)
|
- Once that is done, click the icon next to "Launch"
|
||||||
- Once all of that is done, click the icon next to "Launch"
|
|
||||||
- To play on your new server:
|
- To play on your new server:
|
||||||
- Click "Connect with Grasscutter"
|
- Click "Connect with Grasscutter"
|
||||||
- Input `localhost` as the address, and `443` as the port
|
- Input `localhost` as the address, and `443` as the port
|
||||||
@@ -58,6 +58,19 @@ Download and open the MSI, and once installed, run Cultivation as administrator.
|
|||||||
- Any specific Cultivation issues should go in [the issues section](/issues)
|
- 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)
|
- Any Grasscutter server related issues should go in [the Grasscutter issues section](https://github.com/Grasscutters/Grasscutter)
|
||||||
|
|
||||||
|
# Troubleshooting
|
||||||
|
|
||||||
|
### White screen, insta-crash or something similar
|
||||||
|
|
||||||
|
- First try [running in Windows 8 compatibility mode](https://www.lifewire.com/run-older-programs-with-windows-10-compatibility-mode-4587064).
|
||||||
|
- If that doesn't work, fully uninstall and reinstall [WebView2](https://developer.microsoft.com/en-us/microsoft-edge/webview2/#download-section).
|
||||||
|
- If you are having trouble uninstalling it, try deleting this registry folder and uninstalling again `Computer\HKEY_LOCAL_MACHINE\SOFTWARE\WOW6432Node\Microsoft\EdgeUpdate\Clients\{F3017226-FE2A-4295-8BDF-00C3A9A7E4C5}`
|
||||||
|
- You can also try [uninstalling from the Command Prompt](https://superuser.com/a/1743626)
|
||||||
|
|
||||||
|
### Internet not working after use
|
||||||
|
|
||||||
|
Please allow the Cultivation window to pop back up once you have quit out of the game. This tells you that it knows you closed the game, and that it has reverted your proxy settings. If you have closed Cultivation before this happens, or have had some other issue with your internet, go to your [proxy settings in Windows](https://techviral.net/check-proxy-server-settings-in-windows/) and disable the "Manual proxy setup".
|
||||||
|
|
||||||
# Developer Quickstart
|
# Developer Quickstart
|
||||||
|
|
||||||
### Setup
|
### Setup
|
||||||
@@ -66,7 +79,7 @@ Download and open the MSI, and once installed, run Cultivation as administrator.
|
|||||||
- Install [yarn](https://classic.yarnpkg.com/lang/en/docs/install) (cry about it `npm` lovers)
|
- Install [yarn](https://classic.yarnpkg.com/lang/en/docs/install) (cry about it `npm` lovers)
|
||||||
- Install [Rust](https://www.rust-lang.org/tools/install)
|
- Install [Rust](https://www.rust-lang.org/tools/install)
|
||||||
- `yarn install`
|
- `yarn install`
|
||||||
- `yarn start:dev`
|
- `yarn tauri dev`
|
||||||
|
|
||||||
### Building
|
### Building
|
||||||
|
|
||||||
@@ -102,11 +115,10 @@ A full theming reference can be found [here!](/THEMES.md)
|
|||||||
|
|
||||||
# Screenshots
|
# Screenshots
|
||||||
|
|
||||||

|

|
||||||

|

|
||||||

|

|
||||||

|

|
||||||

|
|
||||||
|
|
||||||
## Credits
|
## Credits
|
||||||
|
|
||||||
|
|||||||
@@ -1,9 +1,5 @@
|
|||||||
[EN](README.md) | 简中 | [繁中](README_zh-TW.md)
|
[EN](README.md) | 简中 | [繁中](README_zh-TW.md)
|
||||||
|
|
||||||
# 客户端修补通知
|
|
||||||
|
|
||||||
对于游戏版本为 2.8 及以上时,使用 Grasscutter 启动时,Cultivation 会自动为您的游戏客户端制作一个小补丁,并在关闭游戏时恢复它。 从理论上讲,你应该是完全安全的,但是不明确**如果您使用它连接到官方服务器,修改游戏客户端可能会导致封号**,但可能性是非常小的,并且从未接到发生过此类情况的问题,但存在这种可能性!
|
|
||||||
|
|
||||||
# Cultivation
|
# Cultivation
|
||||||
|
|
||||||
一个游戏启动器,旨在轻松将某动漫游戏的流量代理到私人服务器。
|
一个游戏启动器,旨在轻松将某动漫游戏的流量代理到私人服务器。
|
||||||
@@ -23,6 +19,10 @@
|
|||||||
- [画面](#画面)
|
- [画面](#画面)
|
||||||
- [成员](#成员)
|
- [成员](#成员)
|
||||||
|
|
||||||
|
# 客户端修补通知
|
||||||
|
|
||||||
|
对于游戏版本为 3.1 及以上时,使用 Grasscutter 启动时,Cultivation 会自动为您的游戏客户端制作一个小补丁,并在关闭游戏时恢复它。 从理论上讲,你应该是完全安全的,但是不明确**如果您使用它连接到官方服务器,修改游戏客户端可能会导致封号**,但可能性是非常小的,并且从未接到发生过此类情况的问题,但存在这种可能性!
|
||||||
|
|
||||||
# 下载
|
# 下载
|
||||||
|
|
||||||
[在此处查找发布版本!](https://github.com/Grasscutters/Cultivation/releases)
|
[在此处查找发布版本!](https://github.com/Grasscutters/Cultivation/releases)
|
||||||
@@ -73,11 +73,10 @@
|
|||||||
|
|
||||||
# 画面
|
# 画面
|
||||||
|
|
||||||

|

|
||||||

|

|
||||||

|

|
||||||

|

|
||||||

|
|
||||||
|
|
||||||
## 成员
|
## 成员
|
||||||
|
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
# 客戶端修補通知
|
# 客戶端修補通知
|
||||||
|
|
||||||
對於遊戲版本為 2.8 及以上時,使用 Grasscutter 啟動時,Cultivation 會自動為您的遊戲客戶端製作一個小修補,並在關閉遊戲時恢復它。 從理論上講,你應該是完全安全的,但是不明確**如果您使用它連接到官方伺服器,修改遊戲客戶端可能會導致封號**,但可能性是非常小的,並且從未接到發生過此類情況的問題,但存在這種可能性!
|
對於遊戲版本為 3.1 及以上時,使用 Grasscutter 啟動時,Cultivation 會自動為您的遊戲客戶端製作一個小修補,並在關閉遊戲時恢復它。 從理論上講,你應該是完全安全的,但是不明確**如果您使用它連接到官方伺服器,修改遊戲客戶端可能會導致封號**,但可能性是非常小的,並且從未接到發生過此類情況的問題,但存在這種可能性!
|
||||||
|
|
||||||
# Cultivation
|
# Cultivation
|
||||||
|
|
||||||
@@ -73,11 +73,10 @@
|
|||||||
|
|
||||||
# 畫面
|
# 畫面
|
||||||
|
|
||||||

|

|
||||||

|

|
||||||

|

|
||||||

|

|
||||||

|
|
||||||
|
|
||||||
## 成員
|
## 成員
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "cultivation",
|
"name": "cultivation",
|
||||||
"version": "1.0.10",
|
"version": "1.0.26",
|
||||||
"private": true,
|
"private": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@tauri-apps/api": "^1.0.0-rc.5",
|
"@tauri-apps/api": "^1.0.0-rc.5",
|
||||||
|
|||||||
1491
src-tauri/Cargo.lock
generated
1491
src-tauri/Cargo.lock
generated
File diff suppressed because it is too large
Load Diff
@@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "cultivation"
|
name = "cultivation"
|
||||||
version = "0.1.0"
|
version = "1.1.1"
|
||||||
description = "A custom launcher for anime game."
|
description = "A custom launcher for anime game."
|
||||||
authors = ["KingRainbow44", "SpikeHD"]
|
authors = ["KingRainbow44", "SpikeHD"]
|
||||||
license = ""
|
license = ""
|
||||||
@@ -12,49 +12,56 @@ rust-version = "1.57"
|
|||||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||||
|
|
||||||
[build-dependencies]
|
[build-dependencies]
|
||||||
tauri-build = { version = "1.2.0", features = [] }
|
tauri-build = { version = "1.0.0-rc.8", features = [] }
|
||||||
cc = "1.0"
|
cc = "1.0"
|
||||||
|
|
||||||
[target.'cfg(windows)'.dependencies]
|
[target.'cfg(windows)'.dependencies]
|
||||||
is_elevated = "0.1.2"
|
is_elevated = "0.1.2"
|
||||||
registry = "1.2.2"
|
registry = "1.2.1"
|
||||||
|
|
||||||
[target.'cfg(unix)'.dependencies]
|
[target.'cfg(unix)'.dependencies]
|
||||||
sudo = "0.6.0"
|
sudo = "0.6.0"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
serde = { version = "1.0", features = ["derive"] }
|
serde = { version = "1.0", features = ["derive"] }
|
||||||
tauri = { version = "1.2.0", features = ["api-all"] }
|
tauri = { version = "1.0.9", features = ["api-all"] }
|
||||||
|
|
||||||
|
# Arg parsing
|
||||||
|
args = "2.0"
|
||||||
|
getopts = "0.2"
|
||||||
|
|
||||||
# Access system process info.
|
# Access system process info.
|
||||||
sysinfo = "0.26.7"
|
sysinfo = "0.28.4"
|
||||||
|
|
||||||
# ZIP-archive library.
|
# ZIP-archive library.
|
||||||
zip-extract = "0.1.1"
|
zip-extract = "0.1.1"
|
||||||
unrar = "0.4.4"
|
unrar = "0.4.4"
|
||||||
zip = "0.6.3"
|
zip = "0.6.2"
|
||||||
|
sevenz-rust = "0.2.9"
|
||||||
|
|
||||||
# For creating a "global" downloads list.
|
# For creating a "global" downloads list.
|
||||||
once_cell = "1.16.0"
|
once_cell = "1.13.0"
|
||||||
|
|
||||||
# Program opener.
|
# Program opener.
|
||||||
open = "3.0.3"
|
open = "3.0.2"
|
||||||
duct = "0.13.5"
|
|
||||||
|
# Services
|
||||||
|
windows-service = "0.6.0"
|
||||||
|
|
||||||
# Serialization.
|
# Serialization.
|
||||||
serde_json = "1"
|
serde_json = "1"
|
||||||
|
|
||||||
# Dependencies for the HTTP(S) proxy.
|
# Dependencies for the HTTP(S) proxy.
|
||||||
http = "0.2"
|
http = "0.2"
|
||||||
hudsucker = "0.18.0"
|
hudsucker = "0.19.1"
|
||||||
tracing = "0.1.37"
|
tracing = "0.1.21"
|
||||||
tokio-rustls = "0.23.4"
|
tokio-rustls = "0.23.0"
|
||||||
tokio-tungstenite = "0.17.2"
|
tokio-tungstenite = "0.17.0"
|
||||||
tokio = { version = "1.21.2", features = ["signal"] }
|
tokio = { version = "1.20.4", features = ["signal"] }
|
||||||
rustls-pemfile = "1.0.1"
|
rustls-pemfile = "1.0.0"
|
||||||
reqwest = { version = "0.11.13", features = ["stream"] }
|
reqwest = { version = "0.11.3", features = ["stream"] }
|
||||||
futures-util = "0.3.25"
|
futures-util = "0.3.14"
|
||||||
rcgen = { version = "0.10", features = ["x509-parser"] }
|
rcgen = { version = "0.9", features = ["x509-parser"] }
|
||||||
|
|
||||||
# metadata stuff
|
# metadata stuff
|
||||||
regex = "1"
|
regex = "1"
|
||||||
|
|||||||
@@ -14,8 +14,9 @@
|
|||||||
"enabled": "已启用",
|
"enabled": "已启用",
|
||||||
"disabled": "已禁用",
|
"disabled": "已禁用",
|
||||||
"game_path": "选择游戏安装路径",
|
"game_path": "选择游戏安装路径",
|
||||||
|
"game_command": "启动游戏的命令行",
|
||||||
"game_executable": "选择游戏可执行文件",
|
"game_executable": "选择游戏可执行文件",
|
||||||
"recover_metadata": "紧急情况下恢复元数据文件",
|
"recover_rsa": "紧急情况下删除补丁文件",
|
||||||
"grasscutter_jar": "选择 Grasscutter JAR 文件",
|
"grasscutter_jar": "选择 Grasscutter JAR 文件",
|
||||||
"toggle_encryption": "启用加密",
|
"toggle_encryption": "启用加密",
|
||||||
"install_certificate": "安装代理证书",
|
"install_certificate": "安装代理证书",
|
||||||
@@ -24,20 +25,30 @@
|
|||||||
"language": "选择语言",
|
"language": "选择语言",
|
||||||
"background": "设置自定义背景(链接或文件)",
|
"background": "设置自定义背景(链接或文件)",
|
||||||
"theme": "设置主题",
|
"theme": "设置主题",
|
||||||
"patch_metadata": "自动修改元数据",
|
"patch_rsa": "自动修改RSA",
|
||||||
"use_proxy": "使用内置代理"
|
"use_proxy": "使用内置代理",
|
||||||
|
"wipe_login": "清除登录缓存",
|
||||||
|
"horny_mode": "Horny 模式",
|
||||||
|
"auto_mongodb": "自动启动 MongoDB",
|
||||||
|
"un_elevated": "非提升运行游戏(无管理员)"
|
||||||
},
|
},
|
||||||
"downloads": {
|
"downloads": {
|
||||||
|
"grasscutter_fullbuild": "下载 Grasscutter 一体化",
|
||||||
|
"grasscutter_fullquest": "下载 Quest 一体化",
|
||||||
"grasscutter_stable_data": "下载 Grasscutter 稳定版数据",
|
"grasscutter_stable_data": "下载 Grasscutter 稳定版数据",
|
||||||
"grasscutter_latest_data": "下载 Grasscutter 开发版数据",
|
"grasscutter_latest_data": "下载 Grasscutter 开发版数据",
|
||||||
"grasscutter_stable_data_update": "更新 Grasscutter 稳定版数据",
|
"grasscutter_stable_data_update": "更新 Grasscutter 稳定版数据",
|
||||||
"grasscutter_latest_data_update": "更新 Grasscutter 开发版数据",
|
"grasscutter_latest_data_update": "更新 Grasscutter 开发版数据",
|
||||||
"grasscutter_stable": "下载 Grasscutter 稳定版",
|
"grasscutter_unstable": "下载 Grasscutter 稳定版",
|
||||||
"grasscutter_latest": "下载 Grasscutter 开发版",
|
"grasscutter_latest": "下载 Grasscutter 开发版",
|
||||||
"grasscutter_stable_update": "更新 Grasscutter 稳定版",
|
"grasscutter_unstable_update": "更新 Grasscutter 稳定版",
|
||||||
"grasscutter_latest_update": "更新 Grasscutter 开发版",
|
"grasscutter_latest_update": "更新 Grasscutter 开发版",
|
||||||
"resources": "下载 Grasscutter 资源",
|
"resources": "下载 Grasscutter 资源",
|
||||||
"game": "下载游戏"
|
"game": "下载游戏",
|
||||||
|
"aio_header": "多合一下载:",
|
||||||
|
"individual_header": "个别部分下载:",
|
||||||
|
"mods_header": "Mods:",
|
||||||
|
"migoto": "下载 GIMI 3dmigoto"
|
||||||
},
|
},
|
||||||
"download_status": {
|
"download_status": {
|
||||||
"downloading": "下载中",
|
"downloading": "下载中",
|
||||||
@@ -50,6 +61,7 @@
|
|||||||
"select_file": "选择文件或文件夹...",
|
"select_file": "选择文件或文件夹...",
|
||||||
"select_folder": "选择文件夹...",
|
"select_folder": "选择文件夹...",
|
||||||
"download": "下载",
|
"download": "下载",
|
||||||
|
"delete": "删除",
|
||||||
"install": "安装"
|
"install": "安装"
|
||||||
},
|
},
|
||||||
"news": {
|
"news": {
|
||||||
@@ -60,13 +72,17 @@
|
|||||||
"port_help_text": "确保这是 Dispatch 服务器端口,而不是游戏服务器端口。大部分服务器的端口都是 443。",
|
"port_help_text": "确保这是 Dispatch 服务器端口,而不是游戏服务器端口。大部分服务器的端口都是 443。",
|
||||||
"game_help_text": "你不需要另外的游戏备份来使用 Grasscutter。这是给想要降级到 2.6 或没有安装游戏的人使用的。",
|
"game_help_text": "你不需要另外的游戏备份来使用 Grasscutter。这是给想要降级到 2.6 或没有安装游戏的人使用的。",
|
||||||
"gc_stable_jar": "下载当前的 Grasscutter 稳定版,包括 JAR 文件和数据。",
|
"gc_stable_jar": "下载当前的 Grasscutter 稳定版,包括 JAR 文件和数据。",
|
||||||
|
"gc_fullbuild": "下载完整的 Grasscutter 构建版本,包括存储库、jar 和资源。 已完全设置,不需要从此菜单下载任何其他内容",
|
||||||
"gc_dev_jar": "下载最新的 Grasscutter 开发版,包括 JAR 文件和数据。",
|
"gc_dev_jar": "下载最新的 Grasscutter 开发版,包括 JAR 文件和数据。",
|
||||||
"gc_stable_data": "下载当前的 Grasscutter 稳定版数据,不包括 JAR 文件。此选项在更新时有帮助。",
|
"gc_stable_data": "下载当前的 Grasscutter 稳定版数据,不包括 JAR 文件。此选项在更新时有帮助。",
|
||||||
"gc_dev_data": "下载最新的 Grasscutter 开发版数据,不包括 JAR 文件。此选项在更新时有帮助。",
|
"gc_dev_data": "下载最新的 Grasscutter 开发版数据,不包括 JAR 文件。此选项在更新时有帮助。",
|
||||||
|
"encryption": "此项设置通常应该处于关闭状态。",
|
||||||
"resources": "资源文件在运行 Grasscutter 服务器时是必要的。此选项在已经存在资源文件时不可选。",
|
"resources": "资源文件在运行 Grasscutter 服务器时是必要的。此选项在已经存在资源文件时不可选。",
|
||||||
"emergency_metadata": "在出现意外情况时,自动将元数据恢复至原始版本",
|
"emergency_rsa": "在出现意外情况时自动将 RSA 恢复到原始版本",
|
||||||
"use_proxy": "使用 Cultivation 的内置代理。除非你使用 Fiddler 等软件,否则应启用此项。",
|
"use_proxy": "使用 Cultivation 的内置代理。除非你使用 Fiddler 等软件,否则应启用此项。",
|
||||||
"patch_metadata": "自动修改和恢复游戏元数据。除非要游玩旧版本/非官方版本,抑或你已经手动修改了元数据,否则应启用此项。"
|
"patch_rsa": "自动修改和恢复 RSA 补丁。 除非您玩的是旧版/非官方版本,或者您手动修改了 RSA,否则应该启用此功能。",
|
||||||
|
"add_delay": "在 3dmigoto 加载程序中设置延迟! \n这应该可以解决加载问题,但会在启动游戏时加载 3dmigoto 时增加一点延迟。 \n您现在可以再次使用 3dmigoto 启动。",
|
||||||
|
"migoto": "用于从 GameBanana 导入模型"
|
||||||
},
|
},
|
||||||
"swag": {
|
"swag": {
|
||||||
"akebi_name": "Akebi",
|
"akebi_name": "Akebi",
|
||||||
|
|||||||
@@ -14,8 +14,9 @@
|
|||||||
"enabled": "已啟用",
|
"enabled": "已啟用",
|
||||||
"disabled": "未啟用",
|
"disabled": "未啟用",
|
||||||
"game_path": "選擇遊戲安裝路徑",
|
"game_path": "選擇遊戲安裝路徑",
|
||||||
|
"game_command": "遊戲啟動命令",
|
||||||
"game_executable": "選擇遊戲執行檔",
|
"game_executable": "選擇遊戲執行檔",
|
||||||
"recover_metadata": "緊急恢復Metadata",
|
"recover_rsa": "緊急恢復RSA",
|
||||||
"grasscutter_jar": "選擇伺服器JAR檔案",
|
"grasscutter_jar": "選擇伺服器JAR檔案",
|
||||||
"toggle_encryption": "設定加密",
|
"toggle_encryption": "設定加密",
|
||||||
"install_certificate": "安裝代理憑證",
|
"install_certificate": "安裝代理憑證",
|
||||||
@@ -24,20 +25,30 @@
|
|||||||
"language": "語言",
|
"language": "語言",
|
||||||
"background": "選擇自定義背景(網址或檔案)",
|
"background": "選擇自定義背景(網址或檔案)",
|
||||||
"theme": "選擇主題",
|
"theme": "選擇主題",
|
||||||
"patch_metadata": "自動修補Metadata",
|
"patch_rsa": "自動修補RSA",
|
||||||
"use_proxy": "使用內建代理伺服器"
|
"use_proxy": "使用內建代理伺服器",
|
||||||
|
"wipe_login": "擦除登錄緩存",
|
||||||
|
"horny_mode": "Horny模式",
|
||||||
|
"auto_mongodb": "自動啟動 MongoDB",
|
||||||
|
"un_elevated": "在不升高的情况下运行游戏(没有管理员)。"
|
||||||
},
|
},
|
||||||
"downloads": {
|
"downloads": {
|
||||||
|
"grasscutter_fullbuild": "下載Grasscutter多合一下載",
|
||||||
|
"grasscutter_fullquest": "下载 Quest 一体化",
|
||||||
"grasscutter_stable_data": "下載Grasscutter穩定版數據(Data)",
|
"grasscutter_stable_data": "下載Grasscutter穩定版數據(Data)",
|
||||||
"grasscutter_latest_data": "下載Grasscutter開發板數據(Data)",
|
"grasscutter_latest_data": "下載Grasscutter開發板數據(Data)",
|
||||||
"grasscutter_stable_data_update": "更新Grasscutter穩定版數據(Data)",
|
"grasscutter_stable_data_update": "更新Grasscutter穩定版數據(Data)",
|
||||||
"grasscutter_latest_data_update": "更新Grasscutter開發板數據(Data)",
|
"grasscutter_latest_data_update": "更新Grasscutter開發板數據(Data)",
|
||||||
"grasscutter_stable": "下載Grasscutter穩定版",
|
"grasscutter_unstable": "下載Grasscutter穩定版",
|
||||||
"grasscutter_latest": "下載Grasscutter開發板",
|
"grasscutter_latest": "下載Grasscutter開發板",
|
||||||
"grasscutter_stable_update": "更新Grasscutter穩定版",
|
"grasscutter_unstable_update": "更新Grasscutter穩定版",
|
||||||
"grasscutter_latest_update": "更新Grasscutter開發板",
|
"grasscutter_latest_update": "更新Grasscutter開發板",
|
||||||
"resources": "下載Grasscutter資源(Resources)",
|
"resources": "下載Grasscutter資源(Resources)",
|
||||||
"game": "下載遊戲"
|
"game": "下載遊戲",
|
||||||
|
"aio_header": "多合一下載:",
|
||||||
|
"individual_header": "個別部分下載:",
|
||||||
|
"mods_header": "Mods:",
|
||||||
|
"migoto": "下載GIMI 3dmigoto"
|
||||||
},
|
},
|
||||||
"download_status": {
|
"download_status": {
|
||||||
"downloading": "下載中",
|
"downloading": "下載中",
|
||||||
@@ -50,6 +61,7 @@
|
|||||||
"select_file": "選擇檔案或資料夾...",
|
"select_file": "選擇檔案或資料夾...",
|
||||||
"select_folder": "選擇資料夾...",
|
"select_folder": "選擇資料夾...",
|
||||||
"download": "下載",
|
"download": "下載",
|
||||||
|
"delete": "刪除",
|
||||||
"install": "安裝"
|
"install": "安裝"
|
||||||
},
|
},
|
||||||
"news": {
|
"news": {
|
||||||
@@ -60,14 +72,17 @@
|
|||||||
"port_help_text": "確保這是Dispatch伺服器端口,不是遊戲伺服器端口。 大部分伺服器的端口都是443。",
|
"port_help_text": "確保這是Dispatch伺服器端口,不是遊戲伺服器端口。 大部分伺服器的端口都是443。",
|
||||||
"game_help_text": "您不需要另外一個遊戲備份來使用Grasscutter。這是給想要降級到2.6或者還沒安裝遊戲的人使用的。",
|
"game_help_text": "您不需要另外一個遊戲備份來使用Grasscutter。這是給想要降級到2.6或者還沒安裝遊戲的人使用的。",
|
||||||
"gc_stable_jar": "下載當前的Grasscutter穩定版本,包括JAR答案還有資料文件。",
|
"gc_stable_jar": "下載當前的Grasscutter穩定版本,包括JAR答案還有資料文件。",
|
||||||
|
"gc_fullbuild": "下載完整的 Grasscutter 構建版本,包括存儲庫、jar 和資源。 已完全設置,不需要從此菜單下載任何其他內容",
|
||||||
"gc_dev_jar": "下載當前的Grasscutter穩定版本資料文件,其中不會附帶JAR文件。這個選項在更新時很有用。",
|
"gc_dev_jar": "下載當前的Grasscutter穩定版本資料文件,其中不會附帶JAR文件。這個選項在更新時很有用。",
|
||||||
"gc_stable_data": "下載當前最新的Grasscutter開發版本資料文件,其中不會附帶JAR文件。這個選項在更新時很有用。",
|
"gc_stable_data": "下載當前最新的Grasscutter開發版本資料文件,其中不會附帶JAR文件。這個選項在更新時很有用。",
|
||||||
"gc_dev_data": "下載當前最新的Grasscutter開發版本的資料文件,其中不會附帶JAR文件。這個選項在更新時很有用。",
|
"gc_dev_data": "下載當前最新的Grasscutter開發版本的資料文件,其中不會附帶JAR文件。這個選項在更新時很有用。",
|
||||||
"encryption": "在正常情況下,此選項應該被關閉。",
|
"encryption": "在正常情況下,此選項應該被關閉。",
|
||||||
"resources": "資源文件在架設一個Grasscutter伺服器時是必要的。 這個選項會在您已經有裡面有檔案的資源資料夾時不可選。",
|
"resources": "資源文件在架設一個Grasscutter伺服器時是必要的。 這個選項會在您已經有裡面有檔案的資源資料夾時不可選。",
|
||||||
"emergency_metadata": "一旦有東西出了問題,此選項可以把您的Metadata恢復成官方版本。",
|
"emergency_rsa": "一旦有東西出了問題,此選項可以把您的rsa恢復成官方版本。",
|
||||||
"use_proxy": "使用Cultivation內建的代理伺服器。此選項應該被啟用,除非你使用其他的代理伺服器。",
|
"use_proxy": "使用Cultivation內建的代理伺服器。此選項應該被啟用,除非你使用其他的代理伺服器。",
|
||||||
"patch_metadata": "自動修補和恢復Metadata。除非您的遊戲版本是舊的或者是非官方的,此選項應該被啟用。"
|
"patch_rsa": "自動修補和恢復RSA。除非您的遊戲版本是舊的或者是非官方的,此選項應該被啟用。",
|
||||||
|
"add_delay": "在 3dmigoto 加載程序中設置延遲! \n這應該可以解決加載問題,但會在啟動遊戲時加載 3dmigoto 時增加一點延遲。 \n您現在可以再次使用 3dmigoto 啟動。",
|
||||||
|
"migoto": "用於從 GameBanana 導入模型"
|
||||||
},
|
},
|
||||||
"swag": {
|
"swag": {
|
||||||
"akebi_name": "Akebi",
|
"akebi_name": "Akebi",
|
||||||
|
|||||||
@@ -15,27 +15,39 @@
|
|||||||
"disabled": "Deaktiviert",
|
"disabled": "Deaktiviert",
|
||||||
"game_path": "Spielpfad",
|
"game_path": "Spielpfad",
|
||||||
"game_executable": "Spiel Datei auswählen",
|
"game_executable": "Spiel Datei auswählen",
|
||||||
"recover_metadata": "Notfall Wiederherstellung der Metadaten",
|
"recover_rsa": "Notfall Wiederherstellung der RSA",
|
||||||
"grasscutter_jar": "Grasscuter JAR auswählen",
|
"grasscutter_jar": "Grasscuter JAR auswählen",
|
||||||
"toggle_encryption": "Verschlüsselung umschalten",
|
"toggle_encryption": "Verschlüsselung umschalten",
|
||||||
|
"install_certificate": "Installeer proxy certificaat",
|
||||||
"java_path": "Benutzerdefinierten Java Pfad setzen",
|
"java_path": "Benutzerdefinierten Java Pfad setzen",
|
||||||
"grasscutter_with_game": "Grasscutter automatisch mit dem Spiel starten",
|
"grasscutter_with_game": "Grasscutter automatisch mit dem Spiel starten",
|
||||||
"language": "Sprache auswählen",
|
"language": "Sprache auswählen",
|
||||||
"background": "Benutzerdefinierten Hintergrund festlegen (link oder bild)",
|
"background": "Benutzerdefinierten Hintergrund festlegen (link oder bild)",
|
||||||
"theme": "Theme auswählen",
|
"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",
|
||||||
|
"un_elevated": "Führen Sie das Spiel nicht erhöht aus (kein Admin)"
|
||||||
},
|
},
|
||||||
"downloads": {
|
"downloads": {
|
||||||
|
"grasscutter_fullbuild": "Alles in Einem Grasscutter Daten herunterladen",
|
||||||
|
"grasscutter_fullquest": "Alles in Einem Questing Daten herunterladen",
|
||||||
"grasscutter_stable_data": "Stabile Grasscutter Daten herunterladen",
|
"grasscutter_stable_data": "Stabile Grasscutter Daten herunterladen",
|
||||||
"grasscutter_latest_data": "Aktuellste Grasscutter Daten herunterladen",
|
"grasscutter_latest_data": "Aktuellste Grasscutter Daten herunterladen",
|
||||||
"grasscutter_stable_data_update": "Stabile Grasscutter Daten aktualisieren",
|
"grasscutter_stable_data_update": "Stabile Grasscutter Daten aktualisieren",
|
||||||
"grasscutter_latest_data_update": "Aktuellste Grasscutter Daten aktualisieren",
|
"grasscutter_latest_data_update": "Aktuellste Grasscutter Daten aktualisieren",
|
||||||
"grasscutter_stable": "Stabile Grasscutter Version herunterladen",
|
"grasscutter_unstable": "Stabile Grasscutter Version herunterladen",
|
||||||
"grasscutter_latest": "Aktuellste Grasscutter Version herunterladen",
|
"grasscutter_latest": "Aktuellste Grasscutter Version herunterladen",
|
||||||
"grasscutter_stable_update": "Stabile Grasscutter Version aktualisieren",
|
"grasscutter_unstable_update": "Stabile Grasscutter Version aktualisieren",
|
||||||
"grasscutter_latest_update": "Aktuellste Grasscutter Version aktualisieren",
|
"grasscutter_latest_update": "Aktuellste Grasscutter Version aktualisieren",
|
||||||
"resources": "Grasscutter Ressourcen herunterladen",
|
"resources": "Grasscutter Ressourcen herunterladen",
|
||||||
"game": "Spiel herunterladen"
|
"game": "Spiel herunterladen",
|
||||||
|
"aio_header": "Alles in Einem herunterladen",
|
||||||
|
"individual_header": "Einzelne Teile herunterladen:",
|
||||||
|
"mods_header": "Mods:",
|
||||||
|
"migoto": "GIMI 3dmigoto herunterladen"
|
||||||
},
|
},
|
||||||
"download_status": {
|
"download_status": {
|
||||||
"downloading": "Lädt herunter",
|
"downloading": "Lädt herunter",
|
||||||
@@ -48,6 +60,7 @@
|
|||||||
"select_file": "Datei oder Ordner auswählen...",
|
"select_file": "Datei oder Ordner auswählen...",
|
||||||
"select_folder": "Ordner auswählen...",
|
"select_folder": "Ordner auswählen...",
|
||||||
"download": "Herunterladen",
|
"download": "Herunterladen",
|
||||||
|
"delete": "Löschen",
|
||||||
"install": "Installieren"
|
"install": "Installieren"
|
||||||
},
|
},
|
||||||
"news": {
|
"news": {
|
||||||
@@ -58,13 +71,16 @@
|
|||||||
"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'.",
|
"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.",
|
"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_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_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_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.",
|
"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",
|
"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",
|
"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.",
|
||||||
|
"add_delay": "Verzögerung im 3dmigoto-Lader einstellen! \nDies sollte die Ladeprobleme beheben, führt aber zu einer kleinen Verzögerung, wenn 3dmigoto beim Start des Spiels geladen wird. \nSie können nun wieder mit 3dmigoto starten.",
|
||||||
|
"migoto": "Zum Importieren von Modellen von GameBanana"
|
||||||
},
|
},
|
||||||
"swag": {
|
"swag": {
|
||||||
"akebi_name": "Akebi",
|
"akebi_name": "Akebi",
|
||||||
|
|||||||
@@ -3,12 +3,13 @@
|
|||||||
"main": {
|
"main": {
|
||||||
"title": "Cultivation",
|
"title": "Cultivation",
|
||||||
"launch_button": "Launch",
|
"launch_button": "Launch",
|
||||||
"gc_enable": "Connect via Grasscutter",
|
"gc_enable": "Connect to Grasscutter",
|
||||||
"https_enable": "Use HTTPS",
|
"https_enable": "Use HTTPS",
|
||||||
"ip_placeholder": "Server Address...",
|
"ip_placeholder": "Server Address...",
|
||||||
"port_placeholder": "Port...",
|
"port_placeholder": "Port...",
|
||||||
"files_downloading": "Files Downloading: ",
|
"files_downloading": "Files Downloading: ",
|
||||||
"files_extracting": "Files Extracting: "
|
"files_extracting": "Files Extracting: ",
|
||||||
|
"game_path_notify": "Game path not found, remember to set it in !"
|
||||||
},
|
},
|
||||||
"options": {
|
"options": {
|
||||||
"enabled": "Enabled",
|
"enabled": "Enabled",
|
||||||
@@ -16,7 +17,7 @@
|
|||||||
"game_path": "Set Game Install Path",
|
"game_path": "Set Game Install Path",
|
||||||
"game_command": "Game Launch Command",
|
"game_command": "Game Launch Command",
|
||||||
"game_executable": "Set Game Executable",
|
"game_executable": "Set Game Executable",
|
||||||
"recover_metadata": "Emergency Metadata Recovery",
|
"recover_rsa": "Emergency Delete RSA",
|
||||||
"grasscutter_jar": "Set Grasscutter JAR",
|
"grasscutter_jar": "Set Grasscutter JAR",
|
||||||
"toggle_encryption": "Toggle Encryption",
|
"toggle_encryption": "Toggle Encryption",
|
||||||
"install_certificate": "Install Proxy Certificate",
|
"install_certificate": "Install Proxy Certificate",
|
||||||
@@ -25,22 +26,30 @@
|
|||||||
"language": "Select Language",
|
"language": "Select Language",
|
||||||
"background": "Set Custom Background (link or image file)",
|
"background": "Set Custom Background (link or image file)",
|
||||||
"theme": "Set Theme",
|
"theme": "Set Theme",
|
||||||
"patch_metadata": "Automatically Patch Metadata",
|
"patch_rsa": "Automatically Patch RSA",
|
||||||
"use_proxy": "Use Internal Proxy",
|
"use_proxy": "Use Internal Proxy",
|
||||||
"wipe_login": "Wipe Login Cache",
|
"wipe_login": "Wipe Login Cache",
|
||||||
"horny_mode": "Horny Mode"
|
"horny_mode": "Horny Mode",
|
||||||
|
"auto_mongodb": "Automatically Start MongoDB",
|
||||||
|
"un_elevated": "Run the game non-elevated (no admin)"
|
||||||
},
|
},
|
||||||
"downloads": {
|
"downloads": {
|
||||||
|
"grasscutter_fullbuild": "Download Grasscutter All-in-One",
|
||||||
|
"grasscutter_fullquest": "Download Questing All-in-One",
|
||||||
"grasscutter_stable_data": "Download Grasscutter Stable Data",
|
"grasscutter_stable_data": "Download Grasscutter Stable Data",
|
||||||
"grasscutter_latest_data": "Download Grasscutter Latest Data",
|
"grasscutter_latest_data": "Download Grasscutter Latest Data",
|
||||||
"grasscutter_stable_data_update": "Update Grasscutter Stable Data",
|
"grasscutter_stable_data_update": "Update Grasscutter Stable Data",
|
||||||
"grasscutter_latest_data_update": "Update Grasscutter Latest Data",
|
"grasscutter_latest_data_update": "Update Grasscutter Latest Data",
|
||||||
"grasscutter_stable": "Download Grasscutter Stable",
|
"grasscutter_unstable": "Download Grasscutter Questing",
|
||||||
"grasscutter_latest": "Download Grasscutter Latest",
|
"grasscutter_latest": "Download Grasscutter Latest",
|
||||||
"grasscutter_stable_update": "Update Grasscutter Stable",
|
"grasscutter_unstable_update": "Update Grasscutter Questing",
|
||||||
"grasscutter_latest_update": "Update Grasscutter Latest",
|
"grasscutter_latest_update": "Update Grasscutter Latest",
|
||||||
"resources": "Download Grasscutter Resources",
|
"resources": "Download Grasscutter Resources",
|
||||||
"game": "Download Game"
|
"game": "Download Game",
|
||||||
|
"aio_header": "All-in-One Downloads:",
|
||||||
|
"individual_header": "Individual downloads:",
|
||||||
|
"mods_header": "Mods:",
|
||||||
|
"migoto": "Download GIMI 3dmigoto"
|
||||||
},
|
},
|
||||||
"download_status": {
|
"download_status": {
|
||||||
"downloading": "Downloading",
|
"downloading": "Downloading",
|
||||||
@@ -53,6 +62,7 @@
|
|||||||
"select_file": "Select file or folder...",
|
"select_file": "Select file or folder...",
|
||||||
"select_folder": "Select folder...",
|
"select_folder": "Select folder...",
|
||||||
"download": "Download",
|
"download": "Download",
|
||||||
|
"delete": "Delete",
|
||||||
"install": "Install"
|
"install": "Install"
|
||||||
},
|
},
|
||||||
"news": {
|
"news": {
|
||||||
@@ -63,20 +73,23 @@
|
|||||||
"port_help_text": "Ensure this is the Dispatch server port, not the Game server port. This is almost always '443'.",
|
"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.",
|
"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_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_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_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.",
|
"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.",
|
"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",
|
"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",
|
"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.",
|
||||||
|
"add_delay": "Set delay in 3dmigoto loader! \nThis should fix loading issues, but will add a small delay to when 3dmigoto is loaded upon launching the game. \nYou can now launch with 3dmigoto again.",
|
||||||
|
"migoto": "For importing models from GameBanana"
|
||||||
},
|
},
|
||||||
"swag": {
|
"swag": {
|
||||||
"akebi_name": "Akebi",
|
"akebi_name": "Akebi",
|
||||||
"migoto_name": "Migoto",
|
"migoto_name": "Migoto",
|
||||||
"reshade_name": "Reshade",
|
"reshade_name": "Reshade",
|
||||||
"akebi": "Set Akebi Executable",
|
"akebi": "Set Akebi/Acrepi Executable",
|
||||||
"migoto": "Set 3DMigoto Executable",
|
"migoto": "Set 3DMigoto Executable",
|
||||||
"reshade": "Set Reshade Injector"
|
"reshade": "Set Reshade Injector"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -14,8 +14,9 @@
|
|||||||
"enabled": "Activado",
|
"enabled": "Activado",
|
||||||
"disabled": "Desactivado",
|
"disabled": "Desactivado",
|
||||||
"game_path": "Ruta de instalación del juego",
|
"game_path": "Ruta de instalación del juego",
|
||||||
|
"game_command": "Comando de lanzamiento del juego",
|
||||||
"game_executable": "Establecer ejecutable 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",
|
"grasscutter_jar": "Establecer JAR de Grasscutter",
|
||||||
"toggle_encryption": "Alternar Cifrado",
|
"toggle_encryption": "Alternar Cifrado",
|
||||||
"install_certificate": "Instalar Certificado Proxie",
|
"install_certificate": "Instalar Certificado Proxie",
|
||||||
@@ -23,19 +24,31 @@
|
|||||||
"grasscutter_with_game": "Iniciar automáticamente Grasscutter con el juego",
|
"grasscutter_with_game": "Iniciar automáticamente Grasscutter con el juego",
|
||||||
"language": "Seleccionar Idioma",
|
"language": "Seleccionar Idioma",
|
||||||
"background": "Establecer Fondo Personalizado (link o archivo de imagen)",
|
"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",
|
||||||
|
"un_elevated": "Ejecutar el juego no elevado (no admin)"
|
||||||
},
|
},
|
||||||
"downloads": {
|
"downloads": {
|
||||||
|
"grasscutter_fullbuild": "Descargar Datos todo en uno de Grasscutter",
|
||||||
|
"grasscutter_fullquest": "Descargar Datos todo en uno de Questing",
|
||||||
"grasscutter_stable_data": "Descargar Datos Estables de Grasscutter",
|
"grasscutter_stable_data": "Descargar Datos Estables de Grasscutter",
|
||||||
"grasscutter_latest_data": "Descargar Datos más Recientes de Grasscutter",
|
"grasscutter_latest_data": "Descargar Datos más Recientes de Grasscutter",
|
||||||
"grasscutter_stable_data_update": "Actualizar Datos Estables de Grasscutter",
|
"grasscutter_stable_data_update": "Actualizar Datos Estables de Grasscutter",
|
||||||
"grasscutter_latest_data_update": "Actualizar Datos más Recientes de Grasscutter",
|
"grasscutter_latest_data_update": "Actualizar Datos más Recientes de Grasscutter",
|
||||||
"grasscutter_stable": "Descargar Grasscutter Estable",
|
"grasscutter_unstable": "Descargar Grasscutter Estable",
|
||||||
"grasscutter_latest": "Descargar Grasscutter más reciente",
|
"grasscutter_latest": "Descargar Grasscutter más reciente",
|
||||||
"grasscutter_stable_update": "Actualizar Grasscutter Estable",
|
"grasscutter_unstable_update": "Actualizar Grasscutter Estable",
|
||||||
"grasscutter_latest_update": "Actualizar Grasscutter más reciente",
|
"grasscutter_latest_update": "Actualizar Grasscutter más reciente",
|
||||||
"resources": "Descargar Recursos de Grasscutter",
|
"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:",
|
||||||
|
"mods_header": "Mods:",
|
||||||
|
"migoto": "Descargar GIMI 3dmigoto"
|
||||||
},
|
},
|
||||||
"download_status": {
|
"download_status": {
|
||||||
"downloading": "Descargando",
|
"downloading": "Descargando",
|
||||||
@@ -48,6 +61,7 @@
|
|||||||
"select_file": "Seleccionar el archivo o carpeta...",
|
"select_file": "Seleccionar el archivo o carpeta...",
|
||||||
"select_folder": "Seleccionar la carpeta...",
|
"select_folder": "Seleccionar la carpeta...",
|
||||||
"download": "Descargar",
|
"download": "Descargar",
|
||||||
|
"delete": "Borrar",
|
||||||
"install": "Instalar"
|
"install": "Instalar"
|
||||||
},
|
},
|
||||||
"news": {
|
"news": {
|
||||||
@@ -58,10 +72,13 @@
|
|||||||
"port_help_text": "Asegúrese de que este sea el Dispatch server port, no el Game server port. Este es casi siempre '443'.",
|
"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.",
|
"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_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_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_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.",
|
"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.",
|
||||||
"resources": "Estos también son necesarios para ejecutar un servidor Grasscutter. Este botón estará gris si tiene una carpeta de recursos existente con contenido dentro."
|
"resources": "Estos también son necesarios para ejecutar un servidor Grasscutter. Este botón estará gris si tiene una carpeta de recursos existente con contenido dentro.",
|
||||||
|
"add_delay": "¡Retraso en el cargador de 3dmigoto! \nEsto debería solucionar los problemas de carga, pero añadirá un pequeño retraso cuando 3dmigoto se cargue al iniciar el juego. \nAhora puede iniciar con 3dmigoto de nuevo.",
|
||||||
|
"migoto": "Para importar modelos de GameBanana"
|
||||||
},
|
},
|
||||||
"swag": {
|
"swag": {
|
||||||
"akebi": "Establecer el ejecutable de Akebi"
|
"akebi": "Establecer el ejecutable de Akebi"
|
||||||
|
|||||||
@@ -11,51 +11,82 @@
|
|||||||
"files_extracting": "Fichiers en cours d'extraction: "
|
"files_extracting": "Fichiers en cours d'extraction: "
|
||||||
},
|
},
|
||||||
"options": {
|
"options": {
|
||||||
"enabled": "active",
|
"enabled": "Activé",
|
||||||
"disabled": "desactiver",
|
"disabled": "Désractivé",
|
||||||
"game_executable": "definir l'executable du jeu",
|
"game_path": "Définir le chemin d'installation du jeu",
|
||||||
"grasscutter_jar": "definir le Jar Grasscutter",
|
"game_command": "Commande de lancement du jeu",
|
||||||
"toggle_encryption": "activer l'encryption",
|
"game_executable": "Définir l'executable du jeu",
|
||||||
"java_path": "definir un chemin java personnalise",
|
"recover_rsa": "Récupération d'urgence des RSA",
|
||||||
|
"grasscutter_jar": "Définir le Jar Grasscutter",
|
||||||
|
"toggle_encryption": "Activer l'encryption",
|
||||||
|
"install_certificate": "Installer le certificat du proxy",
|
||||||
|
"java_path": "Définir un chemin java personnalise",
|
||||||
"grasscutter_with_game": "Lancer Grasscutter automatiquement avec le jeu",
|
"grasscutter_with_game": "Lancer Grasscutter automatiquement avec le jeu",
|
||||||
"language": "Choisir la langue",
|
"language": "Choisir la langue",
|
||||||
"background": "definir un arriere plan personnalise (lien ou fichier image)",
|
"background": "Définir un arriere plan personnalisé (lien ou fichier image)",
|
||||||
"theme": "definir un theme"
|
"theme": "Définir un theme",
|
||||||
|
"patch_rsa": "Patcher automatiquement les clés RSA",
|
||||||
|
"use_proxy": "Utiliser le proxy interne",
|
||||||
|
"wipe_login": "Effacer le cache de connexion",
|
||||||
|
"horny_mode": "Mode horny",
|
||||||
|
"auto_mongodb": "Démarrer automatiquement MongoDB",
|
||||||
|
"un_elevated": "Exécuter le jeu sans élévation (pas d'administrateur)"
|
||||||
},
|
},
|
||||||
"downloads": {
|
"downloads": {
|
||||||
"grasscutter_stable_data": "Telecharger les donnees de Grasscutter (version stable)",
|
"grasscutter_fullbuild": "Telecharger Grasscutter tout-en-un",
|
||||||
"grasscutter_latest_data": "Telecharger les donnees de Grasscutter (derniere version)",
|
"grasscutter_fullquest": "Télécharger les Quêtes tout-en-un",
|
||||||
"grasscutter_stable_data_update": "Mettre a jour les donnees de Grasscutter (version stable)",
|
"grasscutter_stable_data": "Télécharger les donnees de Grasscutter (version stable)",
|
||||||
"grasscutter_latest_data_update": "Mettre a jour les donnees de Grasscutter (derniere version)",
|
"grasscutter_latest_data": "Télécharger les donnees de Grasscutter (derniere version)",
|
||||||
"grasscutter_stable": "Telecharger Grasscutter (version stable)",
|
"grasscutter_stable_data_update": "Mettre à jour les données de Grasscutter (version stable)",
|
||||||
"grasscutter_latest": "Telecharger Grasscutter (derniere version)",
|
"grasscutter_latest_data_update": "Mettre à jour les données de Grasscutter (derniere version)",
|
||||||
"grasscutter_stable_update": "Mettre a jour Grasscutter (version stable)",
|
"grasscutter_unstable": "Télécharger Grasscutter (version stable)",
|
||||||
"grasscutter_latest_update": "Mettre a jour Grasscutter (derniere version)",
|
"grasscutter_latest": "Télécharger Grasscutter (derniere version)",
|
||||||
"resources": "Telecharger les ressources logicielles de Grasscutter"
|
"grasscutter_unstable_update": "Mettre à jour Grasscutter (version stable)",
|
||||||
|
"grasscutter_latest_update": "Mettre à jour Grasscutter (derniere version)",
|
||||||
|
"resources": "Telecharger les ressources de Grasscutter",
|
||||||
|
"aio_header": "Telechargements tout-en-un:",
|
||||||
|
"individual_header": "Telechargements individuels:",
|
||||||
|
"mods_header": "Mods:",
|
||||||
|
"migoto": "Telecharger GIMI 3dmigoto"
|
||||||
},
|
},
|
||||||
"download_status": {
|
"download_status": {
|
||||||
"downloading": "Telechargement",
|
"downloading": "Téléchargement",
|
||||||
"extracting": "Extraction",
|
"extracting": "Extraction",
|
||||||
"error": "Erreur",
|
"error": "Erreur",
|
||||||
"finished": "Termine",
|
"finished": "Terminé",
|
||||||
"stopped": "Arrete"
|
"stopped": "Arrêté"
|
||||||
},
|
},
|
||||||
"components": {
|
"components": {
|
||||||
"select_file": "choisir fichier ou dossier...",
|
"select_file": "Choisir un fichier ou un dossier...",
|
||||||
"select_folder": "choisir dossier...",
|
"select_folder": "Choisir un dossier...",
|
||||||
"download": "Telecharger"
|
"download": "Télécharger",
|
||||||
|
"delete": "Supprimer",
|
||||||
|
"install": "Installer"
|
||||||
},
|
},
|
||||||
"news": {
|
"news": {
|
||||||
"latest_commits": "Recents Commits",
|
"latest_commits": "Commits récents",
|
||||||
"latest_version": "Derniere version"
|
"latest_version": "Dernière version"
|
||||||
},
|
},
|
||||||
"help": {
|
"help": {
|
||||||
"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'.",
|
"port_help_text": "Assurez-vous que c'est le port du serveur de 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",
|
"game_help_text": "Il n'y a pas besoin d'une copie differente du jeu pour jouer avec Grasscutter. C'est utile pour downgrade 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_stable_jar": "Télécharge le dernier build stable de Grasscutter, ce qui inclut le fichier jar et les fichiers de données",
|
||||||
"gc_dev_jar": "Telecharger le dernier build en development de Grasscutter, ce qui inclut le fichier jar et les fichiers de donnees",
|
"gc_fullbuild": "Télécharge 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_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_jar": "Télécharge le dernier build de development de Grasscutter, ce qui inclut le fichier jar et les fichiers de données",
|
||||||
"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",
|
"gc_stable_data": "Télécharge le dernier build stable de Grasscutter, ce qui n'inclut pasle fichier jar. Cela est utile pour mettre a jour",
|
||||||
"resources": "Les ressources sont aussi necessaires pour lancer un serveur Grasscutter. Ce bouton deviendra gris si vous avez deja un fichier ressources avec les donnees dedans."
|
"gc_dev_data": "Télécharge le dernier build de development de Grasscutter, ce qui n'inclut pasle fichier jar. Cela est utile pour mettre a jour",
|
||||||
|
"resources": "Les ressources sont aussi necessaires pour lancer un serveur Grasscutter. Ce bouton deviendra gris si vous avez deja un fichier ressources avec les donnees dedans.",
|
||||||
|
"add_delay": "Définit le délai du chargement de 3dmigoto ! \nCela devrait résoudre les problèmes de chargement, mais ajoutera un petit délai au chargement de 3dmigoto lors du lancement du jeu. \nVous pouvez maintenant lancer le jeu avec 3dmigoto.",
|
||||||
|
"use_proxy": "Active le proxy interne de Cultivation. Il faut activer cette option si un logiciel tel que Fiddler n'est pas installé.",
|
||||||
|
"patch_rsa": "Patch et dépatche les clés RSA du jeu automatiquement. A moins de jouer avec d'anciennes versons ou des versions non officielles (3.0 ou plus ancien), cette option doit être activée.",
|
||||||
|
"migoto": "Pour importer des modèles depuis GameBanana"
|
||||||
|
},
|
||||||
|
"swag": {
|
||||||
|
"akebi_name": "Akebi",
|
||||||
|
"migoto_name": "Migoto",
|
||||||
|
"reshade_name": "Reshade",
|
||||||
|
"akebi": "Définir l'exécutable d'Akebid'aAcrepi",
|
||||||
|
"migoto": "Définir l'exécutable de 3DMigoto",
|
||||||
|
"reshade": "Définir l'injecteur de Reshade"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,24 +10,43 @@
|
|||||||
"files_extracting": "MengExtract File: "
|
"files_extracting": "MengExtract File: "
|
||||||
},
|
},
|
||||||
"options": {
|
"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",
|
"grasscutter_jar": "Path ke Grasscutter JAR",
|
||||||
|
"toggle_encryption": "Alihkan enkripsi",
|
||||||
|
"install_certificate": "Instal sertifikat proxy",
|
||||||
"java_path": "Atur kustom Java path",
|
"java_path": "Atur kustom Java path",
|
||||||
"grasscutter_with_game": "Otomatis Menjalankan Grasscutter Dengan Game",
|
"grasscutter_with_game": "Otomatis Menjalankan Grasscutter Dengan Game",
|
||||||
"language": "Pilih Bahasa",
|
"language": "Pilih Bahasa",
|
||||||
"background": "Atur Kustom Latar Belakang (link atau gambar file)",
|
"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",
|
||||||
|
"un_elevated": "Jalankan game yang tidak ditinggikan (tanpa admin)"
|
||||||
},
|
},
|
||||||
"downloads": {
|
"downloads": {
|
||||||
|
"grasscutter_fullbuild": "Sedang Mendownload Grasscutter Semua Dalam Satu",
|
||||||
|
"grasscutter_fullquest": "Unduh pencarian semua dalam satu",
|
||||||
"grasscutter_stable_data": "Sedang Mendownload Grasscutter Versi Stabil",
|
"grasscutter_stable_data": "Sedang Mendownload Grasscutter Versi Stabil",
|
||||||
"grasscutter_latest_data": "Sedang Mendownload Grasscutter Data Terbaru",
|
"grasscutter_latest_data": "Sedang Mendownload Grasscutter Data Terbaru",
|
||||||
"grasscutter_stable_data_update": "Memperbaharui Grasscutter Data Stabil",
|
"grasscutter_stable_data_update": "Memperbaharui Grasscutter Data Stabil",
|
||||||
"grasscutter_latest_data_update": "Memperbaharui Grasscutter Data Terbaru",
|
"grasscutter_latest_data_update": "Memperbaharui Grasscutter Data Terbaru",
|
||||||
"grasscutter_stable": "Download Grasscutter Stabil Version ",
|
"grasscutter_unstable": "Download Grasscutter Stabil Version ",
|
||||||
"grasscutter_latest": "Download Grasscutter Terbaru Version",
|
"grasscutter_latest": "Download Grasscutter Terbaru Version",
|
||||||
"grasscutter_stable_update": "Sedang MengUpdate Grasscutter Stabil",
|
"grasscutter_unstable_update": "Sedang MengUpdate Grasscutter Stabil",
|
||||||
"grasscutter_latest_update": "Sedang MengUpdate Grasscutter Terbaru",
|
"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:",
|
||||||
|
"mods_header": "Mods:",
|
||||||
|
"migoto": "Sedang Mendownload GIMI 3dmigoto"
|
||||||
},
|
},
|
||||||
"download_status": {
|
"download_status": {
|
||||||
"downloading": "Sedang Mendownload",
|
"downloading": "Sedang Mendownload",
|
||||||
@@ -39,7 +58,8 @@
|
|||||||
"components": {
|
"components": {
|
||||||
"select_file": "Pilih File Atau Folder...",
|
"select_file": "Pilih File Atau Folder...",
|
||||||
"select_folder": "Pilih Folder...",
|
"select_folder": "Pilih Folder...",
|
||||||
"download": "download"
|
"download": "download",
|
||||||
|
"delete": "Menghapus"
|
||||||
},
|
},
|
||||||
"news": {
|
"news": {
|
||||||
"latest_commits": "Commit Terbaru",
|
"latest_commits": "Commit Terbaru",
|
||||||
@@ -49,9 +69,12 @@
|
|||||||
"port_help_text": "Pastikan ini adalah port server port, bukan port server Game. ini Hampir Selalu '443'.",
|
"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..",
|
"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_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_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_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.",
|
"gc_dev_data": "Unduh file data Grasscutter Development saat ini, dimana Tidak Ada JAR file. Ini Berguna Untuk memperbarui.",
|
||||||
"resources": "Ini juga diperlukan untuk menjalankan server Grasscutter. Tombol ini akan berwarna abu-abu jika Anda memiliki folder Resource yang ada dengan File di dalamnya"
|
"resources": "Ini juga diperlukan untuk menjalankan server Grasscutter. Tombol ini akan berwarna abu-abu jika Anda memiliki folder Resource yang ada dengan File di dalamnya",
|
||||||
|
"add_delay": "Atur penundaan di pemuat 3dmigoto! \nIni akan memperbaiki masalah pemuatan, tetapi akan menambah sedikit penundaan ketika 3dmigoto dimuat saat meluncurkan game. \nAnda sekarang dapat meluncurkan dengan 3dmigoto lagi.",
|
||||||
|
"migoto": "Untuk mengimpor model dari GameBanana"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
95
src-tauri/lang/ko.json
Normal file
95
src-tauri/lang/ko.json
Normal file
@@ -0,0 +1,95 @@
|
|||||||
|
{
|
||||||
|
"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 자동 시작",
|
||||||
|
"un_elevated": "게임 비상승 실행(관리자 없음)"
|
||||||
|
},
|
||||||
|
"downloads": {
|
||||||
|
"grasscutter_fullbuild": "올인원 Grasscutter 다운로드",
|
||||||
|
"grasscutter_fullquest": "퀘스트 올인원 다운로드",
|
||||||
|
"grasscutter_stable_data": "안정적인 데이터 다운로드",
|
||||||
|
"grasscutter_latest_data": "최신 데이터 다운로드",
|
||||||
|
"grasscutter_stable_data_update": "안정적 데이터 업데이트",
|
||||||
|
"grasscutter_latest_data_update": "최신 데이터 업데이트",
|
||||||
|
"grasscutter_unstable": "안정 다운로드",
|
||||||
|
"grasscutter_latest": "최신 다운로드",
|
||||||
|
"grasscutter_unstable_update": "안정 업데이트",
|
||||||
|
"grasscutter_latest_update": "최신 업데이트",
|
||||||
|
"resources": "리소스 다운로드",
|
||||||
|
"game": "게임 다운로드",
|
||||||
|
"aio_header": "올인원 다운로드",
|
||||||
|
"individual_header": "개별 부품 다운로드:",
|
||||||
|
"mods_header": "Mods:",
|
||||||
|
"migoto": "GIMI 3dmigoto 다운로드"
|
||||||
|
},
|
||||||
|
"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를 수동으로 패치하지 않은 경우 이 기능을 활성화해야 합니다.",
|
||||||
|
"add_delay": "3dmigoto 로더에서 지연을 설정하세요! \n이렇게하면 로딩 문제가 해결되지만 게임을 시작할 때 3dmigoto가로드되는시기에 약간의 지연이 추가됩니다. \n이제 3dmigoto로 다시 시작할 수 있습니다.",
|
||||||
|
"migoto": "GameBanana에서 모델 가져오기"
|
||||||
|
},
|
||||||
|
"swag": {
|
||||||
|
"akebi_name": "Akebi",
|
||||||
|
"migoto_name": "Migoto",
|
||||||
|
"reshade_name": "Reshade",
|
||||||
|
"akebi": "Akebi 실행 파일 설정",
|
||||||
|
"migoto": "3DMigoto 실행 파일 설정",
|
||||||
|
"reshade": "Reshade 인젝터 설정"
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -14,24 +14,38 @@
|
|||||||
"enabled": "Iespējots",
|
"enabled": "Iespējots",
|
||||||
"disabled": "Atspējots",
|
"disabled": "Atspējots",
|
||||||
"game_executable": "Iestatīt spēles izpildāmu",
|
"game_executable": "Iestatīt spēles izpildāmu",
|
||||||
|
"recover_rsa": "Avārijas RSA atjaunošana",
|
||||||
"grasscutter_jar": "Iestatiet Grasscutter JAR",
|
"grasscutter_jar": "Iestatiet Grasscutter JAR",
|
||||||
"toggle_encryption": "Pārslēgt Šifrēšanu",
|
"toggle_encryption": "Pārslēgt Šifrēšanu",
|
||||||
|
"install_certificate": "Proxy sertifikāta instalēšana",
|
||||||
"java_path": "Iestatiet pielāgotu Java ceļu",
|
"java_path": "Iestatiet pielāgotu Java ceļu",
|
||||||
"grasscutter_with_game": "Automātiski palaidiet Grasscutter ar spēli",
|
"grasscutter_with_game": "Automātiski palaidiet Grasscutter ar spēli",
|
||||||
"language": "Izvēlēties valodu",
|
"language": "Izvēlēties valodu",
|
||||||
"background": "Iestatīt pielāgotu fonu (saite vai attēla fails)",
|
"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",
|
||||||
|
"un_elevated": "Palaist spēli bez paaugstinājuma (bez administratora)"
|
||||||
},
|
},
|
||||||
"downloads": {
|
"downloads": {
|
||||||
|
"grasscutter_fullbuild": "Lejupielādējiet Grasscutter viss vienā",
|
||||||
|
"grasscutter_fullquest": "Lejupielādēt questing viss vienā",
|
||||||
"grasscutter_stable_data": "Lejupielādējiet Grasscutter stabilos datus",
|
"grasscutter_stable_data": "Lejupielādējiet Grasscutter stabilos datus",
|
||||||
"grasscutter_latest_data": "Lejupielādējiet Grasscutter jaunākos datus",
|
"grasscutter_latest_data": "Lejupielādējiet Grasscutter jaunākos datus",
|
||||||
"grasscutter_stable_data_update": "Atjauniniet Grasscutter stabilos datus",
|
"grasscutter_stable_data_update": "Atjauniniet Grasscutter stabilos datus",
|
||||||
"grasscutter_latest_data_update": "Atjauniniet Grasscutter jaunākos datus",
|
"grasscutter_latest_data_update": "Atjauniniet Grasscutter jaunākos datus",
|
||||||
"grasscutter_stable": "Lejupielādēt Grasscutter stabilo",
|
"grasscutter_unstable": "Lejupielādēt Grasscutter stabilo",
|
||||||
"grasscutter_latest": "Lejupielādēt Grasscutter jaunāko",
|
"grasscutter_latest": "Lejupielādēt Grasscutter jaunāko",
|
||||||
"grasscutter_stable_update": "Atjauniet Grasscutter stabilo",
|
"grasscutter_unstable_update": "Atjauniet Grasscutter stabilo",
|
||||||
"grasscutter_latest_update": "Atjauniet Grasscutter jaunāko",
|
"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:",
|
||||||
|
"mods_header": "Mods:",
|
||||||
|
"migoto": "Lejupielādēt GIMI 3dmigoto"
|
||||||
},
|
},
|
||||||
"download_status": {
|
"download_status": {
|
||||||
"downloading": "Notiek lejupielāde",
|
"downloading": "Notiek lejupielāde",
|
||||||
@@ -43,7 +57,8 @@
|
|||||||
"components": {
|
"components": {
|
||||||
"select_file": "Izvēlēties failu vai mapu...",
|
"select_file": "Izvēlēties failu vai mapu...",
|
||||||
"select_folder": "Izvēlēties mapu...",
|
"select_folder": "Izvēlēties mapu...",
|
||||||
"download": "Lejupielādēt"
|
"download": "Lejupielādēt",
|
||||||
|
"delete": "Dzēst"
|
||||||
},
|
},
|
||||||
"news": {
|
"news": {
|
||||||
"latest_commits": "Nesen kommitus",
|
"latest_commits": "Nesen kommitus",
|
||||||
@@ -53,9 +68,12 @@
|
|||||||
"port_help_text": "Pārliecinieties, vai tas ir Dispatch-servera ports, nevis spēļu servera ports. Tas gandrīz vienmēr ir '443'.",
|
"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.",
|
"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_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_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_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.",
|
"gc_dev_data": "Lejupielādējiet jaunāko izstrāde Grasscutter datu failus, kuriem nav pievienots jar fails. Tas ir noderīgi atjaunināšanai.",
|
||||||
"resources": "Tie ir nepieciešami arī Grasscutter servera darbināšanai. Šī poga būs pelēka, ja jums ir resursu mape ar saturu."
|
"resources": "Tie ir nepieciešami arī Grasscutter servera darbināšanai. Šī poga būs pelēka, ja jums ir resursu mape ar saturu.",
|
||||||
|
"add_delay": "Iestatiet kavēšanos 3dmigoto iekrāvē! \nTam vajadzētu novērst ielādes problēmas, bet tas nedaudz aizkavēs 3dmigoto ielādēšanu, uzsākot spēli. \nTagad atkal varat sākt ar 3dmigoto.",
|
||||||
|
"migoto": "Modeļu importēšanai no GameBanana"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -15,7 +15,7 @@
|
|||||||
"disabled": "Uitgeschakeld",
|
"disabled": "Uitgeschakeld",
|
||||||
"game_path": "Spel Installatie Pad Instellen",
|
"game_path": "Spel Installatie Pad Instellen",
|
||||||
"game_executable": "Stel De Exe Van Het Spel In",
|
"game_executable": "Stel De Exe Van Het Spel In",
|
||||||
"recover_metadata": "Noodherstel Van De Metadata",
|
"recover_rsa": "Noodherstel Van De RSA",
|
||||||
"grasscutter_jar": "Stel De Grasscutter JAR In",
|
"grasscutter_jar": "Stel De Grasscutter JAR In",
|
||||||
"toggle_encryption": "Versleuteling Inschakelen",
|
"toggle_encryption": "Versleuteling Inschakelen",
|
||||||
"install_certificate": "Proxy-certificaat installeren",
|
"install_certificate": "Proxy-certificaat installeren",
|
||||||
@@ -24,20 +24,30 @@
|
|||||||
"language": "Selecteer Taal",
|
"language": "Selecteer Taal",
|
||||||
"background": "Aangepaste Achtergrond Instellen (link of afbeeldingsbestand)",
|
"background": "Aangepaste Achtergrond Instellen (link of afbeeldingsbestand)",
|
||||||
"theme": "Thema instellen",
|
"theme": "Thema instellen",
|
||||||
"patch_metadata": "Metadata Automatisch Bijwerken",
|
"patch_rsa": "RSA Automatisch Bijwerken",
|
||||||
"use_proxy": "Gebruik Interne Proxy"
|
"use_proxy": "Gebruik Interne Proxy",
|
||||||
|
"wipe_login": "Login cache wissen",
|
||||||
|
"horny_mode": "Geile modus",
|
||||||
|
"auto_mongodb": "Start automatisch MongoDB",
|
||||||
|
"un_elevated": "Voer het spel uit zonder hoogtevrees (geen admin)"
|
||||||
},
|
},
|
||||||
"downloads": {
|
"downloads": {
|
||||||
|
"grasscutter_fullbuild": "Grasscutter Alles-in-één Downloaden",
|
||||||
|
"grasscutter_fullquest": "Alles-in-één zoeken downloaden",
|
||||||
"grasscutter_stable_data": "Download Stabiele Gegevens Van Grasscutter",
|
"grasscutter_stable_data": "Download Stabiele Gegevens Van Grasscutter",
|
||||||
"grasscutter_latest_data": "Download De Nieuwste Gegevens Van Grasscutter",
|
"grasscutter_latest_data": "Download De Nieuwste Gegevens Van Grasscutter",
|
||||||
"grasscutter_stable_data_update": "Stabiele gegevens Van Grasscutter bijwerken",
|
"grasscutter_stable_data_update": "Stabiele gegevens Van Grasscutter bijwerken",
|
||||||
"grasscutter_latest_data_update": "Nieuwste gegevens Van Grasscutter bijwerken",
|
"grasscutter_latest_data_update": "Nieuwste gegevens Van Grasscutter bijwerken",
|
||||||
"grasscutter_stable": "Download Stabiele Versie Van Grasscutter",
|
"grasscutter_unstable": "Download Stabiele Versie Van Grasscutter",
|
||||||
"grasscutter_latest": "Download De Nieuwste Versie Van Grasscutter",
|
"grasscutter_latest": "Download De Nieuwste Versie Van Grasscutter",
|
||||||
"grasscutter_stable_update": "Update Grasscutter Naar De Stabiele Versie",
|
"grasscutter_unstable_update": "Update Grasscutter Naar De Stabiele Versie",
|
||||||
"grasscutter_latest_update": "Update Grasscutter Naar De Nieuwste Versie",
|
"grasscutter_latest_update": "Update Grasscutter Naar De Nieuwste Versie",
|
||||||
"resources": "Download Grasscutter bronnen",
|
"resources": "Download Grasscutter bronnen",
|
||||||
"game": "Download Spel"
|
"game": "Download Spel",
|
||||||
|
"aio_header": "Alles-in-één Downloads:",
|
||||||
|
"individual_header": "Downloads van afzonderlijke onderdelen:",
|
||||||
|
"mods_header": "Mods:",
|
||||||
|
"migoto": "Download GIMI 3dmigoto"
|
||||||
},
|
},
|
||||||
"download_status": {
|
"download_status": {
|
||||||
"downloading": "Aan Het Downloading",
|
"downloading": "Aan Het Downloading",
|
||||||
@@ -50,6 +60,7 @@
|
|||||||
"select_file": "Select file or folder...",
|
"select_file": "Select file or folder...",
|
||||||
"select_folder": "Select folder...",
|
"select_folder": "Select folder...",
|
||||||
"download": "Download",
|
"download": "Download",
|
||||||
|
"delete": "Verwijder",
|
||||||
"install": "Install"
|
"install": "Install"
|
||||||
},
|
},
|
||||||
"news": {
|
"news": {
|
||||||
@@ -60,14 +71,17 @@
|
|||||||
"port_help_text": "Zorg ervoor dat dit de Dispatch server poort is, niet de Game server poort. Dit is bijna altijd '443'.",
|
"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.",
|
"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_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_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_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.",
|
"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.",
|
"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",
|
"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_metadata": "Voor het geval er iets fout is gegaan, herstel uw metadata naar de laatste offici<63>le versies metadata.",
|
"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",
|
"use_proxy": "Gebruik de Cultivation interne proxy. U zou dit ingeschakeld moeten hebben, tenzij u iets als Fiddler gebruikt",
|
||||||
"patch_metadata": "Patch en unpatch je spel metadata automatisch. Tenzij je met oude/niet-offici<63>le versies speelt, of je hebt je metadata handmatig gepatcht, zou dit ingeschakeld moeten zijn."
|
"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.",
|
||||||
|
"add_delay": "Vertraging instellen in 3dmigoto loader! \nDit zou laadproblemen moeten oplossen, maar zal een kleine vertraging toevoegen aan het laden van 3dmigoto bij het opstarten van het spel. \nJe kunt nu weer starten met 3dmigoto.",
|
||||||
|
"migoto": "Voor het importeren van modellen uit GameBanana"
|
||||||
},
|
},
|
||||||
"swag": {
|
"swag": {
|
||||||
"akebi_name": "Akebi",
|
"akebi_name": "Akebi",
|
||||||
|
|||||||
95
src-tauri/lang/pt-br.json
Normal file
95
src-tauri/lang/pt-br.json
Normal file
@@ -0,0 +1,95 @@
|
|||||||
|
{
|
||||||
|
"lang_name": "Português Brasileiro",
|
||||||
|
"main": {
|
||||||
|
"title": "Cultivation: Edição Thorny",
|
||||||
|
"launch_button": "Iniciar",
|
||||||
|
"gc_enable": "Conectar ao Grasscutter",
|
||||||
|
"https_enable": "Usar HTTPS",
|
||||||
|
"ip_placeholder": "Endereço do Servidor...",
|
||||||
|
"port_placeholder": "Porta...",
|
||||||
|
"files_downloading": "Baixando Arquivos: ",
|
||||||
|
"files_extracting": "Extraindo Arquivos: "
|
||||||
|
},
|
||||||
|
"options": {
|
||||||
|
"enabled": "Habilitado",
|
||||||
|
"disabled": "Desabilitado",
|
||||||
|
"game_path": "Definir o Local de Instalação do Jogo",
|
||||||
|
"game_command": "Comando de Iniciação do Jogo",
|
||||||
|
"game_executable": "Definir o Executavel do Jogo",
|
||||||
|
"recover_rsa": "Exclusão de Emergencia de RSA",
|
||||||
|
"grasscutter_jar": "Definir o arquivo JAR do Grasscutter",
|
||||||
|
"toggle_encryption": "Ativar/Desativar Criptografia",
|
||||||
|
"install_certificate": "Instalar o Certificado de Proxy",
|
||||||
|
"java_path": "Definir um Local Customizado do Java",
|
||||||
|
"grasscutter_with_game": "Iniciar automaticamente o Grasscutter com o Jogo",
|
||||||
|
"language": "Selecionar Idioma",
|
||||||
|
"background": "Definir Fundo Customizado (link ou arquivo de imagem)",
|
||||||
|
"theme": "Definir Tema",
|
||||||
|
"patch_rsa": "Automaticamente Corrigir RSA",
|
||||||
|
"use_proxy": "Usar Proxy Interno",
|
||||||
|
"wipe_login": "Limpar Cache de Login",
|
||||||
|
"horny_mode": "Modo com tesão",
|
||||||
|
"auto_mongodb": "Iniciar MongoDB Automaticamente",
|
||||||
|
"un_elevated": "Executar o jogo não-elevated (sem admin)"
|
||||||
|
},
|
||||||
|
"downloads": {
|
||||||
|
"grasscutter_fullbuild": "Baixar o Grasscutter Tudo-em-Um",
|
||||||
|
"grasscutter_fullquest": "Baixar de missões em um só lugar",
|
||||||
|
"grasscutter_stable_data": "Baixar os Dados do Grasscutter Estável",
|
||||||
|
"grasscutter_latest_data": "Baixar os Dados do Grasscutter Mais Recente",
|
||||||
|
"grasscutter_stable_data_update": "Atualizar os Dados do Grasscutter Estável",
|
||||||
|
"grasscutter_latest_data_update": "Atualizar os Dados do Grasscutter Mais Recente",
|
||||||
|
"grasscutter_unstable": "Baixar o Grasscutter Estável",
|
||||||
|
"grasscutter_latest": "Baixar o Grasscutter Mais Recente",
|
||||||
|
"grasscutter_unstable_update": "Atualizar o Grasscutter Estável",
|
||||||
|
"grasscutter_latest_update": "Atualizar o Grasscutter Mais Recente",
|
||||||
|
"resources": "Baixar os Recursos do Grasscutter ",
|
||||||
|
"game": "Baixar o Jogo",
|
||||||
|
"aio_header": "Downloads Tudo-em-Um:",
|
||||||
|
"individual_header": "Downloads Individuais:",
|
||||||
|
"mods_header": "Mods:",
|
||||||
|
"migoto": "Baixar o GIMI 3dmigoto"
|
||||||
|
},
|
||||||
|
"download_status": {
|
||||||
|
"downloading": "Baixando",
|
||||||
|
"extracting": "Extraindo",
|
||||||
|
"error": "Erro",
|
||||||
|
"finished": "Finalizado",
|
||||||
|
"stopped": "Parado"
|
||||||
|
},
|
||||||
|
"components": {
|
||||||
|
"select_file": "Selecione o arquivo ou pasta...",
|
||||||
|
"select_folder": "Selecione a pasta...",
|
||||||
|
"download": "Baixar",
|
||||||
|
"delete": "Deletar",
|
||||||
|
"install": "Instalar"
|
||||||
|
},
|
||||||
|
"news": {
|
||||||
|
"latest_commits": "Commits Recentes",
|
||||||
|
"latest_version": "Versão mais Recente"
|
||||||
|
},
|
||||||
|
"help": {
|
||||||
|
"port_help_text": "Certifique-se de que esta é a porta do servidor dispatch, não a porta do jogo. Ela é quase sempre '443'.",
|
||||||
|
"game_help_text": "Você não precisa de uma cópia do jogo separada para jogar com o Grasscutter. Isso é para, ou desatualizar para a 2.6, ou se você não tiver o jogo instalado.",
|
||||||
|
"gc_stable_jar": "Baixar a versão atual do Grasscutter estável, que inclui o arquivo jar e os arquivos de dados.",
|
||||||
|
"gc_fullbuild": "Baixar uma versão completa do Grasscutter, incluindo a repo, jar e recursos. Ela está totalmente configurada e não requer nenhum outro download deste menu.",
|
||||||
|
"gc_dev_jar": "Baixar a versão de desenvolvimento mais recente do Grasscutter, que inclui o arquivo jar e os arquivos de dados.",
|
||||||
|
"gc_stable_data": "Baixar os arquivos de dados da versão atual do Grasscutter estável, que não inclui o arquivo jar. Isso é útil para atualizações.",
|
||||||
|
"gc_dev_data": "Baixar os arquivos de dados da versão de desenvolvimento mais recente do Grasscutter, que não vem com um arquivo jar. Isso é útil para atualizações.",
|
||||||
|
"encryption": "Isso normalmente deve estar desativado.",
|
||||||
|
"resources": "Esses também são necessários para usar o Grasscutter. Esse botão ficará cinza caso você já tenha uma pasta resources com coisas dentro.",
|
||||||
|
"emergency_rsa": "Caso algo dê errado, força a exclusão da correção RSA.",
|
||||||
|
"use_proxy": "Usa o proxy interno do Cultivation. Isso deveria estar habilitado a não ser que você utilize algo como o Fiddler.",
|
||||||
|
"patch_rsa": "Corrigir e 'descorrigir' o RSA do seu jogo automaticamente. Isso deve estar habilitado a não ser que você esteja jogando com versões antigas (3.0 ou mais antigas) ou não oficiais.",
|
||||||
|
"add_delay": "Atraso definido na carregadeira 3dmigoto! \nIsto deve resolver os problemas de carregamento, mas acrescentará um pequeno atraso quando o 3dmigoto for carregado no lançamento do jogo. \nAgora você pode lançar novamente com o 3dmigoto.",
|
||||||
|
"migoto": "Para importação de modelos da GameBanana"
|
||||||
|
},
|
||||||
|
"swag": {
|
||||||
|
"akebi_name": "Akebi",
|
||||||
|
"migoto_name": "Migoto",
|
||||||
|
"reshade_name": "Reshade",
|
||||||
|
"akebi": "Definir o Executavel do Akebi/Acrepi",
|
||||||
|
"migoto": "Definir o Executavel do 3DMigoto",
|
||||||
|
"reshade": "Definir o Executavel do Reshade Injector"
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -15,7 +15,7 @@
|
|||||||
"disabled": "Выключено",
|
"disabled": "Выключено",
|
||||||
"game_path": "Установить путь к файлам игры",
|
"game_path": "Установить путь к файлам игры",
|
||||||
"game_executable": "Установить исполняемый файл игры",
|
"game_executable": "Установить исполняемый файл игры",
|
||||||
"recover_metadata": "Принудительное восстановление Метаданных",
|
"recover_rsa": "Принудительное удаление RSA",
|
||||||
"grasscutter_jar": "Установить Grasscutter JAR",
|
"grasscutter_jar": "Установить Grasscutter JAR",
|
||||||
"toggle_encryption": "Переключить шифрование",
|
"toggle_encryption": "Переключить шифрование",
|
||||||
"install_certificate": "Установить сертификат для работы Прокси",
|
"install_certificate": "Установить сертификат для работы Прокси",
|
||||||
@@ -24,20 +24,30 @@
|
|||||||
"language": "Установить язык",
|
"language": "Установить язык",
|
||||||
"background": "Установить свой фон (ссылка или файл)",
|
"background": "Установить свой фон (ссылка или файл)",
|
||||||
"theme": "Установить тему",
|
"theme": "Установить тему",
|
||||||
"patch_metadata": "Автоматический патч Метаданных при запуске",
|
"patch_rsa": "Автоматическое исправление RSA",
|
||||||
"use_proxy": "Использовать встроенный Прокси"
|
"use_proxy": "Использовать встроенный Прокси",
|
||||||
|
"wipe_login": "Очистить кэш входа в систему",
|
||||||
|
"horny_mode": "роговой режим",
|
||||||
|
"auto_mongodb": "Автоматически запускать MongoDB",
|
||||||
|
"un_elevated": "Запустите игру в неэлегантном режиме (без администратора)"
|
||||||
},
|
},
|
||||||
"downloads": {
|
"downloads": {
|
||||||
|
"grasscutter_fullbuild": "Скачать все в одном Grasscutter",
|
||||||
|
"grasscutter_fullquest": "Скачать квесты все в одном",
|
||||||
"grasscutter_stable_data": "Скачать стабильные данные Grasscutter",
|
"grasscutter_stable_data": "Скачать стабильные данные Grasscutter",
|
||||||
"grasscutter_latest_data": "Скачать последние данные Grasscutter",
|
"grasscutter_latest_data": "Скачать последние данные Grasscutter",
|
||||||
"grasscutter_stable_data_update": "Обновить стабильные данные Grasscutter",
|
"grasscutter_stable_data_update": "Обновить стабильные данные Grasscutter",
|
||||||
"grasscutter_latest_data_update": "Обновить последние данные Grasscutter",
|
"grasscutter_latest_data_update": "Обновить последние данные Grasscutter",
|
||||||
"grasscutter_stable": "Скачать стабильную версию Grasscutter",
|
"grasscutter_unstable": "Скачать стабильную версию Grasscutter",
|
||||||
"grasscutter_latest": "Скачать последнюю версию Grasscutter",
|
"grasscutter_latest": "Скачать последнюю версию Grasscutter",
|
||||||
"grasscutter_stable_update": "Обновить стабильную версию Grasscutter",
|
"grasscutter_unstable_update": "Обновить стабильную версию Grasscutter",
|
||||||
"grasscutter_latest_update": "Обновить последнюю версию Grasscutter",
|
"grasscutter_latest_update": "Обновить последнюю версию Grasscutter",
|
||||||
"resources": "Скачать ресурсы Grasscutter",
|
"resources": "Скачать ресурсы Grasscutter",
|
||||||
"game": "Скачать Игру"
|
"game": "Скачать Игру",
|
||||||
|
"aio_header": "Все в одной загрузке:",
|
||||||
|
"individual_header": "загрузка отдельных частей:",
|
||||||
|
"mods_header": "Mods:",
|
||||||
|
"migoto": "Скачать GIMI 3dmigoto"
|
||||||
},
|
},
|
||||||
"download_status": {
|
"download_status": {
|
||||||
"downloading": "Скачивание",
|
"downloading": "Скачивание",
|
||||||
@@ -50,6 +60,7 @@
|
|||||||
"select_file": "Выберите файл или папку...",
|
"select_file": "Выберите файл или папку...",
|
||||||
"select_folder": "Выберите папку...",
|
"select_folder": "Выберите папку...",
|
||||||
"download": "Скачать",
|
"download": "Скачать",
|
||||||
|
"delete": "Удалить",
|
||||||
"install": "Установить"
|
"install": "Установить"
|
||||||
},
|
},
|
||||||
"news": {
|
"news": {
|
||||||
@@ -60,14 +71,17 @@
|
|||||||
"port_help_text": "Убедитесь, что это порт Dispatch-сервера, не порт игрового сервера. Обычно это '443'.",
|
"port_help_text": "Убедитесь, что это порт Dispatch-сервера, не порт игрового сервера. Обычно это '443'.",
|
||||||
"game_help_text": "Вам не нужно устанавливать еще одну копию, что бы играть с Grascutter. Это нужно или для версии 2.6, или если у Вас не установлена игра.",
|
"game_help_text": "Вам не нужно устанавливать еще одну копию, что бы играть с Grascutter. Это нужно или для версии 2.6, или если у Вас не установлена игра.",
|
||||||
"gc_stable_jar": "Скачать последнюю стабильную версию Grasscutter, которая содержит jar файл и данные.",
|
"gc_stable_jar": "Скачать последнюю стабильную версию Grasscutter, которая содержит jar файл и данные.",
|
||||||
|
"gc_fullbuild": "Загрузите полную сборку Grasscutter, включая репо, jar и ресурсы. Полностью настроена и не требует других загрузок из этого меню.",
|
||||||
"gc_dev_jar": "Скачать последнюю версию для разработки Grasscutter, которая содержит jar файл и данные.",
|
"gc_dev_jar": "Скачать последнюю версию для разработки Grasscutter, которая содержит jar файл и данные.",
|
||||||
"gc_stable_data": "Скачать стабильные данные Grasscutter, в которой нету jar файла. Это полезно для обновления.",
|
"gc_stable_data": "Скачать стабильные данные Grasscutter, в которой нету jar файла. Это полезно для обновления.",
|
||||||
"gc_dev_data": "Скачать последнюю версию для разработки Grasscutter, в которой нету jar файла. Это полезно для обновления.",
|
"gc_dev_data": "Скачать последнюю версию для разработки Grasscutter, в которой нету jar файла. Это полезно для обновления.",
|
||||||
"encryption": "Обычно это должно быть выключено.",
|
"encryption": "Обычно это должно быть выключено.",
|
||||||
"resources": "Это необходимо для запуска сервера Grasscutter. Эта кнопка будет серой, если у Вас уже есть не пустая папка с ресурсами.",
|
"resources": "Это необходимо для запуска сервера Grasscutter. Эта кнопка будет серой, если у Вас уже есть не пустая папка с ресурсами.",
|
||||||
"emergency_metadata": "Если что-то пошло не так, восстановит Метаданные до последней официальной версии.",
|
"emergency_rsa": "Если что-то пошло не так, восстановит RSA до последней официальной версии.",
|
||||||
"use_proxy": "Использовать встроенный Прокси. Отключите если используете Fiddler или подобную программу",
|
"use_proxy": "Использовать встроенный Прокси. Отключите если используете Fiddler или подобную программу",
|
||||||
"patch_metadata": "Патчит и восстанавливает Метаданные автоматически. Если вы не играете на старых/модифицированых версиях, или сами в ручную патчите Метаданные, эта опция должна быть включена."
|
"patch_rsa": "Патчит и восстанавливает RSA автоматически. Если вы не играете на старых/модифицированых версиях, или сами в ручную патчите Метаданные, эта опция должна быть включена.",
|
||||||
|
"add_delay": "Установите задержку в загрузчике 3dmigoto! \nЭто должно исправить проблемы с загрузкой, но добавит небольшую задержку в момент загрузки 3dmigoto при запуске игры. \nТеперь вы снова можете запускать игру с помощью 3dmigoto.",
|
||||||
|
"migoto": "Для импорта моделей из GameBanana"
|
||||||
},
|
},
|
||||||
"swag": {
|
"swag": {
|
||||||
"akebi_name": "Akebi",
|
"akebi_name": "Akebi",
|
||||||
|
|||||||
@@ -16,7 +16,7 @@
|
|||||||
"game_path": "Đường dẫn cài game",
|
"game_path": "Đường dẫn cài game",
|
||||||
"game_command": "Lệnh khởi chạy game",
|
"game_command": "Lệnh khởi chạy game",
|
||||||
"game_executable": "Tập tin thực thi 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",
|
"grasscutter_jar": "Tập tin JAR Grasscutter",
|
||||||
"toggle_encryption": "Bật/tắt mã hóa",
|
"toggle_encryption": "Bật/tắt mã hóa",
|
||||||
"install_certificate": "Cài chứng chỉ proxy",
|
"install_certificate": "Cài chứng chỉ proxy",
|
||||||
@@ -25,22 +25,30 @@
|
|||||||
"language": "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)",
|
"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",
|
"theme": "Giao diện",
|
||||||
"patch_metadata": "Tự động vá metadata",
|
"patch_rsa": "Tự động vá RSA",
|
||||||
"use_proxy": "Sử dụng proxy nội bộ",
|
"use_proxy": "Sử dụng proxy nội bộ",
|
||||||
"wipe_login": "Tẩy sạch cache đăng nhập",
|
"wipe_login": "Tẩy sạch cache đăng nhập",
|
||||||
"horny_mode": "Chế độ hứng tình"
|
"horny_mode": "Chế độ hứng tình",
|
||||||
|
"auto_mongodb": "Tự động khởi động MongoDB",
|
||||||
|
"un_elevated": "Chạy trò chơi không nâng cao (không có quản trị viên)"
|
||||||
},
|
},
|
||||||
"downloads": {
|
"downloads": {
|
||||||
|
"grasscutter_fullbuild": "Tải Grasscutter tất cả trong một",
|
||||||
|
"grasscutter_fullquest": "Tải xuống truy vấn tất cả trong một",
|
||||||
"grasscutter_stable_data": "Tải dữ liệu Grasscutter bản ổn định",
|
"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_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",
|
"grasscutter_stable_data_update": "Cập nhật dữ liệu Grasscutter bản ổn định",
|
||||||
"grasscutter_latest_data_update": "Cập nhật dữ liệu Grasscutter bản mới nhất",
|
"grasscutter_latest_data_update": "Cập nhật dữ liệu Grasscutter bản mới nhất",
|
||||||
"grasscutter_stable": "Tải Grasscutter bản ổn định",
|
"grasscutter_unstable": "Tải Grasscutter bản ổn định",
|
||||||
"grasscutter_latest": "Tải Grasscutter bản mới nhất",
|
"grasscutter_latest": "Tải Grasscutter bản mới nhất",
|
||||||
"grasscutter_stable_update": "Cập nhật Grasscutter bản ổn định",
|
"grasscutter_unstable_update": "Cập nhật Grasscutter bản ổn định",
|
||||||
"grasscutter_latest_update": "Cập nhật Grasscutter bản mới nhất",
|
"grasscutter_latest_update": "Cập nhật Grasscutter bản mới nhất",
|
||||||
"resources": "Tải tài nguyên Grasscutter",
|
"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:",
|
||||||
|
"mods_header": "Mods:",
|
||||||
|
"migoto": "Tải GIMI 3dmigoto"
|
||||||
},
|
},
|
||||||
"download_status": {
|
"download_status": {
|
||||||
"downloading": "Đang tải",
|
"downloading": "Đang tải",
|
||||||
@@ -53,6 +61,7 @@
|
|||||||
"select_file": "Chọn tập tin hoặc thư mục...",
|
"select_file": "Chọn tập tin hoặc thư mục...",
|
||||||
"select_folder": "Chọn thư mục...",
|
"select_folder": "Chọn thư mục...",
|
||||||
"download": "Tải",
|
"download": "Tải",
|
||||||
|
"delete": "Xóa bỏ",
|
||||||
"install": "Cài"
|
"install": "Cài"
|
||||||
},
|
},
|
||||||
"news": {
|
"news": {
|
||||||
@@ -63,14 +72,17 @@
|
|||||||
"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'.",
|
"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.",
|
"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_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_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_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.",
|
"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.",
|
"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",
|
"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_metadata": "Trong trường hợp gặp vấn đề, khôi phục lại metadata về phiên bản chính thức mới nhất.",
|
"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",
|
"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_metadata": "Tự động vá và sửa lại metadata 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á metadata rồi."
|
"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.",
|
||||||
|
"add_delay": "Đặt độ trễ trong trình tải 3dmigoto! \nĐiều này sẽ khắc phục sự cố tải, nhưng sẽ thêm một độ trễ nhỏ khi 3dmigoto được tải khi khởi chạy trò chơi. \nBây giờ bạn có thể khởi chạy lại với 3dmigoto.",
|
||||||
|
"migoto": "Để nhập mô hình từ GameBanana"
|
||||||
},
|
},
|
||||||
"swag": {
|
"swag": {
|
||||||
"akebi_name": "Akebi",
|
"akebi_name": "Akebi",
|
||||||
|
|||||||
BIN
src-tauri/patch/version.dll
Normal file
BIN
src-tauri/patch/version.dll
Normal file
Binary file not shown.
@@ -1,5 +1,7 @@
|
|||||||
#[cfg(windows)]
|
#[cfg(windows)]
|
||||||
pub fn reopen_as_admin() {
|
pub fn reopen_as_admin() {
|
||||||
|
use std::process::{exit, Command};
|
||||||
|
|
||||||
let install = std::env::current_exe().unwrap();
|
let install = std::env::current_exe().unwrap();
|
||||||
|
|
||||||
println!("Opening as admin: {}", install.to_str().unwrap());
|
println!("Opening as admin: {}", install.to_str().unwrap());
|
||||||
@@ -18,3 +20,6 @@ pub fn reopen_as_admin() {
|
|||||||
|
|
||||||
#[cfg(target_os = "linux")]
|
#[cfg(target_os = "linux")]
|
||||||
pub fn reopen_as_admin() {}
|
pub fn reopen_as_admin() {}
|
||||||
|
|
||||||
|
#[cfg(target_os = "macos")]
|
||||||
|
pub fn reopen_as_admin() {}
|
||||||
|
|||||||
45
src-tauri/src/config.rs
Normal file
45
src-tauri/src/config.rs
Normal file
@@ -0,0 +1,45 @@
|
|||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
use std::path::PathBuf;
|
||||||
|
use std::string::String;
|
||||||
|
|
||||||
|
// Config may not exist, or may be old, so it's okay if these are optional
|
||||||
|
#[derive(Serialize, Deserialize, Debug)]
|
||||||
|
pub struct Configuration {
|
||||||
|
pub toggle_grasscutter: Option<bool>,
|
||||||
|
pub game_install_path: Option<String>,
|
||||||
|
pub grasscutter_with_game: Option<bool>,
|
||||||
|
pub grasscutter_path: Option<String>,
|
||||||
|
pub java_path: Option<String>,
|
||||||
|
pub close_action: Option<u64>,
|
||||||
|
pub startup_launch: Option<bool>,
|
||||||
|
pub last_ip: Option<String>,
|
||||||
|
pub last_port: Option<String>,
|
||||||
|
pub language: Option<String>,
|
||||||
|
pub custom_background: Option<String>,
|
||||||
|
pub cert_generated: Option<bool>,
|
||||||
|
pub theme: Option<String>,
|
||||||
|
pub https_enabled: Option<bool>,
|
||||||
|
pub debug_enabled: Option<bool>,
|
||||||
|
pub patch_rsa: Option<bool>,
|
||||||
|
pub use_internal_proxy: Option<bool>,
|
||||||
|
pub wipe_login: Option<bool>,
|
||||||
|
pub horny_mode: Option<bool>,
|
||||||
|
pub auto_mongodb: Option<bool>,
|
||||||
|
pub un_elevated: Option<bool>,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn config_path() -> PathBuf {
|
||||||
|
let mut path = tauri::api::path::data_dir().unwrap();
|
||||||
|
path.push("cultivation");
|
||||||
|
path.push("configuration.json");
|
||||||
|
|
||||||
|
path
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_config() -> Configuration {
|
||||||
|
let path = config_path();
|
||||||
|
let config = std::fs::read_to_string(path).unwrap_or("{}".to_string());
|
||||||
|
let config: Configuration = serde_json::from_str(&config).unwrap();
|
||||||
|
|
||||||
|
config
|
||||||
|
}
|
||||||
@@ -131,8 +131,13 @@ pub fn read_file(path: String) -> String {
|
|||||||
let mut file = match fs::File::open(path_buf) {
|
let mut file = match fs::File::open(path_buf) {
|
||||||
Ok(file) => file,
|
Ok(file) => file,
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
println!("Failed to open file: {}", e);
|
if path.contains("config") {
|
||||||
return String::new();
|
// 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
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -13,15 +13,26 @@ pub async fn get_download_links(mod_id: String) -> String {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[tauri::command]
|
#[tauri::command]
|
||||||
pub async fn list_submissions(mode: String, page: String) -> String {
|
pub async fn list_submissions(mode: String, page: String, search: String) -> String {
|
||||||
web::query(
|
if search.is_empty() {
|
||||||
format!(
|
web::query(
|
||||||
"{}/apiv9/Util/Game/Submissions?_idGameRow=8552&_nPage={}&_nPerpage=50&_sMode={}",
|
format!(
|
||||||
SITE_URL, page, mode
|
"{}/apiv9/Util/Game/Submissions?_idGameRow=8552&_nPage={}&_nPerpage=50&_sMode={}",
|
||||||
|
SITE_URL, page, mode
|
||||||
|
)
|
||||||
|
.as_str(),
|
||||||
)
|
)
|
||||||
.as_str(),
|
.await
|
||||||
)
|
} else {
|
||||||
.await
|
web::query(
|
||||||
|
format!(
|
||||||
|
"{}/apiv11/Util/Search/Results?_nPage={}&_sOrder=best_match&_idGameRow=8552&_sSearchString={}&_csvFields=name,description,article,attribs,studio,owner,credits",
|
||||||
|
SITE_URL, page, search
|
||||||
|
)
|
||||||
|
.as_str()
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tauri::command]
|
#[tauri::command]
|
||||||
|
|||||||
@@ -28,7 +28,12 @@ pub async fn get_languages() -> std::collections::HashMap<String, String> {
|
|||||||
for entry in lang_files {
|
for entry in lang_files {
|
||||||
let entry = entry.unwrap();
|
let entry = entry.unwrap();
|
||||||
let path = entry.path();
|
let path = entry.path();
|
||||||
let filename = path.file_name().unwrap().to_str().unwrap();
|
let filename = path
|
||||||
|
.file_name()
|
||||||
|
.unwrap_or_else(|| panic!("Failed to get filename from path: {:?}", path))
|
||||||
|
.to_str()
|
||||||
|
.unwrap_or_else(|| panic!("Failed to convert filename to string: {:?}", path))
|
||||||
|
.to_string();
|
||||||
|
|
||||||
let content = match std::fs::read_to_string(&path) {
|
let content = match std::fs::read_to_string(&path) {
|
||||||
Ok(x) => x,
|
Ok(x) => x,
|
||||||
|
|||||||
@@ -2,10 +2,12 @@
|
|||||||
all(not(debug_assertions), target_os = "windows"),
|
all(not(debug_assertions), target_os = "windows"),
|
||||||
windows_subsystem = "windows"
|
windows_subsystem = "windows"
|
||||||
)]
|
)]
|
||||||
#![deny(clippy::all, unused)]
|
|
||||||
|
|
||||||
|
use args::{Args, ArgsError};
|
||||||
use file_helpers::dir_exists;
|
use file_helpers::dir_exists;
|
||||||
|
|
||||||
use once_cell::sync::Lazy;
|
use once_cell::sync::Lazy;
|
||||||
|
use proxy::set_proxy_addr;
|
||||||
use std::fs;
|
use std::fs;
|
||||||
use std::io::Write;
|
use std::io::Write;
|
||||||
use std::{collections::HashMap, sync::Mutex};
|
use std::{collections::HashMap, sync::Mutex};
|
||||||
@@ -14,51 +16,147 @@ use tauri::api::path::data_dir;
|
|||||||
use tauri::async_runtime::block_on;
|
use tauri::async_runtime::block_on;
|
||||||
|
|
||||||
use std::thread;
|
use std::thread;
|
||||||
use sysinfo::{System, SystemExt};
|
use sysinfo::{Pid, ProcessExt, System, SystemExt};
|
||||||
|
|
||||||
#[cfg(windows)]
|
|
||||||
use crate::admin::reopen_as_admin;
|
use crate::admin::reopen_as_admin;
|
||||||
|
|
||||||
mod admin;
|
mod admin;
|
||||||
|
mod config;
|
||||||
mod downloader;
|
mod downloader;
|
||||||
mod file_helpers;
|
mod file_helpers;
|
||||||
mod gamebanana;
|
mod gamebanana;
|
||||||
mod lang;
|
mod lang;
|
||||||
mod metadata_patcher;
|
mod patch;
|
||||||
mod proxy;
|
mod proxy;
|
||||||
|
mod release;
|
||||||
mod system_helpers;
|
mod system_helpers;
|
||||||
mod unzip;
|
mod unzip;
|
||||||
mod web;
|
mod web;
|
||||||
|
|
||||||
static WATCH_GAME_PROCESS: Lazy<Mutex<String>> = Lazy::new(|| Mutex::new(String::new()));
|
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() {
|
fn try_flush() {
|
||||||
std::io::stdout().flush().unwrap_or(())
|
std::io::stdout().flush().unwrap_or(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn has_arg(args: &[String], arg: &str) -> bool {
|
async fn parse_args(inp: &Vec<String>) -> Result<Args, ArgsError> {
|
||||||
args.contains(&arg.to_string())
|
let mut args = Args::new(
|
||||||
}
|
"Cultivation",
|
||||||
|
"Private server helper program for an Anime Game",
|
||||||
|
);
|
||||||
|
args.flag("h", "help", "Print various CLI args");
|
||||||
|
args.flag("p", "proxy", "Start the proxy server");
|
||||||
|
args.flag("G", "launch-game", "Launch the game");
|
||||||
|
args.flag(
|
||||||
|
"A",
|
||||||
|
"no-admin",
|
||||||
|
"Launch without requiring admin permissions",
|
||||||
|
);
|
||||||
|
args.flag(
|
||||||
|
"g",
|
||||||
|
"no-gui",
|
||||||
|
"Run in CLI mode. Requires -A to be passed as well.",
|
||||||
|
);
|
||||||
|
args.flag("s", "server", "Launch the configured GC server");
|
||||||
|
args.flag(
|
||||||
|
"P",
|
||||||
|
"patch",
|
||||||
|
"Patch your game before launching, with whatever your game version needs",
|
||||||
|
);
|
||||||
|
args.flag(
|
||||||
|
"N",
|
||||||
|
"non-elevated-game",
|
||||||
|
"Launch the game without admin permissions",
|
||||||
|
);
|
||||||
|
args.option(
|
||||||
|
"H",
|
||||||
|
"host",
|
||||||
|
"Set host to connect to (eg. 'localhost:443' or 'my.awesomeserver.com:6969)",
|
||||||
|
"SERVER_HOST",
|
||||||
|
getopts::Occur::Optional,
|
||||||
|
None,
|
||||||
|
);
|
||||||
|
args.option(
|
||||||
|
"a",
|
||||||
|
"game-args",
|
||||||
|
"Arguments to pass to the game process, if launching it",
|
||||||
|
r#""-opt-one -opt-two""#,
|
||||||
|
getopts::Occur::Optional,
|
||||||
|
None,
|
||||||
|
);
|
||||||
|
|
||||||
async fn arg_handler(args: &[String]) {
|
args.parse(inp).unwrap();
|
||||||
if has_arg(args, "--proxy") {
|
|
||||||
let mut pathbuf = data_dir().unwrap();
|
let config = config::get_config();
|
||||||
|
|
||||||
|
if args.value_of("help")? {
|
||||||
|
println!("{}", args.full_usage());
|
||||||
|
std::process::exit(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
if args.value_of("launch-game")? {
|
||||||
|
let game_path = config.game_install_path;
|
||||||
|
let game_args: String = args.value_of("game-args").unwrap_or_default();
|
||||||
|
|
||||||
|
// Patch if needed
|
||||||
|
if args.value_of("patch")? {
|
||||||
|
patch::patch_game().await;
|
||||||
|
}
|
||||||
|
|
||||||
|
if game_path.is_some() {
|
||||||
|
if args.value_of("non-elevated-game")? {
|
||||||
|
system_helpers::run_un_elevated(game_path.unwrap(), Some(game_args))
|
||||||
|
} else {
|
||||||
|
system_helpers::run_program(game_path.unwrap(), Some(game_args))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if args.value_of("server")? && config.grasscutter_path.is_some() && config.java_path.is_some() {
|
||||||
|
let server_jar = config.grasscutter_path.unwrap();
|
||||||
|
let mut server_path = server_jar.clone();
|
||||||
|
// Strip jar name from path
|
||||||
|
if server_path.contains('/') {
|
||||||
|
// Can never panic because of if
|
||||||
|
let len = server_jar.rfind('/').unwrap();
|
||||||
|
server_path.truncate(len);
|
||||||
|
} else if server_path.contains('\\') {
|
||||||
|
let len = server_jar.rfind('\\').unwrap();
|
||||||
|
server_path.truncate(len);
|
||||||
|
}
|
||||||
|
let java_path = config.java_path.unwrap();
|
||||||
|
|
||||||
|
system_helpers::run_jar(server_jar, server_path.to_string(), java_path);
|
||||||
|
}
|
||||||
|
|
||||||
|
if args.value_of::<String>("host").is_ok() && !args.value_of::<String>("host")?.is_empty() {
|
||||||
|
let host = args.value_of::<String>("host")?;
|
||||||
|
set_proxy_addr(host);
|
||||||
|
}
|
||||||
|
|
||||||
|
if args.value_of("proxy")? {
|
||||||
|
println!("Starting proxy server...");
|
||||||
|
let mut pathbuf = tauri::api::path::data_dir().unwrap();
|
||||||
pathbuf.push("cultivation");
|
pathbuf.push("cultivation");
|
||||||
pathbuf.push("ca");
|
pathbuf.push("ca");
|
||||||
|
|
||||||
connect(8035, pathbuf.to_str().unwrap().to_string()).await;
|
connect(8035, pathbuf.to_str().unwrap().to_string()).await;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Ok(args)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn main() {
|
fn main() -> Result<(), ArgsError> {
|
||||||
let args: Vec<String> = std::env::args().collect();
|
let args: Vec<String> = std::env::args().collect();
|
||||||
|
let parsed_args = block_on(parse_args(&args)).unwrap();
|
||||||
|
|
||||||
if !is_elevated() && !has_arg(&args, "--no-admin") {
|
if !is_elevated() && !parsed_args.value_of("no-admin")? {
|
||||||
println!("===============================================================================");
|
println!("===============================================================================");
|
||||||
println!("You running as a non-elevated user. Some stuff will almost definitely not work.");
|
println!("You running as a non-elevated user. Some stuff will almost definitely not work.");
|
||||||
println!("===============================================================================");
|
println!("===============================================================================");
|
||||||
|
|
||||||
#[cfg(windows)]
|
|
||||||
reopen_as_admin();
|
reopen_as_admin();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -72,36 +170,44 @@ fn main() {
|
|||||||
exe_path.pop();
|
exe_path.pop();
|
||||||
std::env::set_current_dir(&exe_path).unwrap();
|
std::env::set_current_dir(&exe_path).unwrap();
|
||||||
|
|
||||||
block_on(arg_handler(&args));
|
|
||||||
|
|
||||||
// For disabled GUI
|
// For disabled GUI
|
||||||
ctrlc::set_handler(|| {
|
ctrlc::set_handler(|| {
|
||||||
disconnect();
|
disconnect();
|
||||||
|
block_on(patch::unpatch_game());
|
||||||
std::process::exit(0);
|
std::process::exit(0);
|
||||||
})
|
})
|
||||||
.unwrap_or(());
|
.unwrap_or(());
|
||||||
|
|
||||||
if !has_arg(&args, "--no-gui") {
|
if !parsed_args.value_of("no-gui")? {
|
||||||
tauri::Builder::default()
|
tauri::Builder::default()
|
||||||
.invoke_handler(tauri::generate_handler![
|
.invoke_handler(tauri::generate_handler![
|
||||||
enable_process_watcher,
|
enable_process_watcher,
|
||||||
|
enable_grasscutter_watcher,
|
||||||
connect,
|
connect,
|
||||||
disconnect,
|
disconnect,
|
||||||
req_get,
|
req_get,
|
||||||
is_game_running,
|
is_game_running,
|
||||||
|
is_grasscutter_running,
|
||||||
|
restart_grasscutter,
|
||||||
get_theme_list,
|
get_theme_list,
|
||||||
system_helpers::run_command,
|
system_helpers::run_command,
|
||||||
system_helpers::run_program,
|
system_helpers::run_program,
|
||||||
system_helpers::run_program_relative,
|
system_helpers::run_program_relative,
|
||||||
|
system_helpers::start_service,
|
||||||
|
system_helpers::service_status,
|
||||||
|
system_helpers::stop_service,
|
||||||
system_helpers::run_jar,
|
system_helpers::run_jar,
|
||||||
system_helpers::open_in_browser,
|
system_helpers::open_in_browser,
|
||||||
system_helpers::install_location,
|
system_helpers::install_location,
|
||||||
system_helpers::is_elevated,
|
system_helpers::is_elevated,
|
||||||
system_helpers::set_migoto_target,
|
system_helpers::set_migoto_target,
|
||||||
|
system_helpers::set_migoto_delay,
|
||||||
system_helpers::wipe_registry,
|
system_helpers::wipe_registry,
|
||||||
system_helpers::get_platform,
|
system_helpers::get_platform,
|
||||||
|
system_helpers::run_un_elevated,
|
||||||
proxy::set_proxy_addr,
|
proxy::set_proxy_addr,
|
||||||
proxy::generate_ca_files,
|
proxy::generate_ca_files,
|
||||||
|
release::get_latest_release,
|
||||||
unzip::unzip,
|
unzip::unzip,
|
||||||
file_helpers::rename,
|
file_helpers::rename,
|
||||||
file_helpers::dir_create,
|
file_helpers::dir_create,
|
||||||
@@ -122,9 +228,15 @@ fn main() {
|
|||||||
web::web_get,
|
web::web_get,
|
||||||
gamebanana::get_download_links,
|
gamebanana::get_download_links,
|
||||||
gamebanana::list_submissions,
|
gamebanana::list_submissions,
|
||||||
gamebanana::list_mods,
|
gamebanana::list_mods
|
||||||
metadata_patcher::patch_metadata
|
|
||||||
])
|
])
|
||||||
|
.on_window_event(|event| match event.event() {
|
||||||
|
tauri::WindowEvent::CloseRequested { api, .. } => {
|
||||||
|
// Ensure all proxy stuff is handled
|
||||||
|
disconnect();
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
|
})
|
||||||
.run(tauri::generate_context!())
|
.run(tauri::generate_context!())
|
||||||
.expect("error while running tauri application");
|
.expect("error while running tauri application");
|
||||||
} else {
|
} else {
|
||||||
@@ -135,6 +247,11 @@ fn main() {
|
|||||||
|
|
||||||
// Always disconnect upon closing the program
|
// Always disconnect upon closing the program
|
||||||
disconnect();
|
disconnect();
|
||||||
|
|
||||||
|
// Always unpatch game upon closing the program
|
||||||
|
block_on(patch::unpatch_game());
|
||||||
|
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tauri::command]
|
#[tauri::command]
|
||||||
@@ -162,7 +279,8 @@ fn enable_process_watcher(window: tauri::Window, process: String) {
|
|||||||
let mut system = System::new_all();
|
let mut system = System::new_all();
|
||||||
|
|
||||||
loop {
|
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
|
// Refresh system info
|
||||||
system.refresh_all();
|
system.refresh_all();
|
||||||
@@ -189,6 +307,123 @@ 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()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(windows)]
|
||||||
|
#[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(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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(unix)]
|
||||||
|
#[tauri::command]
|
||||||
|
fn restart_grasscutter(_window: tauri::Window) {
|
||||||
|
// Placeholder text for imports
|
||||||
|
let s = System::new();
|
||||||
|
if let Some(process) = s.process(Pid::from(1337)) {
|
||||||
|
println!("{}", process.name());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(windows)]
|
||||||
|
#[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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(unix)]
|
||||||
|
#[tauri::command]
|
||||||
|
fn enable_grasscutter_watcher(_window: tauri::Window, _process: String) {
|
||||||
|
let gc_pid = Pid::from(696969);
|
||||||
|
*GC_PID.lock().unwrap() = gc_pid.into();
|
||||||
|
}
|
||||||
|
|
||||||
#[tauri::command]
|
#[tauri::command]
|
||||||
async fn connect(port: u16, certificate_path: String) {
|
async fn connect(port: u16, certificate_path: String) {
|
||||||
// Log message to console.
|
// Log message to console.
|
||||||
|
|||||||
@@ -1,153 +0,0 @@
|
|||||||
use regex::Regex;
|
|
||||||
use std::{fs, fs::File, fs::OpenOptions, io::Read, io::Write, path::Path};
|
|
||||||
|
|
||||||
// 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");
|
|
||||||
// check if metadata_file exists
|
|
||||||
if !Path::new(metadata_file).exists() {
|
|
||||||
println!("Metadata file not found");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
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 path from current directory
|
|
||||||
let key_file_path = std::env::current_dir()
|
|
||||||
.unwrap()
|
|
||||||
.join("keys")
|
|
||||||
.join(file_name);
|
|
||||||
|
|
||||||
let key_data = match fs::read(&key_file_path) {
|
|
||||||
Ok(file) => file,
|
|
||||||
Err(e) => {
|
|
||||||
println!("Failed to open {}: {}", key_file_path.to_str().unwrap(), e);
|
|
||||||
return String::new();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
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
|
|
||||||
}
|
|
||||||
62
src-tauri/src/patch.rs
Normal file
62
src-tauri/src/patch.rs
Normal file
@@ -0,0 +1,62 @@
|
|||||||
|
use crate::config;
|
||||||
|
use crate::file_helpers;
|
||||||
|
use crate::system_helpers;
|
||||||
|
use std::path::PathBuf;
|
||||||
|
|
||||||
|
pub async fn patch_game() -> bool {
|
||||||
|
let patch_path = PathBuf::from(system_helpers::install_location()).join("patch/version.dll");
|
||||||
|
|
||||||
|
// Are we already patched with mhypbase? If so, that's fine, just continue as normal
|
||||||
|
let game_is_patched = file_helpers::are_files_identical(
|
||||||
|
patch_path.clone().to_str().unwrap(),
|
||||||
|
PathBuf::from(get_game_rsa_path().await.unwrap())
|
||||||
|
.join("mhypbase.dll")
|
||||||
|
.to_str()
|
||||||
|
.unwrap(),
|
||||||
|
);
|
||||||
|
|
||||||
|
// Tell user they won't be unpatched with manual mhypbase patch
|
||||||
|
if game_is_patched {
|
||||||
|
println!(
|
||||||
|
"You are already patched using mhypbase, so you will not be auto patched and unpatched!"
|
||||||
|
);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Copy the patch to game files
|
||||||
|
let replaced = file_helpers::copy_file_with_new_name(
|
||||||
|
patch_path.clone().to_str().unwrap().to_string(),
|
||||||
|
get_game_rsa_path().await.unwrap(),
|
||||||
|
String::from("version.dll"),
|
||||||
|
);
|
||||||
|
|
||||||
|
if !replaced {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
true
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn unpatch_game() -> bool {
|
||||||
|
// Just delete patch since it's not replacing any existing file
|
||||||
|
let deleted = file_helpers::delete_file(
|
||||||
|
PathBuf::from(get_game_rsa_path().await.unwrap())
|
||||||
|
.join("version.dll")
|
||||||
|
.to_str()
|
||||||
|
.unwrap()
|
||||||
|
.to_string(),
|
||||||
|
);
|
||||||
|
|
||||||
|
deleted
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn get_game_rsa_path() -> Option<String> {
|
||||||
|
let config = config::get_config();
|
||||||
|
|
||||||
|
config.game_install_path.as_ref()?;
|
||||||
|
|
||||||
|
let mut game_folder = PathBuf::from(config.game_install_path.unwrap());
|
||||||
|
game_folder.pop();
|
||||||
|
|
||||||
|
Some(format!("{}/", game_folder.to_str().unwrap()).replace('\\', "/"))
|
||||||
|
}
|
||||||
@@ -12,7 +12,7 @@ use std::{path::PathBuf, str::FromStr, sync::Mutex};
|
|||||||
use hudsucker::{
|
use hudsucker::{
|
||||||
async_trait::async_trait,
|
async_trait::async_trait,
|
||||||
certificate_authority::RcgenAuthority,
|
certificate_authority::RcgenAuthority,
|
||||||
hyper::{Body, Request, Response},
|
hyper::{Body, Request, Response, StatusCode},
|
||||||
*,
|
*,
|
||||||
};
|
};
|
||||||
use rcgen::*;
|
use rcgen::*;
|
||||||
@@ -41,29 +41,47 @@ struct ProxyHandler;
|
|||||||
|
|
||||||
#[tauri::command]
|
#[tauri::command]
|
||||||
pub fn set_proxy_addr(addr: String) {
|
pub fn set_proxy_addr(addr: String) {
|
||||||
*SERVER.lock().unwrap() = addr;
|
if addr.contains(' ') {
|
||||||
|
let addr2 = addr.replace(' ', "");
|
||||||
|
*SERVER.lock().unwrap() = addr2;
|
||||||
|
} else {
|
||||||
|
*SERVER.lock().unwrap() = addr;
|
||||||
|
}
|
||||||
|
|
||||||
|
println!("Set server to {}", SERVER.lock().unwrap());
|
||||||
}
|
}
|
||||||
|
|
||||||
#[async_trait]
|
#[async_trait]
|
||||||
impl HttpHandler for ProxyHandler {
|
impl HttpHandler for ProxyHandler {
|
||||||
async fn handle_request(
|
async fn handle_request(
|
||||||
&mut self,
|
&mut self,
|
||||||
_context: &HttpContext,
|
_ctx: &HttpContext,
|
||||||
mut request: Request<Body>,
|
mut req: Request<Body>,
|
||||||
) -> RequestOrResponse {
|
) -> RequestOrResponse {
|
||||||
let uri = request.uri().to_string();
|
let uri = req.uri().to_string();
|
||||||
let uri_path_and_query = request.uri().path_and_query().unwrap().as_str();
|
|
||||||
|
|
||||||
if uri.contains("hoyoverse.com") || uri.contains("mihoyo.com") || uri.contains("yuanshen.com") {
|
if uri.contains("hoyoverse.com") || uri.contains("mihoyo.com") || uri.contains("yuanshen.com") {
|
||||||
// Create new URI.
|
// Handle CONNECTs
|
||||||
let new_uri =
|
if req.method().as_str() == "CONNECT" {
|
||||||
Uri::from_str(format!("{}{}", SERVER.lock().unwrap(), uri_path_and_query).as_str())
|
let builder = Response::builder()
|
||||||
.unwrap();
|
.header("DecryptEndpoint", "Created")
|
||||||
// Set request URI to the new one.
|
.status(StatusCode::OK);
|
||||||
*request.uri_mut() = new_uri;
|
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(
|
async fn handle_response(
|
||||||
@@ -73,6 +91,12 @@ impl HttpHandler for ProxyHandler {
|
|||||||
) -> Response<Body> {
|
) -> Response<Body> {
|
||||||
response
|
response
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async fn should_intercept(&mut self, _ctx: &HttpContext, _req: &Request<Body>) -> bool {
|
||||||
|
let uri = _req.uri().to_string();
|
||||||
|
|
||||||
|
uri.contains("hoyoverse.com") || uri.contains("mihoyo.com") || uri.contains("yuanshen.com")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -150,7 +174,8 @@ pub fn connect_to_proxy(proxy_port: u16) {
|
|||||||
let settings = Hive::CurrentUser
|
let settings = Hive::CurrentUser
|
||||||
.open(
|
.open(
|
||||||
r"Software\Microsoft\Windows\CurrentVersion\Internet Settings",
|
r"Software\Microsoft\Windows\CurrentVersion\Internet Settings",
|
||||||
Security::Write,
|
// Only write should be needed but too many cases of Culti not being able to read/write proxy settings
|
||||||
|
Security::AllAccess,
|
||||||
)
|
)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
@@ -197,7 +222,7 @@ pub fn disconnect_from_proxy() {
|
|||||||
let settings = Hive::CurrentUser
|
let settings = Hive::CurrentUser
|
||||||
.open(
|
.open(
|
||||||
r"Software\Microsoft\Windows\CurrentVersion\Internet Settings",
|
r"Software\Microsoft\Windows\CurrentVersion\Internet Settings",
|
||||||
Security::Write,
|
Security::AllAccess,
|
||||||
)
|
)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
@@ -338,7 +363,7 @@ pub fn install_ca_files(cert_path: &Path) {
|
|||||||
// Create dir if it doesn't exist
|
// Create dir if it doesn't exist
|
||||||
fs::create_dir_all(&usr_certs).expect("Unable to create local certificate directory");
|
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");
|
fs::copy(cert_path, usr_cert_path).expect("Unable to copy cert to local certificate directory");
|
||||||
run_command("update-ca-certificates", vec![], None);
|
run_command("update-ca-certificates", vec![], None);
|
||||||
|
|
||||||
println!("Installed certificate.");
|
println!("Installed certificate.");
|
||||||
|
|||||||
33
src-tauri/src/release.rs
Normal file
33
src-tauri/src/release.rs
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
#[derive(serde::Serialize, serde::Deserialize)]
|
||||||
|
pub struct Release {
|
||||||
|
pub tag_name: String,
|
||||||
|
pub link: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[tauri::command]
|
||||||
|
pub async fn get_latest_release() -> Release {
|
||||||
|
let url = "https://api.github.com/repos/Grasscutters/Cultivation/releases/latest";
|
||||||
|
let client = reqwest::Client::new();
|
||||||
|
let response = client
|
||||||
|
.get(url)
|
||||||
|
.header("User-Agent", "Cultivation")
|
||||||
|
.send()
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
let text = response.text().await.unwrap();
|
||||||
|
|
||||||
|
// This includes ip when github rate limits you, so avoid it for now to avoid leaks through screenshots
|
||||||
|
//println!("Response: {}", text);
|
||||||
|
|
||||||
|
// Parse "tag_name" from JSON
|
||||||
|
let json: serde_json::Value = serde_json::from_str(&text).unwrap();
|
||||||
|
let tag_name = json["tag_name"].as_str().unwrap();
|
||||||
|
|
||||||
|
// Parse "html_url"
|
||||||
|
let link = json["html_url"].as_str().unwrap();
|
||||||
|
|
||||||
|
Release {
|
||||||
|
tag_name: tag_name.to_string(),
|
||||||
|
link: link.to_string(),
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,14 +1,25 @@
|
|||||||
use duct::cmd;
|
|
||||||
use ini::Ini;
|
use ini::Ini;
|
||||||
|
use std::ffi::OsStr;
|
||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
|
use std::process::Command;
|
||||||
|
|
||||||
#[cfg(windows)]
|
#[cfg(windows)]
|
||||||
use registry::{Data, Hive, Security};
|
use {
|
||||||
|
registry::{Data, Hive, Security},
|
||||||
|
windows_service::service::{ServiceAccess, ServiceState::Stopped},
|
||||||
|
windows_service::service_manager::{ServiceManager, ServiceManagerAccess},
|
||||||
|
};
|
||||||
|
|
||||||
#[tauri::command]
|
#[tauri::command]
|
||||||
pub fn run_program(path: String, args: Option<String>) {
|
pub fn run_program(path: String, args: Option<String>) {
|
||||||
// Without unwrap_or, this can crash when UAC prompt is denied
|
// Without unwrap_or, this can crash when UAC prompt is denied
|
||||||
open::that(format!("{} {}", &path, &args.unwrap_or_else(|| "".into()))).unwrap_or(());
|
match open::with(
|
||||||
|
format!("{} {}", path, args.unwrap_or_else(|| "".into())),
|
||||||
|
path.clone(),
|
||||||
|
) {
|
||||||
|
Ok(_) => (),
|
||||||
|
Err(e) => println!("Failed to open program ({}): {}", &path, e),
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tauri::command]
|
#[tauri::command]
|
||||||
@@ -49,7 +60,10 @@ pub fn run_command(program: &str, args: Vec<&str>, relative: Option<bool>) {
|
|||||||
std::env::set_current_dir(&path_buf).unwrap();
|
std::env::set_current_dir(&path_buf).unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
cmd(prog, args).run().unwrap();
|
// Run the command
|
||||||
|
let mut command = Command::new(&prog);
|
||||||
|
command.args(&args);
|
||||||
|
command.spawn().unwrap();
|
||||||
|
|
||||||
// Restore the original working directory
|
// Restore the original working directory
|
||||||
std::env::set_current_dir(cwd).unwrap();
|
std::env::set_current_dir(cwd).unwrap();
|
||||||
@@ -64,6 +78,8 @@ pub fn run_jar(path: String, execute_in: String, java_path: String) {
|
|||||||
format!("\"{}\" -jar \"{}\"", java_path, path)
|
format!("\"{}\" -jar \"{}\"", java_path, path)
|
||||||
};
|
};
|
||||||
|
|
||||||
|
println!("Launching .jar with command: {}", &command);
|
||||||
|
|
||||||
// Open the program from the specified path.
|
// Open the program from the specified path.
|
||||||
match open::with(
|
match open::with(
|
||||||
format!("/k cd /D \"{}\" & {}", &execute_in, &command),
|
format!("/k cd /D \"{}\" & {}", &execute_in, &command),
|
||||||
@@ -74,6 +90,22 @@ pub fn run_jar(path: String, execute_in: String, java_path: String) {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[tauri::command]
|
||||||
|
pub fn run_un_elevated(path: String, args: Option<String>) {
|
||||||
|
// Open the program non-elevated.
|
||||||
|
match open::with(
|
||||||
|
format!(
|
||||||
|
"cmd /min /C \"set __COMPAT_LAYER=RUNASINVOKER && start \"\" \"{}\"\" {}",
|
||||||
|
path,
|
||||||
|
args.unwrap_or_else(|| "".into())
|
||||||
|
),
|
||||||
|
"C:\\Windows\\System32\\cmd.exe",
|
||||||
|
) {
|
||||||
|
Ok(_) => (),
|
||||||
|
Err(e) => println!("Failed to open program ({}): {}", &path, e),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
#[tauri::command]
|
#[tauri::command]
|
||||||
pub fn open_in_browser(url: String) {
|
pub fn open_in_browser(url: String) {
|
||||||
// Open the URL in the default browser.
|
// Open the URL in the default browser.
|
||||||
@@ -94,8 +126,45 @@ pub fn install_location() -> String {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[tauri::command]
|
#[tauri::command]
|
||||||
pub fn set_migoto_target(path: String, migoto_path: String) -> bool {
|
pub fn set_migoto_target(window: tauri::Window, migoto_path: String) -> bool {
|
||||||
let pathbuf = PathBuf::from(path);
|
let mut migoto_pathbuf = PathBuf::from(migoto_path);
|
||||||
|
|
||||||
|
migoto_pathbuf.pop();
|
||||||
|
migoto_pathbuf.push("d3dx.ini");
|
||||||
|
|
||||||
|
let mut conf = match Ini::load_from_file(&migoto_pathbuf) {
|
||||||
|
Ok(c) => {
|
||||||
|
println!("Loaded migoto ini");
|
||||||
|
c
|
||||||
|
}
|
||||||
|
Err(e) => {
|
||||||
|
println!("Error loading migoto config: {}", e);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
window.emit("migoto_set", &()).unwrap();
|
||||||
|
|
||||||
|
// Set options
|
||||||
|
conf
|
||||||
|
.with_section(Some("Loader"))
|
||||||
|
.set("target", "GenshinImpact.exe");
|
||||||
|
|
||||||
|
// Write file
|
||||||
|
match conf.write_to_file(&migoto_pathbuf) {
|
||||||
|
Ok(_) => {
|
||||||
|
println!("Wrote config!");
|
||||||
|
true
|
||||||
|
}
|
||||||
|
Err(e) => {
|
||||||
|
println!("Error writing config: {}", e);
|
||||||
|
false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[tauri::command]
|
||||||
|
pub fn set_migoto_delay(migoto_path: String) -> bool {
|
||||||
let mut migoto_pathbuf = PathBuf::from(migoto_path);
|
let mut migoto_pathbuf = PathBuf::from(migoto_path);
|
||||||
|
|
||||||
migoto_pathbuf.pop();
|
migoto_pathbuf.pop();
|
||||||
@@ -113,18 +182,16 @@ pub fn set_migoto_target(path: String, migoto_path: String) -> bool {
|
|||||||
};
|
};
|
||||||
|
|
||||||
// Set options
|
// Set options
|
||||||
conf
|
conf.with_section(Some("Loader")).set("delay", "20");
|
||||||
.with_section(Some("Loader"))
|
|
||||||
.set("target", pathbuf.file_name().unwrap().to_str().unwrap());
|
|
||||||
|
|
||||||
// Write file
|
// Write file
|
||||||
match conf.write_to_file(&migoto_pathbuf) {
|
match conf.write_to_file(&migoto_pathbuf) {
|
||||||
Ok(_) => {
|
Ok(_) => {
|
||||||
println!("Wrote config!");
|
println!("Wrote delay!");
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
println!("Error writing config: {}", e);
|
println!("Error writing delay: {}", e);
|
||||||
false
|
false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -153,6 +220,86 @@ pub fn wipe_registry(exec_name: String) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[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 let Ok(..) = status_result {
|
||||||
|
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(unix)]
|
||||||
|
#[tauri::command]
|
||||||
|
pub fn service_status(_service: String) {}
|
||||||
|
|
||||||
|
#[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(unix)]
|
||||||
|
#[tauri::command]
|
||||||
|
pub fn start_service(_service: String) {
|
||||||
|
let _started = OsStr::new("Started service!");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[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 stop_service(_service: String) {}
|
||||||
|
|
||||||
#[cfg(unix)]
|
#[cfg(unix)]
|
||||||
#[tauri::command]
|
#[tauri::command]
|
||||||
pub fn wipe_registry(_exec_name: String) {}
|
pub fn wipe_registry(_exec_name: String) {}
|
||||||
|
|||||||
@@ -72,6 +72,10 @@ pub fn unzip(
|
|||||||
|
|
||||||
let archive = Archive::new(zipfile.clone());
|
let archive = Archive::new(zipfile.clone());
|
||||||
name = archive.list().unwrap().next().unwrap().unwrap().filename;
|
name = archive.list().unwrap().next().unwrap().unwrap().filename;
|
||||||
|
} else if zipfile.ends_with(".7z") {
|
||||||
|
success = extract_7z(&zipfile, &f, &full_path, top_level.unwrap_or(true));
|
||||||
|
|
||||||
|
name = String::from("banana");
|
||||||
} else {
|
} else {
|
||||||
success = extract_zip(&zipfile, &f, &full_path, top_level.unwrap_or(true));
|
success = extract_zip(&zipfile, &f, &full_path, top_level.unwrap_or(true));
|
||||||
|
|
||||||
@@ -96,31 +100,52 @@ pub fn unzip(
|
|||||||
.unwrap();
|
.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();
|
||||||
|
}
|
||||||
|
|
||||||
|
if zipfile.contains("GrasscutterQuests") {
|
||||||
|
window
|
||||||
|
.emit("jar_extracted", destpath.to_string() + "grasscutter.jar")
|
||||||
|
.unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
if zipfile.contains("GIMI") {
|
||||||
|
window
|
||||||
|
.emit(
|
||||||
|
"migoto_extracted",
|
||||||
|
destpath.to_string() + "3DMigoto Loader.exe",
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
// Delete zip file
|
// Delete zip file
|
||||||
match std::fs::remove_file(&zipfile) {
|
match std::fs::remove_file(&zipfile) {
|
||||||
Ok(_) => {
|
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);
|
println!("Deleted zip file: {}", zipfile);
|
||||||
}
|
}
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
println!("Failed to delete zip file: {}", 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() && !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();
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -163,3 +188,20 @@ fn extract_zip(_zipfile: &str, f: &File, full_path: &path::Path, top_level: bool
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn extract_7z(sevenzfile: &str, _f: &File, full_path: &path::Path, _top_level: bool) -> bool {
|
||||||
|
match sevenz_rust::decompress_file(sevenzfile, full_path) {
|
||||||
|
Ok(_) => {
|
||||||
|
println!(
|
||||||
|
"Extracted 7zip file to: {}",
|
||||||
|
full_path.to_str().unwrap_or("Error")
|
||||||
|
);
|
||||||
|
|
||||||
|
true
|
||||||
|
}
|
||||||
|
Err(e) => {
|
||||||
|
println!("Failed to extract 7zip file: {}", e);
|
||||||
|
false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,16 +1,27 @@
|
|||||||
|
use http::header;
|
||||||
|
use once_cell::sync::Lazy;
|
||||||
use reqwest::header::{CONTENT_TYPE, USER_AGENT};
|
use reqwest::header::{CONTENT_TYPE, USER_AGENT};
|
||||||
|
static CLIENT: Lazy<reqwest::Client> = Lazy::new(|| {
|
||||||
|
let mut headers = header::HeaderMap::new();
|
||||||
|
headers.insert(USER_AGENT, header::HeaderValue::from_static("cultivation"));
|
||||||
|
headers.insert(
|
||||||
|
CONTENT_TYPE,
|
||||||
|
header::HeaderValue::from_static("application/json"),
|
||||||
|
);
|
||||||
|
|
||||||
|
let client = reqwest::Client::builder().default_headers(headers);
|
||||||
|
client.build().unwrap()
|
||||||
|
});
|
||||||
|
|
||||||
pub(crate) async fn query(site: &str) -> String {
|
pub(crate) async fn query(site: &str) -> String {
|
||||||
let client = reqwest::Client::new();
|
CLIENT
|
||||||
|
|
||||||
let response = client
|
|
||||||
.get(site)
|
.get(site)
|
||||||
.header(USER_AGENT, "cultivation")
|
|
||||||
.header(CONTENT_TYPE, "application/json")
|
|
||||||
.send()
|
.send()
|
||||||
.await
|
.await
|
||||||
.unwrap();
|
.expect("Failed to get web response")
|
||||||
response.text().await.unwrap()
|
.text()
|
||||||
|
.await
|
||||||
|
.unwrap()
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tauri::command]
|
#[tauri::command]
|
||||||
@@ -23,9 +34,13 @@ pub(crate) async fn valid_url(url: String) -> bool {
|
|||||||
.header(USER_AGENT, "cultivation")
|
.header(USER_AGENT, "cultivation")
|
||||||
.send()
|
.send()
|
||||||
.await
|
.await
|
||||||
.unwrap();
|
.ok();
|
||||||
|
|
||||||
response.status().as_str() == "200"
|
if response.is_some() {
|
||||||
|
return response.unwrap().status().as_str() == "200";
|
||||||
|
} else {
|
||||||
|
false
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tauri::command]
|
#[tauri::command]
|
||||||
|
|||||||
@@ -7,17 +7,17 @@
|
|||||||
},
|
},
|
||||||
"package": {
|
"package": {
|
||||||
"productName": "Cultivation",
|
"productName": "Cultivation",
|
||||||
"version": "1.0.10"
|
"version": "1.1.1"
|
||||||
},
|
},
|
||||||
"tauri": {
|
"tauri": {
|
||||||
"allowlist": {
|
"allowlist": {
|
||||||
"fs": {
|
"fs": {
|
||||||
"scope": ["$DATA", "$DATA/cultivation", "$DATA/cultivation/*"]
|
"scope": ["$DATA", "$DATA/cultivation", "$DATA/cultivation/**"]
|
||||||
},
|
},
|
||||||
"protocol": {
|
"protocol": {
|
||||||
"all": true,
|
"all": true,
|
||||||
"asset": true,
|
"asset": true,
|
||||||
"assetScope": ["$DATA", "$DATA/cultivation", "$DATA/cultivation/*"]
|
"assetScope": ["$DATA", "$DATA/cultivation", "$DATA/cultivation/**"]
|
||||||
},
|
},
|
||||||
"all": true
|
"all": true
|
||||||
},
|
},
|
||||||
@@ -40,7 +40,7 @@
|
|||||||
"providerShortName": null,
|
"providerShortName": null,
|
||||||
"signingIdentity": null
|
"signingIdentity": null
|
||||||
},
|
},
|
||||||
"resources": ["lang/*.json", "keys/*"],
|
"resources": ["lang/*.json", "keys/*", "patch/*"],
|
||||||
"targets": "all",
|
"targets": "all",
|
||||||
"windows": {
|
"windows": {
|
||||||
"allowDowngrades": false,
|
"allowDowngrades": false,
|
||||||
@@ -53,7 +53,7 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"security": {
|
"security": {
|
||||||
"csp": "default-src 'self' https://asset.localhost; img-src 'self'; img-src https://* asset: https://asset.localhost"
|
"csp": "default-src 'self'; img-src 'self'; img-src https://* asset: https://asset.localhost; media-src https://* asset: https://asset.localhost; style-src-elem https://* asset https://asset.localhost; script-src-elem https://* asset https://asset.localhost;"
|
||||||
},
|
},
|
||||||
"updater": {
|
"updater": {
|
||||||
"active": false,
|
"active": false,
|
||||||
|
|||||||
BIN
src/resources/icons/FALLBACK_BG.jpg
Normal file
BIN
src/resources/icons/FALLBACK_BG.jpg
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 642 KiB |
@@ -107,11 +107,11 @@ select:focus {
|
|||||||
width: 100%;
|
width: 100%;
|
||||||
height: 160px;
|
height: 160px;
|
||||||
|
|
||||||
backdrop-filter: blur(10px);
|
|
||||||
box-shadow: inset 0px 5px 12px -3px rgb(50 50 50 / 75%);
|
|
||||||
|
|
||||||
margin: 0;
|
margin: 0;
|
||||||
padding: 0;
|
padding: 0;
|
||||||
|
|
||||||
|
backdrop-filter: blur(10px);
|
||||||
|
box-shadow: inset 0px 5px 12px -3px rgb(0, 0, 0, 0.43);
|
||||||
}
|
}
|
||||||
|
|
||||||
@media (max-height: 580px) {
|
@media (max-height: 580px) {
|
||||||
|
|||||||
@@ -8,6 +8,9 @@ import { convertFileSrc, invoke } from '@tauri-apps/api/tauri'
|
|||||||
import { Main } from './Main'
|
import { Main } from './Main'
|
||||||
import { Mods } from './Mods'
|
import { Mods } from './Mods'
|
||||||
|
|
||||||
|
// From https://www.pixiv.net/en/artworks/93995273
|
||||||
|
import FALLBACK_BG from '../resources/icons/FALLBACK_BG.jpg'
|
||||||
|
|
||||||
interface IState {
|
interface IState {
|
||||||
page: string
|
page: string
|
||||||
bgFile: string
|
bgFile: string
|
||||||
@@ -35,7 +38,7 @@ class App extends React.Component<Readonly<unknown>, IState> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Get custom bg AFTER theme is loaded !! important !!
|
// Get custom bg AFTER theme is loaded !! important !!
|
||||||
const custom_bg = await getConfigOption('customBackground')
|
const custom_bg = await getConfigOption('custom_background')
|
||||||
|
|
||||||
if (custom_bg) {
|
if (custom_bg) {
|
||||||
const isUrl = /^http(s)?:\/\//gm.test(custom_bg)
|
const isUrl = /^http(s)?:\/\//gm.test(custom_bg)
|
||||||
@@ -64,6 +67,18 @@ class App extends React.Component<Readonly<unknown>, IState> {
|
|||||||
this.forceUpdate
|
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) => {
|
window.addEventListener('changePage', (e) => {
|
||||||
|
|||||||
111
src/ui/Main.tsx
111
src/ui/Main.tsx
@@ -11,20 +11,23 @@ import Downloads from './components/menu/Downloads'
|
|||||||
import NewsSection from './components/news/NewsSection'
|
import NewsSection from './components/news/NewsSection'
|
||||||
import Game from './components/menu/Game'
|
import Game from './components/menu/Game'
|
||||||
import RightBar from './components/RightBar'
|
import RightBar from './components/RightBar'
|
||||||
|
import { ExtrasMenu } from './components/menu/ExtrasMenu'
|
||||||
|
import Notification from './components/common/Notification'
|
||||||
|
import GamePathNotify from './components/menu/GamePathNotify'
|
||||||
|
|
||||||
import { getConfigOption, setConfigOption } from '../utils/configuration'
|
import { getConfigOption, setConfigOption } from '../utils/configuration'
|
||||||
import { invoke } from '@tauri-apps/api'
|
import { invoke } from '@tauri-apps/api'
|
||||||
|
import { getVersion } from '@tauri-apps/api/app'
|
||||||
import { listen } from '@tauri-apps/api/event'
|
import { listen } from '@tauri-apps/api/event'
|
||||||
import { dataDir } from '@tauri-apps/api/path'
|
import { dataDir } from '@tauri-apps/api/path'
|
||||||
import { appWindow } from '@tauri-apps/api/window'
|
import { appWindow } from '@tauri-apps/api/window'
|
||||||
import { unpatchGame } from '../utils/metadata'
|
import { unpatchGame } from '../utils/rsa'
|
||||||
import DownloadHandler from '../utils/download'
|
import DownloadHandler from '../utils/download'
|
||||||
|
|
||||||
// Graphics
|
// Graphics
|
||||||
import cogBtn from '../resources/icons/cog.svg'
|
import cogBtn from '../resources/icons/cog.svg'
|
||||||
import downBtn from '../resources/icons/download.svg'
|
import downBtn from '../resources/icons/download.svg'
|
||||||
import wrenchBtn from '../resources/icons/wrench.svg'
|
import wrenchBtn from '../resources/icons/wrench.svg'
|
||||||
import { ExtrasMenu } from './components/menu/ExtrasMenu'
|
|
||||||
|
|
||||||
interface IProps {
|
interface IProps {
|
||||||
downloadHandler: DownloadHandler
|
downloadHandler: DownloadHandler
|
||||||
@@ -39,6 +42,9 @@ interface IState {
|
|||||||
extrasOpen: boolean
|
extrasOpen: boolean
|
||||||
migotoSet: boolean
|
migotoSet: boolean
|
||||||
playGame: (exe?: string, proc_name?: string) => void
|
playGame: (exe?: string, proc_name?: string) => void
|
||||||
|
notification: React.ReactElement | null
|
||||||
|
isGamePathSet: boolean
|
||||||
|
game_install_path: string
|
||||||
}
|
}
|
||||||
|
|
||||||
export class Main extends React.Component<IProps, IState> {
|
export class Main extends React.Component<IProps, IState> {
|
||||||
@@ -55,6 +61,9 @@ export class Main extends React.Component<IProps, IState> {
|
|||||||
playGame: () => {
|
playGame: () => {
|
||||||
alert('Error launching game')
|
alert('Error launching game')
|
||||||
},
|
},
|
||||||
|
notification: null,
|
||||||
|
isGamePathSet: true,
|
||||||
|
game_install_path: '',
|
||||||
}
|
}
|
||||||
|
|
||||||
listen('lang_error', (payload) => {
|
listen('lang_error', (payload) => {
|
||||||
@@ -65,21 +74,40 @@ export class Main extends React.Component<IProps, IState> {
|
|||||||
setConfigOption('grasscutter_path', payload)
|
setConfigOption('grasscutter_path', payload)
|
||||||
})
|
})
|
||||||
|
|
||||||
// Emitted for metadata replacing-purposes
|
listen('migoto_extracted', ({ payload }: { payload: string }) => {
|
||||||
|
setConfigOption('migoto_path', payload)
|
||||||
|
})
|
||||||
|
|
||||||
|
// Emitted for rsa replacing-purposes
|
||||||
listen('game_closed', async () => {
|
listen('game_closed', async () => {
|
||||||
const wasPatched = await getConfigOption('patch_metadata')
|
const wasPatched = await getConfigOption('patch_rsa')
|
||||||
|
|
||||||
if (wasPatched) {
|
if (wasPatched) {
|
||||||
const unpatched = await unpatchGame()
|
const unpatched = await unpatchGame()
|
||||||
|
|
||||||
if (!unpatched) {
|
if (unpatched) {
|
||||||
alert(
|
alert(`Could not unpatch game! (Delete version.dll in your game folder)`)
|
||||||
`Could not unpatch game! (You should be able to find your metadata backup in ${await dataDir()}\\cultivation\\)`
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
listen('migoto_set', async () => {
|
||||||
|
this.setState({
|
||||||
|
migotoSet: !!(await getConfigOption('migoto_path')),
|
||||||
|
})
|
||||||
|
|
||||||
|
window.location.reload()
|
||||||
|
})
|
||||||
|
|
||||||
|
// 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
|
let min = false
|
||||||
|
|
||||||
// periodically check if we need to min/max based on whether the game is open
|
// periodically check if we need to min/max based on whether the game is open
|
||||||
@@ -99,8 +127,13 @@ export class Main extends React.Component<IProps, IState> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async componentDidMount() {
|
async componentDidMount() {
|
||||||
|
const game_path = await getConfigOption('game_install_path')
|
||||||
const cert_generated = await getConfigOption('cert_generated')
|
const cert_generated = await getConfigOption('cert_generated')
|
||||||
|
|
||||||
|
this.setState({
|
||||||
|
game_install_path: game_path,
|
||||||
|
})
|
||||||
|
|
||||||
this.setState({
|
this.setState({
|
||||||
migotoSet: !!(await getConfigOption('migoto_path')),
|
migotoSet: !!(await getConfigOption('migoto_path')),
|
||||||
})
|
})
|
||||||
@@ -114,6 +147,39 @@ export class Main extends React.Component<IProps, IState> {
|
|||||||
await setConfigOption('cert_generated', true)
|
await setConfigOption('cert_generated', true)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Ensure old configs are updated to use RSA
|
||||||
|
const updatedConfig = await getConfigOption('patch_rsa')
|
||||||
|
await setConfigOption('patch_rsa', updatedConfig)
|
||||||
|
|
||||||
|
// Get latest version and compare to this version
|
||||||
|
const latestVersion: {
|
||||||
|
tag_name: string
|
||||||
|
link: string
|
||||||
|
} = await invoke('get_latest_release')
|
||||||
|
const tagName = latestVersion?.tag_name.replace(/[^\d.]/g, '')
|
||||||
|
|
||||||
|
// Check if tagName is different than current version
|
||||||
|
if (tagName && tagName !== (await getVersion())) {
|
||||||
|
// Display notification of new release
|
||||||
|
this.setState({
|
||||||
|
notification: (
|
||||||
|
<>
|
||||||
|
Cultivation{' '}
|
||||||
|
<a href="#" onClick={() => invoke('open_in_browser', { url: latestVersion.link })}>
|
||||||
|
{latestVersion?.tag_name}
|
||||||
|
</a>{' '}
|
||||||
|
is now available!
|
||||||
|
</>
|
||||||
|
),
|
||||||
|
})
|
||||||
|
|
||||||
|
setTimeout(() => {
|
||||||
|
this.setState({
|
||||||
|
notification: null,
|
||||||
|
})
|
||||||
|
}, 6000)
|
||||||
|
}
|
||||||
|
|
||||||
// Period check to only show progress bar when downloading files
|
// Period check to only show progress bar when downloading files
|
||||||
setInterval(() => {
|
setInterval(() => {
|
||||||
this.setState({
|
this.setState({
|
||||||
@@ -129,6 +195,31 @@ export class Main extends React.Component<IProps, IState> {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async componentDidUpdate(prevProps: Readonly<IProps>, prevState: Readonly<IState>) {
|
||||||
|
const game_path = await getConfigOption('game_install_path')
|
||||||
|
|
||||||
|
// Check if game exists at set location
|
||||||
|
const game_exists: boolean = (await invoke('dir_exists', {
|
||||||
|
path: game_path,
|
||||||
|
})) as boolean
|
||||||
|
|
||||||
|
// Set no game path so the user understands it doesn't exist there
|
||||||
|
if (!game_exists) {
|
||||||
|
setConfigOption('game_install_path', '')
|
||||||
|
}
|
||||||
|
|
||||||
|
//if previous state is not equal the current one - update the game_install_path to be the current game path
|
||||||
|
if (prevState.game_install_path != game_path) {
|
||||||
|
this.setState({
|
||||||
|
game_install_path: game_path,
|
||||||
|
})
|
||||||
|
|
||||||
|
this.state.game_install_path === ''
|
||||||
|
? this.setState({ isGamePathSet: false })
|
||||||
|
: this.setState({ isGamePathSet: true })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
@@ -165,6 +256,10 @@ export class Main extends React.Component<IProps, IState> {
|
|||||||
</div> */}
|
</div> */}
|
||||||
</TopBar>
|
</TopBar>
|
||||||
|
|
||||||
|
<Notification show={!!this.state.notification}>{this.state.notification}</Notification>
|
||||||
|
|
||||||
|
{this.state.isGamePathSet ? <></> : <GamePathNotify />}
|
||||||
|
|
||||||
<RightBar />
|
<RightBar />
|
||||||
|
|
||||||
<NewsSection />
|
<NewsSection />
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
.Mods {
|
.Mods {
|
||||||
backdrop-filter: blur(10px);
|
backdrop-filter: blur(10px);
|
||||||
height: 90%;
|
height: 80%;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -14,6 +14,8 @@ import Back from '../resources/icons/back.svg'
|
|||||||
import Menu from './components/menu/Menu'
|
import Menu from './components/menu/Menu'
|
||||||
import BigButton from './components/common/BigButton'
|
import BigButton from './components/common/BigButton'
|
||||||
import Tr from '../utils/language'
|
import Tr from '../utils/language'
|
||||||
|
import { ModPages } from './components/mods/ModPages'
|
||||||
|
import TextInput from './components/common/TextInput'
|
||||||
|
|
||||||
interface IProps {
|
interface IProps {
|
||||||
downloadHandler: DownloadHandler
|
downloadHandler: DownloadHandler
|
||||||
@@ -23,8 +25,21 @@ interface IState {
|
|||||||
isDownloading: boolean
|
isDownloading: boolean
|
||||||
category: string
|
category: string
|
||||||
downloadList: { name: string; url: string; mod: ModData }[] | null
|
downloadList: { name: string; url: string; mod: ModData }[] | null
|
||||||
|
page: number
|
||||||
|
search: string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const pages = [
|
||||||
|
{
|
||||||
|
name: -1,
|
||||||
|
title: '<',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 1,
|
||||||
|
title: '>',
|
||||||
|
},
|
||||||
|
]
|
||||||
|
|
||||||
const headers = [
|
const headers = [
|
||||||
{
|
{
|
||||||
name: 'ripe',
|
name: 'ripe',
|
||||||
@@ -46,17 +61,22 @@ const headers = [
|
|||||||
* @TODO Categorizaiton/sorting (by likes, views, etc)
|
* @TODO Categorizaiton/sorting (by likes, views, etc)
|
||||||
*/
|
*/
|
||||||
export class Mods extends React.Component<IProps, IState> {
|
export class Mods extends React.Component<IProps, IState> {
|
||||||
|
timeout: number
|
||||||
constructor(props: IProps) {
|
constructor(props: IProps) {
|
||||||
super(props)
|
super(props)
|
||||||
|
this.timeout = 0
|
||||||
|
|
||||||
this.state = {
|
this.state = {
|
||||||
isDownloading: false,
|
isDownloading: false,
|
||||||
category: '',
|
category: '',
|
||||||
downloadList: null,
|
downloadList: null,
|
||||||
|
page: 1,
|
||||||
|
search: '',
|
||||||
}
|
}
|
||||||
|
|
||||||
this.setCategory = this.setCategory.bind(this)
|
this.setCategory = this.setCategory.bind(this)
|
||||||
this.addDownload = this.addDownload.bind(this)
|
this.addDownload = this.addDownload.bind(this)
|
||||||
|
this.setPage = this.setPage.bind(this)
|
||||||
}
|
}
|
||||||
|
|
||||||
async addDownload(mod: ModData) {
|
async addDownload(mod: ModData) {
|
||||||
@@ -111,6 +131,29 @@ export class Mods extends React.Component<IProps, IState> {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async setPage(value: number) {
|
||||||
|
const current = this.state.page
|
||||||
|
if (current + value == 0) return
|
||||||
|
this.setState(
|
||||||
|
{
|
||||||
|
page: current + value,
|
||||||
|
},
|
||||||
|
this.forceUpdate
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
async setSearch(text: string) {
|
||||||
|
if (this.timeout) clearTimeout(this.timeout)
|
||||||
|
this.timeout = window.setTimeout(() => {
|
||||||
|
this.setState(
|
||||||
|
{
|
||||||
|
search: text,
|
||||||
|
},
|
||||||
|
this.forceUpdate
|
||||||
|
)
|
||||||
|
}, 500)
|
||||||
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
return (
|
return (
|
||||||
<div className="Mods">
|
<div className="Mods">
|
||||||
@@ -162,7 +205,30 @@ export class Mods extends React.Component<IProps, IState> {
|
|||||||
|
|
||||||
<ModHeader onChange={this.setCategory} headers={headers} defaultHeader={'ripe'} />
|
<ModHeader onChange={this.setCategory} headers={headers} defaultHeader={'ripe'} />
|
||||||
|
|
||||||
<ModList key={this.state.category} mode={this.state.category} addDownload={this.addDownload} />
|
{this.state.category != 'installed' && (
|
||||||
|
<>
|
||||||
|
<div className="ModPagesPage">
|
||||||
|
<TextInput
|
||||||
|
id="search"
|
||||||
|
key="search"
|
||||||
|
placeholder={this.state.page.toString()}
|
||||||
|
onChange={(text: string) => {
|
||||||
|
this.setSearch(text)
|
||||||
|
}}
|
||||||
|
initalValue={''}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<ModPages onClick={this.setPage} headers={pages} defaultHeader={this.state.page} />
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
|
||||||
|
<ModList
|
||||||
|
key={`${this.state.category}_${this.state.page}_${this.state.search}`}
|
||||||
|
mode={this.state.category}
|
||||||
|
addDownload={this.addDownload}
|
||||||
|
page={this.state.page}
|
||||||
|
search={this.state.search}
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,17 +8,12 @@
|
|||||||
justify-content: flex-start;
|
justify-content: flex-start;
|
||||||
|
|
||||||
height: 100vh;
|
height: 100vh;
|
||||||
width: 80px;
|
width: 70px;
|
||||||
right: 0%;
|
right: 0%;
|
||||||
|
|
||||||
z-index: 99;
|
z-index: 99;
|
||||||
|
|
||||||
background-color: rgba(77, 77, 77, 0.6);
|
background-color: rgba(0, 0, 0, 0.25);
|
||||||
z-index: 999;
|
|
||||||
}
|
|
||||||
|
|
||||||
.BarImg:hover {
|
|
||||||
cursor: pointer;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.RightBarInner > div {
|
.RightBarInner > div {
|
||||||
@@ -26,16 +21,11 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.RightBar img {
|
.RightBar img {
|
||||||
height: 40px;
|
height: 30px;
|
||||||
filter: invert(100%) sepia(0%) saturate(11%) hue-rotate(227deg) brightness(103%) contrast(105%);
|
filter: invert(100%) sepia(0%) saturate(11%) hue-rotate(227deg) brightness(103%) contrast(105%);
|
||||||
|
|
||||||
transition: filter 0.2s ease-in-out;
|
transition: filter 0.2s ease-in-out;
|
||||||
}
|
}
|
||||||
|
|
||||||
.RightBar img:hover {
|
|
||||||
filter: invert(75%) sepia(0%) saturate(100%) hue-rotate(0deg) brightness(100%) contrast(100%);
|
|
||||||
}
|
|
||||||
|
|
||||||
@media (max-height: 580px) {
|
@media (max-height: 580px) {
|
||||||
.RightBar {
|
.RightBar {
|
||||||
height: calc(100vh - 180px);
|
height: calc(100vh - 180px);
|
||||||
@@ -47,3 +37,25 @@
|
|||||||
height: calc(100vh - 170px);
|
height: calc(100vh - 170px);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.BarImg {
|
||||||
|
transition: 0.2s;
|
||||||
|
border-radius: 50%;
|
||||||
|
width: 40px;
|
||||||
|
height: 40px;
|
||||||
|
background-color: rgba(0, 0, 0, 0.685);
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
border: 2px solid transparent;
|
||||||
|
}
|
||||||
|
|
||||||
|
.BarImg:hover {
|
||||||
|
cursor: pointer;
|
||||||
|
border: 2px solid #ffc920;
|
||||||
|
}
|
||||||
|
|
||||||
|
.BarImg:hover img {
|
||||||
|
transition: 0.2s;
|
||||||
|
filter: invert(72%) sepia(68%) saturate(777%) hue-rotate(341deg) brightness(113%) contrast(101%);
|
||||||
|
}
|
||||||
|
|||||||
@@ -20,6 +20,8 @@
|
|||||||
#playButton .BigButton {
|
#playButton .BigButton {
|
||||||
height: 100%;
|
height: 100%;
|
||||||
box-shadow: 0 0 5px 3px rgba(0, 0, 0, 0.2);
|
box-shadow: 0 0 5px 3px rgba(0, 0, 0, 0.2);
|
||||||
|
font-size: 24px;
|
||||||
|
font-weight: 500;
|
||||||
}
|
}
|
||||||
|
|
||||||
#serverControls {
|
#serverControls {
|
||||||
|
|||||||
@@ -12,8 +12,9 @@ import Plus from '../../resources/icons/plus.svg'
|
|||||||
|
|
||||||
import './ServerLaunchSection.css'
|
import './ServerLaunchSection.css'
|
||||||
import { dataDir } from '@tauri-apps/api/path'
|
import { dataDir } from '@tauri-apps/api/path'
|
||||||
import { getGameExecutable, getGameVersion } from '../../utils/game'
|
import { getGameExecutable, getGameVersion, getGrasscutterJar } from '../../utils/game'
|
||||||
import { patchGame, unpatchGame } from '../../utils/metadata'
|
import { patchGame, unpatchGame } from '../../utils/rsa'
|
||||||
|
import { listen } from '@tauri-apps/api/event'
|
||||||
|
|
||||||
interface IProps {
|
interface IProps {
|
||||||
openExtras: (playGame: () => void) => void
|
openExtras: (playGame: () => void) => void
|
||||||
@@ -25,18 +26,19 @@ interface IState {
|
|||||||
checkboxLabel: string
|
checkboxLabel: string
|
||||||
ip: string
|
ip: string
|
||||||
port: string
|
port: string
|
||||||
|
launchServer: (proc_name?: string) => void
|
||||||
|
|
||||||
ipPlaceholder: string
|
ipPlaceholder: string
|
||||||
portPlaceholder: string
|
portPlaceholder: string
|
||||||
|
|
||||||
portHelpText: string
|
|
||||||
|
|
||||||
httpsLabel: string
|
httpsLabel: string
|
||||||
httpsEnabled: boolean
|
httpsEnabled: boolean
|
||||||
|
|
||||||
swag: boolean
|
swag: boolean
|
||||||
akebiSet: boolean
|
akebiSet: boolean
|
||||||
migotoSet: boolean
|
migotoSet: boolean
|
||||||
|
|
||||||
|
unElevated: boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
export default class ServerLaunchSection extends React.Component<IProps, IState> {
|
export default class ServerLaunchSection extends React.Component<IProps, IState> {
|
||||||
@@ -51,12 +53,15 @@ export default class ServerLaunchSection extends React.Component<IProps, IState>
|
|||||||
port: '',
|
port: '',
|
||||||
ipPlaceholder: '',
|
ipPlaceholder: '',
|
||||||
portPlaceholder: '',
|
portPlaceholder: '',
|
||||||
portHelpText: '',
|
|
||||||
httpsLabel: '',
|
httpsLabel: '',
|
||||||
httpsEnabled: false,
|
httpsEnabled: false,
|
||||||
|
launchServer: () => {
|
||||||
|
alert('Error launching grasscutter')
|
||||||
|
},
|
||||||
swag: false,
|
swag: false,
|
||||||
akebiSet: false,
|
akebiSet: false,
|
||||||
migotoSet: false,
|
migotoSet: false,
|
||||||
|
unElevated: false,
|
||||||
}
|
}
|
||||||
|
|
||||||
this.toggleGrasscutter = this.toggleGrasscutter.bind(this)
|
this.toggleGrasscutter = this.toggleGrasscutter.bind(this)
|
||||||
@@ -64,6 +69,11 @@ export default class ServerLaunchSection extends React.Component<IProps, IState>
|
|||||||
this.setIp = this.setIp.bind(this)
|
this.setIp = this.setIp.bind(this)
|
||||||
this.setPort = this.setPort.bind(this)
|
this.setPort = this.setPort.bind(this)
|
||||||
this.toggleHttps = this.toggleHttps.bind(this)
|
this.toggleHttps = this.toggleHttps.bind(this)
|
||||||
|
this.launchServer = this.launchServer.bind(this)
|
||||||
|
|
||||||
|
listen('start_grasscutter', async () => {
|
||||||
|
this.launchServer()
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
async componentDidMount() {
|
async componentDidMount() {
|
||||||
@@ -77,12 +87,12 @@ export default class ServerLaunchSection extends React.Component<IProps, IState>
|
|||||||
port: config.last_port || '',
|
port: config.last_port || '',
|
||||||
ipPlaceholder: await translate('main.ip_placeholder'),
|
ipPlaceholder: await translate('main.ip_placeholder'),
|
||||||
portPlaceholder: await translate('help.port_placeholder'),
|
portPlaceholder: await translate('help.port_placeholder'),
|
||||||
portHelpText: await translate('help.port_help_text'),
|
|
||||||
httpsLabel: await translate('main.https_enable'),
|
httpsLabel: await translate('main.https_enable'),
|
||||||
httpsEnabled: config.https_enabled || false,
|
httpsEnabled: config.https_enabled || false,
|
||||||
swag: config.swag_mode || false,
|
swag: config.swag_mode || false,
|
||||||
akebiSet: config.akebi_path !== '',
|
akebiSet: config.akebi_path !== '',
|
||||||
migotoSet: config.migoto_path !== '',
|
migotoSet: config.migoto_path !== '',
|
||||||
|
unElevated: config.un_elevated || false,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -109,7 +119,7 @@ export default class ServerLaunchSection extends React.Component<IProps, IState>
|
|||||||
|
|
||||||
// Connect to proxy
|
// Connect to proxy
|
||||||
if (config.toggle_grasscutter) {
|
if (config.toggle_grasscutter) {
|
||||||
if (config.patch_metadata) {
|
if (config.patch_rsa) {
|
||||||
const gameVersion = await getGameVersion()
|
const gameVersion = await getGameVersion()
|
||||||
console.log(gameVersion)
|
console.log(gameVersion)
|
||||||
|
|
||||||
@@ -120,24 +130,20 @@ export default class ServerLaunchSection extends React.Component<IProps, IState>
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if (gameVersion?.major == 2 && gameVersion?.minor < 8) {
|
if (gameVersion?.major == 2 && gameVersion?.minor < 9) {
|
||||||
alert(
|
alert('Game version is too old for RSA patching. Please disable RSA patching in the settings and try again.')
|
||||||
'Game version is too old for metadata patching. Please disable metadata patching in the settings and try again.'
|
|
||||||
)
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if (gameVersion?.major == 3 && gameVersion?.minor >= 1) {
|
if (gameVersion?.major == 3 && gameVersion?.minor < 1) {
|
||||||
alert(
|
alert('Game version is too old for RSA patching. Please disable RSA patching in the settings and try again.')
|
||||||
'Game version is too new for metadata patching. Please disable metadata patching in the settings to launch the game.\nNOTE: You will require a UA patch to play the game.'
|
|
||||||
)
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
const patched = await patchGame()
|
const patched = await patchGame()
|
||||||
|
|
||||||
if (!patched) {
|
if (!patched) {
|
||||||
alert('Could not patch game!')
|
alert('Could not patch! Try launching again, or patching manually.')
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -163,26 +169,10 @@ export default class ServerLaunchSection extends React.Component<IProps, IState>
|
|||||||
|
|
||||||
// Open server as well if the options are set
|
// Open server as well if the options are set
|
||||||
if (config.grasscutter_with_game) {
|
if (config.grasscutter_with_game) {
|
||||||
const jarFolderArr = config.grasscutter_path.replace(/\\/g, '/').split('/')
|
this.launchServer()
|
||||||
jarFolderArr.pop()
|
|
||||||
|
|
||||||
const jarFolder = jarFolderArr.join('/')
|
|
||||||
|
|
||||||
await invoke('run_jar', {
|
|
||||||
path: config.grasscutter_path,
|
|
||||||
executeIn: jarFolder,
|
|
||||||
javaPath: config.java_path || '',
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
} else {
|
} 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) {
|
if (config.wipe_login) {
|
||||||
@@ -198,15 +188,35 @@ export default class ServerLaunchSection extends React.Component<IProps, IState>
|
|||||||
path: exe || config.game_install_path,
|
path: exe || config.game_install_path,
|
||||||
})
|
})
|
||||||
|
|
||||||
if (gameExists) await invoke('run_program_relative', { path: exe || config.game_install_path })
|
if (gameExists)
|
||||||
|
if (config.un_elevated) {
|
||||||
|
await invoke('run_un_elevated', {
|
||||||
|
path: config.game_install_path,
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
await invoke('run_program_relative', { path: exe || config.game_install_path })
|
||||||
|
}
|
||||||
else alert('Game not found! At: ' + (exe || config.game_install_path))
|
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()
|
const config = await getConfig()
|
||||||
|
|
||||||
if (!config.grasscutter_path) return alert('Grasscutter not installed or set!')
|
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
|
let jarFolder = config.grasscutter_path
|
||||||
|
|
||||||
if (jarFolder.includes('/')) {
|
if (jarFolder.includes('/')) {
|
||||||
@@ -280,7 +290,7 @@ export default class ServerLaunchSection extends React.Component<IProps, IState>
|
|||||||
onChange={this.setPort}
|
onChange={this.setPort}
|
||||||
initalValue={this.state.port}
|
initalValue={this.state.port}
|
||||||
/>
|
/>
|
||||||
<HelpButton contents={this.state.portHelpText} />
|
<HelpButton contents={'help.port_help_text'} />
|
||||||
<Checkbox
|
<Checkbox
|
||||||
id="httpsEnable"
|
id="httpsEnable"
|
||||||
label={this.state.httpsLabel}
|
label={this.state.httpsLabel}
|
||||||
|
|||||||
@@ -7,7 +7,7 @@
|
|||||||
padding: 0 30px;
|
padding: 0 30px;
|
||||||
border-radius: 5px;
|
border-radius: 5px;
|
||||||
border: none;
|
border: none;
|
||||||
background: linear-gradient(#ffd326, #ffc61e);
|
background: linear-gradient(#ffcf0d, #fec004);
|
||||||
color: #704a1d;
|
color: #704a1d;
|
||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
|
|
||||||
@@ -16,7 +16,7 @@
|
|||||||
|
|
||||||
.BigButton:hover {
|
.BigButton:hover {
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
background: linear-gradient(#ffc61e, #ffd326);
|
background: linear-gradient(#fdd841, #ffc517);
|
||||||
}
|
}
|
||||||
|
|
||||||
.BigButton.disabled {
|
.BigButton.disabled {
|
||||||
|
|||||||
@@ -17,7 +17,7 @@
|
|||||||
|
|
||||||
.CheckboxDisplay img {
|
.CheckboxDisplay img {
|
||||||
height: 100%;
|
height: 100%;
|
||||||
filter: invert(99%) sepia(0%) saturate(1188%) hue-rotate(186deg) brightness(97%) contrast(67%);
|
filter: invert(60%);
|
||||||
}
|
}
|
||||||
|
|
||||||
.Checkbox label {
|
.Checkbox label {
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
.DirInput {
|
.DirInput {
|
||||||
display: flex;
|
display: inline-flex;
|
||||||
flex-direction: row;
|
flex-direction: row;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
|
|||||||
24
src/ui/components/common/Notification.css
Normal file
24
src/ui/components/common/Notification.css
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
.Notification {
|
||||||
|
position: absolute;
|
||||||
|
|
||||||
|
/* Default styles, changed when showing notif */
|
||||||
|
top: -100%;
|
||||||
|
right: 10%;
|
||||||
|
|
||||||
|
padding: 20px;
|
||||||
|
border-radius: 4px;
|
||||||
|
border: 1px solid #ffc61e;
|
||||||
|
|
||||||
|
background-color: #fff;
|
||||||
|
color: #000;
|
||||||
|
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
|
||||||
|
transition: all 0.2s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.NotifShow {
|
||||||
|
top: 10%;
|
||||||
|
}
|
||||||
22
src/ui/components/common/Notification.tsx
Normal file
22
src/ui/components/common/Notification.tsx
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
import React from 'react'
|
||||||
|
|
||||||
|
import './Notification.css'
|
||||||
|
|
||||||
|
interface IProps {
|
||||||
|
children: React.ReactNode | null
|
||||||
|
show: boolean
|
||||||
|
}
|
||||||
|
|
||||||
|
export default class Notification extends React.Component<IProps> {
|
||||||
|
constructor(props: IProps) {
|
||||||
|
super(props)
|
||||||
|
}
|
||||||
|
|
||||||
|
render() {
|
||||||
|
return (
|
||||||
|
<div className={'Notification ' + (this.props.show ? 'NotifShow' : '')}>
|
||||||
|
<div className="NotificationMessage">{this.props.children}</div>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
32
src/ui/components/common/SmallButton.css
Normal file
32
src/ui/components/common/SmallButton.css
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
.SmallButtonSection {
|
||||||
|
display: inline-block;
|
||||||
|
margin-left: 20px;
|
||||||
|
vertical-align: middle;
|
||||||
|
}
|
||||||
|
|
||||||
|
.SmallButtonButton {
|
||||||
|
height: 20px;
|
||||||
|
filter: drop-shadow(0px 0px 5px rgb(0 0 0 / 20%));
|
||||||
|
}
|
||||||
|
|
||||||
|
.SmallButtonButton:hover {
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
.SmallButtonButton img {
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.SmallButtonContents {
|
||||||
|
text-align: center;
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
.SmallButtonContents .MiniDialog {
|
||||||
|
position: absolute;
|
||||||
|
|
||||||
|
bottom: 40px;
|
||||||
|
right: -450%;
|
||||||
|
width: 200px;
|
||||||
|
height: 120px;
|
||||||
|
}
|
||||||
39
src/ui/components/common/SmallButton.tsx
Normal file
39
src/ui/components/common/SmallButton.tsx
Normal file
@@ -0,0 +1,39 @@
|
|||||||
|
import React from 'react'
|
||||||
|
|
||||||
|
import './SmallButton.css'
|
||||||
|
import Wrench from '../../../resources/icons/wrench.svg'
|
||||||
|
import { translate } from '../../../utils/language'
|
||||||
|
|
||||||
|
interface IProps {
|
||||||
|
children?: React.ReactNode[] | React.ReactNode
|
||||||
|
onClick: () => unknown
|
||||||
|
id?: string
|
||||||
|
contents?: string
|
||||||
|
}
|
||||||
|
|
||||||
|
export default class SmallButton extends React.Component<IProps> {
|
||||||
|
constructor(props: IProps) {
|
||||||
|
super(props)
|
||||||
|
|
||||||
|
this.handleClick = this.handleClick.bind(this)
|
||||||
|
}
|
||||||
|
|
||||||
|
async showAlert() {
|
||||||
|
if (this.props.contents) alert(await translate(this.props.contents))
|
||||||
|
}
|
||||||
|
|
||||||
|
handleClick() {
|
||||||
|
this.props.onClick()
|
||||||
|
this.showAlert()
|
||||||
|
}
|
||||||
|
|
||||||
|
render() {
|
||||||
|
return (
|
||||||
|
<div className="SmallButtonSection">
|
||||||
|
<div className="SmallButtonButton" onClick={this.handleClick}>
|
||||||
|
<img src={Wrench} />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -17,6 +17,16 @@
|
|||||||
height: 100%;
|
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 {
|
.DownloadValue .BigButton {
|
||||||
height: 100%;
|
height: 100%;
|
||||||
min-height: 30px;
|
min-height: 30px;
|
||||||
|
|||||||
@@ -13,11 +13,14 @@ import { invoke } from '@tauri-apps/api'
|
|||||||
import { listen } from '@tauri-apps/api/event'
|
import { listen } from '@tauri-apps/api/event'
|
||||||
import HelpButton from '../common/HelpButton'
|
import HelpButton from '../common/HelpButton'
|
||||||
|
|
||||||
|
const FULL_BUILD_DOWNLOAD = 'https://github.com/NotThorny/Grasscutter/releases/download/culti-aio/GrasscutterCulti.zip'
|
||||||
const STABLE_REPO_DOWNLOAD = 'https://github.com/Grasscutters/Grasscutter/archive/refs/heads/stable.zip'
|
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 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 UNSTABLE_DOWNLOAD = 'https://nightly.link/Grasscutters/Grasscutter/workflows/build/unstable/Grasscutter.zip'
|
||||||
const DEV_DOWNLOAD = 'https://nightly.link/Grasscutters/Grasscutter/workflows/build/development/Grasscutter.zip'
|
const DEV_DOWNLOAD = 'https://nightly.link/Grasscutters/Grasscutter/workflows/build/development/Grasscutter.zip'
|
||||||
const RESOURCES_DOWNLOAD = 'https://github.com/tamilpp25/Grasscutter_Resources/archive/refs/heads/3.0.zip'
|
const RESOURCES_DOWNLOAD = 'https://gitlab.com/api/v4/projects/35984297/repository/archive.zip' // Use Yuuki res as grasscutter crepe res are broken
|
||||||
|
const MIGOTO_DOWNLOAD =
|
||||||
|
'https://github.com/SilentNightSound/GI-Model-Importer/releases/download/V7.0/3dmigoto-GIMI-for-playing-mods.zip'
|
||||||
|
|
||||||
interface IProps {
|
interface IProps {
|
||||||
closeFn: () => void
|
closeFn: () => void
|
||||||
@@ -25,11 +28,14 @@ interface IProps {
|
|||||||
}
|
}
|
||||||
|
|
||||||
interface IState {
|
interface IState {
|
||||||
|
fullbuild_downloading: boolean
|
||||||
grasscutter_downloading: boolean
|
grasscutter_downloading: boolean
|
||||||
resources_downloading: boolean
|
resources_downloading: boolean
|
||||||
repo_downloading: boolean
|
repo_downloading: boolean
|
||||||
|
migoto_downloading: boolean
|
||||||
grasscutter_set: boolean
|
grasscutter_set: boolean
|
||||||
resources_exist: boolean
|
resources_exist: boolean
|
||||||
|
swag: boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
export default class Downloads extends React.Component<IProps, IState> {
|
export default class Downloads extends React.Component<IProps, IState> {
|
||||||
@@ -37,24 +43,34 @@ export default class Downloads extends React.Component<IProps, IState> {
|
|||||||
super(props)
|
super(props)
|
||||||
|
|
||||||
this.state = {
|
this.state = {
|
||||||
|
fullbuild_downloading: this.props.downloadManager.downloadingFullBuild(),
|
||||||
grasscutter_downloading: this.props.downloadManager.downloadingJar(),
|
grasscutter_downloading: this.props.downloadManager.downloadingJar(),
|
||||||
resources_downloading: this.props.downloadManager.downloadingResources(),
|
resources_downloading: this.props.downloadManager.downloadingResources(),
|
||||||
repo_downloading: this.props.downloadManager.downloadingRepo(),
|
repo_downloading: this.props.downloadManager.downloadingRepo(),
|
||||||
|
migoto_downloading: this.props.downloadManager.downloadingMigoto(),
|
||||||
grasscutter_set: false,
|
grasscutter_set: false,
|
||||||
resources_exist: false,
|
resources_exist: false,
|
||||||
|
swag: false,
|
||||||
}
|
}
|
||||||
|
|
||||||
this.getGrasscutterFolder = this.getGrasscutterFolder.bind(this)
|
this.getGrasscutterFolder = this.getGrasscutterFolder.bind(this)
|
||||||
|
this.downloadGrasscutterFullBuild = this.downloadGrasscutterFullBuild.bind(this)
|
||||||
this.downloadGrasscutterStableRepo = this.downloadGrasscutterStableRepo.bind(this)
|
this.downloadGrasscutterStableRepo = this.downloadGrasscutterStableRepo.bind(this)
|
||||||
this.downloadGrasscutterDevRepo = this.downloadGrasscutterDevRepo.bind(this)
|
this.downloadGrasscutterDevRepo = this.downloadGrasscutterDevRepo.bind(this)
|
||||||
this.downloadGrasscutterStable = this.downloadGrasscutterStable.bind(this)
|
this.downloadGrasscutterUnstable = this.downloadGrasscutterUnstable.bind(this)
|
||||||
this.downloadGrasscutterLatest = this.downloadGrasscutterLatest.bind(this)
|
this.downloadGrasscutterLatest = this.downloadGrasscutterLatest.bind(this)
|
||||||
this.downloadResources = this.downloadResources.bind(this)
|
this.downloadResources = this.downloadResources.bind(this)
|
||||||
|
this.downloadMigoto = this.downloadMigoto.bind(this)
|
||||||
this.toggleButtons = this.toggleButtons.bind(this)
|
this.toggleButtons = this.toggleButtons.bind(this)
|
||||||
}
|
}
|
||||||
|
|
||||||
async componentDidMount() {
|
async componentDidMount() {
|
||||||
const gc_path = await getConfigOption('grasscutter_path')
|
const gc_path = await getConfigOption('grasscutter_path')
|
||||||
|
const swag = await getConfigOption('swag_mode')
|
||||||
|
|
||||||
|
this.setState({
|
||||||
|
swag: swag || false,
|
||||||
|
})
|
||||||
|
|
||||||
listen('jar_extracted', () => {
|
listen('jar_extracted', () => {
|
||||||
this.setState({ grasscutter_set: true }, this.forceUpdate)
|
this.setState({ grasscutter_set: true }, this.forceUpdate)
|
||||||
@@ -109,6 +125,22 @@ export default class Downloads extends React.Component<IProps, IState> {
|
|||||||
return folderPath
|
return folderPath
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async getCultivationFolder() {
|
||||||
|
const folderPath = (await dataDir()) + 'cultivation'
|
||||||
|
|
||||||
|
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() {
|
async downloadGrasscutterStableRepo() {
|
||||||
const folder = await this.getGrasscutterFolder()
|
const folder = await this.getGrasscutterFolder()
|
||||||
this.props.downloadManager.addDownload(STABLE_REPO_DOWNLOAD, folder + '\\grasscutter_repo.zip', async () => {
|
this.props.downloadManager.addDownload(STABLE_REPO_DOWNLOAD, folder + '\\grasscutter_repo.zip', async () => {
|
||||||
@@ -129,16 +161,13 @@ export default class Downloads extends React.Component<IProps, IState> {
|
|||||||
this.toggleButtons()
|
this.toggleButtons()
|
||||||
}
|
}
|
||||||
|
|
||||||
async downloadGrasscutterStable() {
|
async downloadGrasscutterUnstable() {
|
||||||
const folder = await this.getGrasscutterFolder()
|
const folder = await this.getGrasscutterFolder()
|
||||||
this.props.downloadManager.addDownload(STABLE_DOWNLOAD, folder + '\\grasscutter.zip', async () => {
|
this.props.downloadManager.addDownload(UNSTABLE_DOWNLOAD, folder + '\\grasscutter.zip', async () => {
|
||||||
await unzip(folder + '\\grasscutter.zip', folder + '\\', true)
|
await unzip(folder + '\\grasscutter.zip', folder + '\\', true)
|
||||||
this.toggleButtons
|
this.toggleButtons
|
||||||
})
|
})
|
||||||
|
|
||||||
// Also add repo download
|
|
||||||
this.downloadGrasscutterStableRepo()
|
|
||||||
|
|
||||||
this.toggleButtons()
|
this.toggleButtons()
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -158,6 +187,11 @@ export default class Downloads extends React.Component<IProps, IState> {
|
|||||||
async downloadResources() {
|
async downloadResources() {
|
||||||
const folder = await this.getGrasscutterFolder()
|
const folder = await this.getGrasscutterFolder()
|
||||||
this.props.downloadManager.addDownload(RESOURCES_DOWNLOAD, folder + '\\resources.zip', async () => {
|
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
|
// Delete the existing folder if it exists
|
||||||
if (
|
if (
|
||||||
await invoke('dir_exists', {
|
await invoke('dir_exists', {
|
||||||
@@ -182,14 +216,30 @@ export default class Downloads extends React.Component<IProps, IState> {
|
|||||||
this.toggleButtons()
|
this.toggleButtons()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async downloadMigoto() {
|
||||||
|
const folder = (await this.getCultivationFolder()) + '\\3dmigoto'
|
||||||
|
await invoke('dir_create', {
|
||||||
|
path: folder,
|
||||||
|
})
|
||||||
|
|
||||||
|
this.props.downloadManager.addDownload(MIGOTO_DOWNLOAD, folder + '\\GIMI-3dmigoto.zip', async () => {
|
||||||
|
await unzip(folder + '\\GIMI-3dmigoto.zip', folder + '\\', true)
|
||||||
|
this.toggleButtons()
|
||||||
|
})
|
||||||
|
|
||||||
|
this.toggleButtons()
|
||||||
|
}
|
||||||
|
|
||||||
async toggleButtons() {
|
async toggleButtons() {
|
||||||
const gc_path = await getConfigOption('grasscutter_path')
|
const gc_path = await getConfigOption('grasscutter_path')
|
||||||
|
|
||||||
// Set states since we know we are downloading something if this is called
|
// Set states since we know we are downloading something if this is called
|
||||||
this.setState({
|
this.setState({
|
||||||
|
fullbuild_downloading: this.props.downloadManager.downloadingFullBuild(),
|
||||||
grasscutter_downloading: this.props.downloadManager.downloadingJar(),
|
grasscutter_downloading: this.props.downloadManager.downloadingJar(),
|
||||||
resources_downloading: this.props.downloadManager.downloadingResources(),
|
resources_downloading: this.props.downloadManager.downloadingResources(),
|
||||||
repo_downloading: this.props.downloadManager.downloadingRepo(),
|
repo_downloading: this.props.downloadManager.downloadingRepo(),
|
||||||
|
migoto_downloading: this.props.downloadManager.downloadingMigoto(),
|
||||||
grasscutter_set: gc_path !== '',
|
grasscutter_set: gc_path !== '',
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@@ -197,18 +247,45 @@ export default class Downloads extends React.Component<IProps, IState> {
|
|||||||
render() {
|
render() {
|
||||||
return (
|
return (
|
||||||
<Menu closeFn={this.props.closeFn} className="Downloads" heading="Downloads">
|
<Menu closeFn={this.props.closeFn} className="Downloads" heading="Downloads">
|
||||||
<div className="DownloadMenuSection" id="downloadMenuContainerGCStable">
|
<Divider />
|
||||||
<div className="DownloadLabel" id="downloadMenuLabelGCStable">
|
|
||||||
<Tr
|
<div className="HeaderText" id="downloadMenuAIOHeader">
|
||||||
text={this.state.grasscutter_set ? 'downloads.grasscutter_stable' : 'downloads.grasscutter_stable_update'}
|
<Tr text="downloads.aio_header" />
|
||||||
/>
|
</div>
|
||||||
<HelpButton contents="help.gc_stable_jar" />
|
<div className="DownloadMenuSection" id="downloadMenuContainerGCFullBuild">
|
||||||
|
<div className="DownloadLabel" id="downloadMenuLabelGCFullBuild">
|
||||||
|
<Tr text={'downloads.grasscutter_fullbuild'} />
|
||||||
|
<HelpButton contents="help.gc_fullbuild" />
|
||||||
</div>
|
</div>
|
||||||
<div className="DownloadValue" id="downloadMenuButtonGCStable">
|
<div className="DownloadValue" id="downloadMenuButtonGCFullBuild">
|
||||||
<BigButton
|
<BigButton
|
||||||
disabled={this.state.grasscutter_downloading}
|
disabled={this.state.grasscutter_downloading}
|
||||||
onClick={this.downloadGrasscutterStable}
|
onClick={this.downloadGrasscutterFullBuild}
|
||||||
id="grasscutterStableBtn"
|
id="grasscutterFullBuildBtn"
|
||||||
|
>
|
||||||
|
<Tr text="components.download" />
|
||||||
|
</BigButton>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<Divider />
|
||||||
|
|
||||||
|
<div className="HeaderText" id="downloadMenuIndividualHeader">
|
||||||
|
<Tr text="downloads.individual_header" />
|
||||||
|
</div>
|
||||||
|
<div className="DownloadMenuSection" id="downloadMenuContainerGCUnstable">
|
||||||
|
<div className="DownloadLabel" id="downloadMenuLabelGCUnstable">
|
||||||
|
<Tr
|
||||||
|
text={
|
||||||
|
this.state.grasscutter_set ? 'downloads.grasscutter_unstable' : 'downloads.grasscutter_unstable_update'
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
<HelpButton contents="help.gc_unstable_jar" />
|
||||||
|
</div>
|
||||||
|
<div className="DownloadValue" id="downloadMenuButtonGCUnstable">
|
||||||
|
<BigButton
|
||||||
|
disabled={this.state.grasscutter_downloading}
|
||||||
|
onClick={this.downloadGrasscutterUnstable}
|
||||||
|
id="grasscutterUnstableBtn"
|
||||||
>
|
>
|
||||||
<Tr text="components.download" />
|
<Tr text="components.download" />
|
||||||
</BigButton>
|
</BigButton>
|
||||||
@@ -232,9 +309,7 @@ export default class Downloads extends React.Component<IProps, IState> {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<Divider />
|
{/* <div className="DownloadMenuSection" id="downloadMenuContainerGCStableData">
|
||||||
|
|
||||||
<div className="DownloadMenuSection" id="downloadMenuContainerGCStableData">
|
|
||||||
<div className="DownloadLabel" id="downloadMenuLabelGCStableData">
|
<div className="DownloadLabel" id="downloadMenuLabelGCStableData">
|
||||||
<Tr
|
<Tr
|
||||||
text={
|
text={
|
||||||
@@ -254,7 +329,7 @@ export default class Downloads extends React.Component<IProps, IState> {
|
|||||||
<Tr text="components.download" />
|
<Tr text="components.download" />
|
||||||
</BigButton>
|
</BigButton>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div> */}
|
||||||
<div className="DownloadMenuSection" id="downloadMenuContainerGCDevData">
|
<div className="DownloadMenuSection" id="downloadMenuContainerGCDevData">
|
||||||
<div className="DownloadLabel" id="downloadMenuLabelGCDevData">
|
<div className="DownloadLabel" id="downloadMenuLabelGCDevData">
|
||||||
<Tr
|
<Tr
|
||||||
@@ -277,8 +352,6 @@ export default class Downloads extends React.Component<IProps, IState> {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<Divider />
|
|
||||||
|
|
||||||
<div className="DownloadMenuSection" id="downloadMenuContainerResources">
|
<div className="DownloadMenuSection" id="downloadMenuContainerResources">
|
||||||
<div className="DownloadLabel" id="downloadMenuLabelResources">
|
<div className="DownloadLabel" id="downloadMenuLabelResources">
|
||||||
<Tr text="downloads.resources" />
|
<Tr text="downloads.resources" />
|
||||||
@@ -294,6 +367,26 @@ export default class Downloads extends React.Component<IProps, IState> {
|
|||||||
</BigButton>
|
</BigButton>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
{this.state.swag && (
|
||||||
|
<>
|
||||||
|
<Divider />
|
||||||
|
<div className="HeaderText" id="downloadMenuModsHeader">
|
||||||
|
<Tr text="downloads.mods_header" />
|
||||||
|
</div>
|
||||||
|
<div className="DownloadMenuSection" id="downloadMenuContainerMigoto">
|
||||||
|
<div className="DownloadLabel" id="downloadMenuLabelMigoto">
|
||||||
|
<Tr text={'downloads.migoto'} />
|
||||||
|
<HelpButton contents="help.migoto" />
|
||||||
|
</div>
|
||||||
|
<div className="DownloadValue" id="downloadMenuButtonMigoto">
|
||||||
|
<BigButton disabled={this.state.migoto_downloading} onClick={this.downloadMigoto} id="migotoBtn">
|
||||||
|
<Tr text="components.download" />
|
||||||
|
</BigButton>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
</Menu>
|
</Menu>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
15
src/ui/components/menu/GamePathNotify.css
Normal file
15
src/ui/components/menu/GamePathNotify.css
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
.GameInstallNotify {
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
background-color: rgb(39, 39, 39);
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
|
|
||||||
|
.GameInstallNotify span {
|
||||||
|
padding: 10px 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
#pointer {
|
||||||
|
position: absolute;
|
||||||
|
right: 85px;
|
||||||
|
}
|
||||||
15
src/ui/components/menu/GamePathNotify.tsx
Normal file
15
src/ui/components/menu/GamePathNotify.tsx
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
import React from 'react'
|
||||||
|
import './GamePathNotify.css'
|
||||||
|
import Tr from '../../../utils/language'
|
||||||
|
|
||||||
|
export default class GamePathNotify extends React.Component {
|
||||||
|
render() {
|
||||||
|
return (
|
||||||
|
<div className="GameInstallNotify">
|
||||||
|
<span>
|
||||||
|
<Tr text={'main.game_path_notify'} />
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -3,7 +3,7 @@ import { invoke } from '@tauri-apps/api'
|
|||||||
import { dataDir } from '@tauri-apps/api/path'
|
import { dataDir } from '@tauri-apps/api/path'
|
||||||
import DirInput from '../common/DirInput'
|
import DirInput from '../common/DirInput'
|
||||||
import Menu from './Menu'
|
import Menu from './Menu'
|
||||||
import Tr, { getLanguages, translate } from '../../../utils/language'
|
import Tr, { getLanguages } from '../../../utils/language'
|
||||||
import { setConfigOption, getConfig, getConfigOption, Configuration } from '../../../utils/configuration'
|
import { setConfigOption, getConfig, getConfigOption, Configuration } from '../../../utils/configuration'
|
||||||
import Checkbox from '../common/Checkbox'
|
import Checkbox from '../common/Checkbox'
|
||||||
import Divider from './Divider'
|
import Divider from './Divider'
|
||||||
@@ -13,9 +13,10 @@ import * as server from '../../../utils/server'
|
|||||||
import './Options.css'
|
import './Options.css'
|
||||||
import BigButton from '../common/BigButton'
|
import BigButton from '../common/BigButton'
|
||||||
import DownloadHandler from '../../../utils/download'
|
import DownloadHandler from '../../../utils/download'
|
||||||
import * as meta from '../../../utils/metadata'
|
import * as meta from '../../../utils/rsa'
|
||||||
import HelpButton from '../common/HelpButton'
|
import HelpButton from '../common/HelpButton'
|
||||||
import TextInput from '../common/TextInput'
|
import TextInput from '../common/TextInput'
|
||||||
|
import SmallButton from '../common/SmallButton'
|
||||||
|
|
||||||
interface IProps {
|
interface IProps {
|
||||||
closeFn: () => void
|
closeFn: () => void
|
||||||
@@ -33,12 +34,14 @@ interface IState {
|
|||||||
themes: string[]
|
themes: string[]
|
||||||
theme: string
|
theme: string
|
||||||
encryption: boolean
|
encryption: boolean
|
||||||
patch_metadata: boolean
|
patch_rsa: boolean
|
||||||
use_internal_proxy: boolean
|
use_internal_proxy: boolean
|
||||||
wipe_login: boolean
|
wipe_login: boolean
|
||||||
horny_mode: boolean
|
horny_mode: boolean
|
||||||
|
auto_mongodb: boolean
|
||||||
swag: boolean
|
swag: boolean
|
||||||
platform: string
|
platform: string
|
||||||
|
un_elevated: boolean
|
||||||
|
|
||||||
// Swag stuff
|
// Swag stuff
|
||||||
akebi_path: string
|
akebi_path: string
|
||||||
@@ -61,12 +64,14 @@ export default class Options extends React.Component<IProps, IState> {
|
|||||||
themes: ['default'],
|
themes: ['default'],
|
||||||
theme: '',
|
theme: '',
|
||||||
encryption: false,
|
encryption: false,
|
||||||
patch_metadata: false,
|
patch_rsa: false,
|
||||||
use_internal_proxy: false,
|
use_internal_proxy: false,
|
||||||
wipe_login: false,
|
wipe_login: false,
|
||||||
horny_mode: false,
|
horny_mode: false,
|
||||||
swag: false,
|
swag: false,
|
||||||
|
auto_mongodb: false,
|
||||||
platform: '',
|
platform: '',
|
||||||
|
un_elevated: false,
|
||||||
|
|
||||||
// Swag stuff
|
// Swag stuff
|
||||||
akebi_path: '',
|
akebi_path: '',
|
||||||
@@ -82,7 +87,9 @@ export default class Options extends React.Component<IProps, IState> {
|
|||||||
this.toggleGrasscutterWithGame = this.toggleGrasscutterWithGame.bind(this)
|
this.toggleGrasscutterWithGame = this.toggleGrasscutterWithGame.bind(this)
|
||||||
this.setCustomBackground = this.setCustomBackground.bind(this)
|
this.setCustomBackground = this.setCustomBackground.bind(this)
|
||||||
this.toggleEncryption = this.toggleEncryption.bind(this)
|
this.toggleEncryption = this.toggleEncryption.bind(this)
|
||||||
this.restoreMetadata = this.restoreMetadata.bind(this)
|
this.removeRSA = this.removeRSA.bind(this)
|
||||||
|
this.addMigotoDelay = this.addMigotoDelay.bind(this)
|
||||||
|
this.toggleUnElevatedGame = this.toggleUnElevatedGame.bind(this)
|
||||||
}
|
}
|
||||||
|
|
||||||
async componentDidMount() {
|
async componentDidMount() {
|
||||||
@@ -104,16 +111,18 @@ export default class Options extends React.Component<IProps, IState> {
|
|||||||
grasscutter_with_game: config.grasscutter_with_game || false,
|
grasscutter_with_game: config.grasscutter_with_game || false,
|
||||||
language_options: languages,
|
language_options: languages,
|
||||||
current_language: config.language || 'en',
|
current_language: config.language || 'en',
|
||||||
bg_url_or_path: config.customBackground || '',
|
bg_url_or_path: config.custom_background || '',
|
||||||
themes: (await getThemeList()).map((t) => t.name),
|
themes: (await getThemeList()).map((t) => t.name),
|
||||||
theme: config.theme || 'default',
|
theme: config.theme || 'default',
|
||||||
encryption: await translate(encEnabled ? 'options.enabled' : 'options.disabled'),
|
encryption: encEnabled || false,
|
||||||
patch_metadata: config.patch_metadata || false,
|
patch_rsa: config.patch_rsa || false,
|
||||||
use_internal_proxy: config.use_internal_proxy || false,
|
use_internal_proxy: config.use_internal_proxy || false,
|
||||||
wipe_login: config.wipe_login || false,
|
wipe_login: config.wipe_login || false,
|
||||||
horny_mode: config.horny_mode || false,
|
horny_mode: config.horny_mode || false,
|
||||||
swag: config.swag_mode || false,
|
swag: config.swag_mode || false,
|
||||||
|
auto_mongodb: config.auto_mongodb || false,
|
||||||
platform,
|
platform,
|
||||||
|
un_elevated: config.un_elevated || false,
|
||||||
|
|
||||||
// Swag stuff
|
// Swag stuff
|
||||||
akebi_path: config.akebi_path || '',
|
akebi_path: config.akebi_path || '',
|
||||||
@@ -143,12 +152,24 @@ export default class Options extends React.Component<IProps, IState> {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
setGrasscutterJar(value: string) {
|
async setGrasscutterJar(value: string) {
|
||||||
setConfigOption('grasscutter_path', value)
|
setConfigOption('grasscutter_path', value)
|
||||||
|
|
||||||
this.setState({
|
this.setState({
|
||||||
grasscutter_path: value,
|
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: encEnabled,
|
||||||
|
})
|
||||||
|
|
||||||
|
window.location.reload()
|
||||||
}
|
}
|
||||||
|
|
||||||
setJavaPath(value: string) {
|
setJavaPath(value: string) {
|
||||||
@@ -173,12 +194,6 @@ export default class Options extends React.Component<IProps, IState> {
|
|||||||
this.setState({
|
this.setState({
|
||||||
migoto_path: value,
|
migoto_path: value,
|
||||||
})
|
})
|
||||||
|
|
||||||
// Set game exe in Migoto ini
|
|
||||||
invoke('set_migoto_target', {
|
|
||||||
path: this.state.game_install_path,
|
|
||||||
migotoPath: value,
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
setReshade(value: string) {
|
setReshade(value: string) {
|
||||||
@@ -211,13 +226,13 @@ export default class Options extends React.Component<IProps, IState> {
|
|||||||
async setCustomBackground(value: string) {
|
async setCustomBackground(value: string) {
|
||||||
const isUrl = /^(?:http(s)?:\/\/)/gm.test(value)
|
const isUrl = /^(?:http(s)?:\/\/)/gm.test(value)
|
||||||
|
|
||||||
if (!value) return await setConfigOption('customBackground', '')
|
if (!value) return await setConfigOption('custom_background', '')
|
||||||
|
|
||||||
if (!isUrl) {
|
if (!isUrl) {
|
||||||
const filename = value.replace(/\\/g, '/').split('/').pop()
|
const filename = value.replace(/\\/g, '/').split('/').pop()
|
||||||
const localBgPath = ((await dataDir()) as string).replace(/\\/g, '/')
|
const localBgPath = ((await dataDir()) as string).replace(/\\/g, '/')
|
||||||
|
|
||||||
await setConfigOption('customBackground', `${localBgPath}/cultivation/bg/${filename}`)
|
await setConfigOption('custom_background', `${localBgPath}/cultivation/bg/${filename}`)
|
||||||
|
|
||||||
// Copy the file over to the local directory
|
// Copy the file over to the local directory
|
||||||
await invoke('copy_file', {
|
await invoke('copy_file', {
|
||||||
@@ -227,7 +242,7 @@ export default class Options extends React.Component<IProps, IState> {
|
|||||||
|
|
||||||
window.location.reload()
|
window.location.reload()
|
||||||
} else {
|
} else {
|
||||||
await setConfigOption('customBackground', value)
|
await setConfigOption('custom_background', value)
|
||||||
window.location.reload()
|
window.location.reload()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -248,14 +263,33 @@ export default class Options extends React.Component<IProps, IState> {
|
|||||||
await server.toggleEncryption(folderPath + '/config.json')
|
await server.toggleEncryption(folderPath + '/config.json')
|
||||||
|
|
||||||
this.setState({
|
this.setState({
|
||||||
encryption: await translate(
|
encryption: await server.encryptionEnabled(folderPath + '/config.json'),
|
||||||
(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 toggleUnElevatedGame() {
|
||||||
|
const changedVal = !(await getConfigOption('un_elevated'))
|
||||||
|
setConfigOption('un_elevated', changedVal)
|
||||||
|
|
||||||
|
this.setState({
|
||||||
|
un_elevated: changedVal,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
async restoreMetadata() {
|
async removeRSA() {
|
||||||
await meta.restoreMetadata(this.props.downloadManager)
|
await meta.unpatchGame()
|
||||||
|
}
|
||||||
|
|
||||||
|
async addMigotoDelay() {
|
||||||
|
invoke('set_migoto_delay', {
|
||||||
|
migotoPath: this.state.migoto_path,
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
async installCert() {
|
async installCert() {
|
||||||
@@ -299,26 +333,22 @@ export default class Options extends React.Component<IProps, IState> {
|
|||||||
)}
|
)}
|
||||||
<div className="OptionSection" id="menuOptionsContainermetaDownload">
|
<div className="OptionSection" id="menuOptionsContainermetaDownload">
|
||||||
<div className="OptionLabel" id="menuOptionsLabelmetaDownload">
|
<div className="OptionLabel" id="menuOptionsLabelmetaDownload">
|
||||||
<Tr text="options.recover_metadata" />
|
<Tr text="options.recover_rsa" />
|
||||||
<HelpButton contents="help.emergency_metadata" />
|
<HelpButton contents="help.emergency_rsa" />
|
||||||
</div>
|
</div>
|
||||||
<div className="OptionValue" id="menuOptionsButtonmetaDownload">
|
<div className="OptionValue" id="menuOptionsButtonmetaDownload">
|
||||||
<BigButton onClick={this.restoreMetadata} id="metaDownload">
|
<BigButton onClick={this.removeRSA} id="metaDownload">
|
||||||
<Tr text="components.download" />
|
<Tr text="components.delete" />
|
||||||
</BigButton>
|
</BigButton>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="OptionSection" id="menuOptionsContainerPatchMeta">
|
<div className="OptionSection" id="menuOptionsContainerPatchMeta">
|
||||||
<div className="OptionLabel" id="menuOptionsLabelPatchMeta">
|
<div className="OptionLabel" id="menuOptionsLabelPatchMeta">
|
||||||
<Tr text="options.patch_metadata" />
|
<Tr text="options.patch_rsa" />
|
||||||
<HelpButton contents="help.patch_metadata" />
|
<HelpButton contents="help.patch_rsa" />
|
||||||
</div>
|
</div>
|
||||||
<div className="OptionValue" id="menuOptionsCheckboxPatchMeta">
|
<div className="OptionValue" id="menuOptionsCheckboxPatchMeta">
|
||||||
<Checkbox
|
<Checkbox onChange={() => this.toggleOption('patch_rsa')} checked={this.state?.patch_rsa} id="patchMeta" />
|
||||||
onChange={() => this.toggleOption('patch_metadata')}
|
|
||||||
checked={this.state?.patch_metadata}
|
|
||||||
id="patchMeta"
|
|
||||||
/>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="OptionSection" id="menuOptionsContainerUseProxy">
|
<div className="OptionSection" id="menuOptionsContainerUseProxy">
|
||||||
@@ -346,6 +376,18 @@ export default class Options extends React.Component<IProps, IState> {
|
|||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</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>
|
||||||
|
|
||||||
<Divider />
|
<Divider />
|
||||||
|
|
||||||
@@ -363,9 +405,7 @@ export default class Options extends React.Component<IProps, IState> {
|
|||||||
<HelpButton contents="help.encryption" />
|
<HelpButton contents="help.encryption" />
|
||||||
</div>
|
</div>
|
||||||
<div className="OptionValue" id="menuOptionsButtonToggleEnc">
|
<div className="OptionValue" id="menuOptionsButtonToggleEnc">
|
||||||
<BigButton onClick={this.toggleEncryption} id="toggleEnc">
|
<Checkbox onChange={() => this.toggleEncryption()} checked={this.state.encryption} id="toggleEnc" />
|
||||||
{this.state.encryption}
|
|
||||||
</BigButton>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="OptionSection" id="menuOptionsContainerInstallCert">
|
<div className="OptionSection" id="menuOptionsContainerInstallCert">
|
||||||
@@ -394,6 +434,7 @@ export default class Options extends React.Component<IProps, IState> {
|
|||||||
<Tr text="swag.migoto" />
|
<Tr text="swag.migoto" />
|
||||||
</div>
|
</div>
|
||||||
<div className="OptionValue" id="menuOptionsDirMigoto">
|
<div className="OptionValue" id="menuOptionsDirMigoto">
|
||||||
|
<SmallButton onClick={this.addMigotoDelay} id="migotoDelay" contents="help.add_delay"></SmallButton>
|
||||||
<DirInput onChange={this.setMigoto} value={this.state?.migoto_path} extensions={['exe']} />
|
<DirInput onChange={this.setMigoto} value={this.state?.migoto_path} extensions={['exe']} />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -422,6 +463,18 @@ export default class Options extends React.Component<IProps, IState> {
|
|||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<div className="OptionSection" id="menuOptionsContainerUEGame">
|
||||||
|
<div className="OptionLabel" id="menuOptionsLabelUEGame">
|
||||||
|
<Tr text="options.un_elevated" />
|
||||||
|
</div>
|
||||||
|
<div className="OptionValue" id="menuOptionsCheckboxUEGame">
|
||||||
|
<Checkbox
|
||||||
|
onChange={() => this.toggleOption('un_elevated')}
|
||||||
|
checked={this.state?.un_elevated}
|
||||||
|
id="unElevatedGame"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
{this.state.swag ? (
|
{this.state.swag ? (
|
||||||
<div className="OptionSection" id="menuOptionsContainerHorny">
|
<div className="OptionSection" id="menuOptionsContainerHorny">
|
||||||
<div className="OptionLabel" id="menuOptionsLabelHorny">
|
<div className="OptionLabel" id="menuOptionsLabelHorny">
|
||||||
@@ -483,7 +536,7 @@ export default class Options extends React.Component<IProps, IState> {
|
|||||||
readonly={false}
|
readonly={false}
|
||||||
clearable={true}
|
clearable={true}
|
||||||
customClearBehaviour={async () => {
|
customClearBehaviour={async () => {
|
||||||
await setConfigOption('customBackground', '')
|
await setConfigOption('custom_background', '')
|
||||||
window.location.reload()
|
window.location.reload()
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
|
|||||||
@@ -8,6 +8,8 @@ import { ModTile } from './ModTile'
|
|||||||
|
|
||||||
interface IProps {
|
interface IProps {
|
||||||
mode: string
|
mode: string
|
||||||
|
page: number
|
||||||
|
search: string
|
||||||
addDownload: (mod: ModData) => void
|
addDownload: (mod: ModData) => void
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -62,7 +64,8 @@ export class ModList extends React.Component<IProps, IState> {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
const mods = await getMods(this.props.mode)
|
const mods = await getMods(this.props.mode, this.props.page, this.props.search)
|
||||||
|
|
||||||
const horny = await getConfigOption('horny_mode')
|
const horny = await getConfigOption('horny_mode')
|
||||||
|
|
||||||
this.setState({
|
this.setState({
|
||||||
|
|||||||
66
src/ui/components/mods/ModPages.css
Normal file
66
src/ui/components/mods/ModPages.css
Normal file
@@ -0,0 +1,66 @@
|
|||||||
|
.ModPages {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-around;
|
||||||
|
width: 100%;
|
||||||
|
z-index: 2;
|
||||||
|
position: relative;
|
||||||
|
|
||||||
|
padding: 5px;
|
||||||
|
|
||||||
|
font-size: 20px;
|
||||||
|
font-weight: bold;
|
||||||
|
color: #fff;
|
||||||
|
background: rgba(77, 77, 77, 0.6);
|
||||||
|
}
|
||||||
|
|
||||||
|
.ModPagesTitle {
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
z-index: 3;
|
||||||
|
|
||||||
|
width: 100%;
|
||||||
|
max-width: 20%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ModPagesTitle:hover {
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ModPagesTitle.selected {
|
||||||
|
border-bottom: 0px solid #fff;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ModPagesPage {
|
||||||
|
position: absolute;
|
||||||
|
justify-self: center;
|
||||||
|
left: 50%;
|
||||||
|
margin-top: 10px;
|
||||||
|
transform: translateX(-50%);
|
||||||
|
text-align: center;
|
||||||
|
|
||||||
|
padding: -5px;
|
||||||
|
z-index: 3;
|
||||||
|
|
||||||
|
font-size: 15px;
|
||||||
|
font-weight: bold;
|
||||||
|
color: #fff;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ModPagesPage input {
|
||||||
|
text-align: center;
|
||||||
|
padding: 3px;
|
||||||
|
border-radius: 3px;
|
||||||
|
height: 18px;
|
||||||
|
font-size: 20px;
|
||||||
|
font-weight: bold;
|
||||||
|
color: #fff;
|
||||||
|
background: rgba(77, 77, 77, 0.6);
|
||||||
|
}
|
||||||
|
|
||||||
|
.ModPagesPage .TextInputWrapper {
|
||||||
|
background: rgba(77, 77, 77, 0.6);
|
||||||
|
z-index: -1;
|
||||||
|
display: inline-block;
|
||||||
|
}
|
||||||
54
src/ui/components/mods/ModPages.tsx
Normal file
54
src/ui/components/mods/ModPages.tsx
Normal file
@@ -0,0 +1,54 @@
|
|||||||
|
import React from 'react'
|
||||||
|
|
||||||
|
import './ModPages.css'
|
||||||
|
|
||||||
|
interface IProps {
|
||||||
|
headers: {
|
||||||
|
title: string
|
||||||
|
name: number
|
||||||
|
}[]
|
||||||
|
onClick: (value: number) => void
|
||||||
|
defaultHeader: number
|
||||||
|
}
|
||||||
|
|
||||||
|
interface IState {
|
||||||
|
selected: number
|
||||||
|
}
|
||||||
|
|
||||||
|
export class ModPages extends React.Component<IProps, IState> {
|
||||||
|
constructor(props: IProps) {
|
||||||
|
super(props)
|
||||||
|
|
||||||
|
this.state = {
|
||||||
|
selected: this.props.defaultHeader,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
setSelected(value: number) {
|
||||||
|
const current = this.state.selected
|
||||||
|
if (current + value == 0) return
|
||||||
|
this.setState({
|
||||||
|
selected: current + value,
|
||||||
|
})
|
||||||
|
|
||||||
|
this.props.onClick(value)
|
||||||
|
}
|
||||||
|
|
||||||
|
render() {
|
||||||
|
return (
|
||||||
|
<div className="ModPages">
|
||||||
|
{this.props.headers.map((header, index) => {
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
key={index}
|
||||||
|
className={`ModPagesTitle ${this.state.selected === header.name ? 'selected' : ''}`}
|
||||||
|
onClick={() => this.setSelected(header.name)}
|
||||||
|
>
|
||||||
|
{header.title}
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
})}
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,5 +1,6 @@
|
|||||||
import React from 'react'
|
import React from 'react'
|
||||||
import { ModData, PartialModData } from '../../../utils/gamebanana'
|
import { ModData, PartialModData } from '../../../utils/gamebanana'
|
||||||
|
import { getConfigOption } from '../../../utils/configuration'
|
||||||
|
|
||||||
import './ModTile.css'
|
import './ModTile.css'
|
||||||
import Like from '../../../resources/icons/like.svg'
|
import Like from '../../../resources/icons/like.svg'
|
||||||
@@ -18,6 +19,7 @@ interface IProps {
|
|||||||
}
|
}
|
||||||
|
|
||||||
interface IState {
|
interface IState {
|
||||||
|
horny: boolean
|
||||||
hover: boolean
|
hover: boolean
|
||||||
modEnabled: boolean
|
modEnabled: boolean
|
||||||
}
|
}
|
||||||
@@ -27,6 +29,7 @@ export class ModTile extends React.Component<IProps, IState> {
|
|||||||
super(props)
|
super(props)
|
||||||
|
|
||||||
this.state = {
|
this.state = {
|
||||||
|
horny: false,
|
||||||
hover: false,
|
hover: false,
|
||||||
modEnabled: false,
|
modEnabled: false,
|
||||||
}
|
}
|
||||||
@@ -44,10 +47,13 @@ export class ModTile extends React.Component<IProps, IState> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async componentDidMount() {
|
async componentDidMount() {
|
||||||
|
const horny = await getConfigOption('horny_mode')
|
||||||
|
|
||||||
if (!('id' in this.props.mod)) {
|
if (!('id' in this.props.mod)) {
|
||||||
// Partial mod
|
// Partial mod
|
||||||
this.setState({
|
this.setState({
|
||||||
modEnabled: await modIsEnabled(this.props.mod.name),
|
modEnabled: await modIsEnabled(this.props.mod.name),
|
||||||
|
horny,
|
||||||
})
|
})
|
||||||
|
|
||||||
return
|
return
|
||||||
@@ -55,6 +61,7 @@ export class ModTile extends React.Component<IProps, IState> {
|
|||||||
|
|
||||||
this.setState({
|
this.setState({
|
||||||
modEnabled: await modIsEnabled(String(this.props.mod.id)),
|
modEnabled: await modIsEnabled(String(this.props.mod.id)),
|
||||||
|
horny,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -66,6 +73,7 @@ export class ModTile extends React.Component<IProps, IState> {
|
|||||||
this.setState(
|
this.setState(
|
||||||
{
|
{
|
||||||
modEnabled: !this.state.modEnabled,
|
modEnabled: !this.state.modEnabled,
|
||||||
|
horny: !this.state.horny,
|
||||||
},
|
},
|
||||||
() => {
|
() => {
|
||||||
if (this.state.modEnabled) {
|
if (this.state.modEnabled) {
|
||||||
@@ -108,7 +116,7 @@ export class ModTile extends React.Component<IProps, IState> {
|
|||||||
))}
|
))}
|
||||||
<img
|
<img
|
||||||
src={mod.images[0]}
|
src={mod.images[0]}
|
||||||
className={`ModImageInner ${'id' in mod && !this.props.horny && mod.nsfw ? 'nsfw' : ''} ${
|
className={`ModImageInner ${'id' in mod && !this.state.horny && mod.nsfw ? 'nsfw' : ''} ${
|
||||||
this.state.hover ? 'blur' : ''
|
this.state.hover ? 'blur' : ''
|
||||||
}`}
|
}`}
|
||||||
/>
|
/>
|
||||||
|
|||||||
@@ -50,6 +50,7 @@ export default class NewsSection extends React.Component<IProps, IState> {
|
|||||||
componentDidMount() {
|
componentDidMount() {
|
||||||
// Call showNews off the bat
|
// Call showNews off the bat
|
||||||
this.showNews()
|
this.showNews()
|
||||||
|
this.setSelected('commits')
|
||||||
}
|
}
|
||||||
|
|
||||||
setSelected(item: string) {
|
setSelected(item: string) {
|
||||||
@@ -59,51 +60,52 @@ export default class NewsSection extends React.Component<IProps, IState> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async showLatestCommits() {
|
async showLatestCommits() {
|
||||||
if (!this.state.commitList) {
|
// Just use official API
|
||||||
const response: string = await invoke('req_get', { url: 'https://api.grasscutter.io/cultivation/query' })
|
const response: string = await invoke('req_get', {
|
||||||
let grasscutterApiResponse: GrasscutterAPIResponse | null = null
|
url: 'https://api.github.com/repos/Grasscutters/Grasscutter/commits',
|
||||||
|
})
|
||||||
|
let grasscutterApiResponse: GrasscutterAPIResponse | null = null
|
||||||
|
|
||||||
try {
|
try {
|
||||||
grasscutterApiResponse = JSON.parse(response)
|
grasscutterApiResponse = JSON.parse(response)
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
grasscutterApiResponse = null
|
grasscutterApiResponse = null
|
||||||
}
|
|
||||||
|
|
||||||
let commits: CommitResponse[]
|
|
||||||
if (grasscutterApiResponse?.commits == null) {
|
|
||||||
// If it didn't work, use official API
|
|
||||||
const response: string = await invoke('req_get', {
|
|
||||||
url: 'https://api.github.com/repos/Grasscutters/Grasscutter/commits',
|
|
||||||
})
|
|
||||||
commits = JSON.parse(response)
|
|
||||||
} else {
|
|
||||||
commits = grasscutterApiResponse.commits.gc_stable
|
|
||||||
}
|
|
||||||
|
|
||||||
// Probably rate-limited
|
|
||||||
if (!Array.isArray(commits)) return
|
|
||||||
|
|
||||||
// Get only first 5
|
|
||||||
const commitsList = commits.slice(0, 10)
|
|
||||||
const commitsListHtml = commitsList.map((commitResponse: CommitResponse) => {
|
|
||||||
return (
|
|
||||||
<tr className="Commit" id="newsCommitsTable" key={commitResponse.sha}>
|
|
||||||
<td className="CommitAuthor">
|
|
||||||
<span>{commitResponse.commit.author.name}</span>
|
|
||||||
</td>
|
|
||||||
<td className="CommitMessage">
|
|
||||||
<span>{commitResponse.commit.message}</span>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
)
|
|
||||||
})
|
|
||||||
|
|
||||||
this.setState({
|
|
||||||
commitList: commitsListHtml,
|
|
||||||
news: <>{commitsListHtml}</>,
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let commits: CommitResponse[]
|
||||||
|
if (grasscutterApiResponse?.commits == null) {
|
||||||
|
// If it didn't work, try again anyways
|
||||||
|
const response: string = await invoke('req_get', {
|
||||||
|
url: 'https://api.github.com/repos/Grasscutters/Grasscutter/commits',
|
||||||
|
})
|
||||||
|
commits = JSON.parse(response)
|
||||||
|
} else {
|
||||||
|
commits = grasscutterApiResponse.commits.gc_stable
|
||||||
|
}
|
||||||
|
|
||||||
|
// Probably rate-limited
|
||||||
|
if (!Array.isArray(commits)) return
|
||||||
|
|
||||||
|
// Get only first 5
|
||||||
|
const commitsList = commits.slice(0, 10)
|
||||||
|
const commitsListHtml = commitsList.map((commitResponse: CommitResponse) => {
|
||||||
|
return (
|
||||||
|
<tr className="Commit" id="newsCommitsTable" key={commitResponse.sha}>
|
||||||
|
<td className="CommitAuthor">
|
||||||
|
<span>{commitResponse.commit.author.name}</span>
|
||||||
|
</td>
|
||||||
|
<td className="CommitMessage">
|
||||||
|
<span>{commitResponse.commit.message}</span>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
|
this.setState({
|
||||||
|
commitList: commitsListHtml,
|
||||||
|
news: <>{commitsListHtml}</>,
|
||||||
|
})
|
||||||
|
|
||||||
return this.state.commitList
|
return this.state.commitList
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -122,7 +124,7 @@ export default class NewsSection extends React.Component<IProps, IState> {
|
|||||||
case 'latest_version':
|
case 'latest_version':
|
||||||
news = (
|
news = (
|
||||||
<tr>
|
<tr>
|
||||||
<td>Latest version</td>
|
<td>Latest version: Grasscutter 1.5.0 - Cultivation 1.1.1</td>
|
||||||
</tr>
|
</tr>
|
||||||
)
|
)
|
||||||
break
|
break
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ let configFilePath: string
|
|||||||
let defaultConfig: Configuration
|
let defaultConfig: Configuration
|
||||||
;(async () => {
|
;(async () => {
|
||||||
defaultConfig = {
|
defaultConfig = {
|
||||||
toggle_grasscutter: false,
|
toggle_grasscutter: true,
|
||||||
game_install_path: 'C:\\Program Files\\Genshin Impact\\Genshin Impact game\\GenshinImpact.exe',
|
game_install_path: 'C:\\Program Files\\Genshin Impact\\Genshin Impact game\\GenshinImpact.exe',
|
||||||
grasscutter_with_game: false,
|
grasscutter_with_game: false,
|
||||||
grasscutter_path: '',
|
grasscutter_path: '',
|
||||||
@@ -15,15 +15,17 @@ let defaultConfig: Configuration
|
|||||||
last_ip: 'localhost',
|
last_ip: 'localhost',
|
||||||
last_port: '443',
|
last_port: '443',
|
||||||
language: 'en',
|
language: 'en',
|
||||||
customBackground: '',
|
custom_background: '',
|
||||||
cert_generated: false,
|
cert_generated: false,
|
||||||
theme: 'default',
|
theme: 'default',
|
||||||
https_enabled: false,
|
https_enabled: false,
|
||||||
debug_enabled: false,
|
debug_enabled: false,
|
||||||
patch_metadata: true,
|
patch_rsa: true,
|
||||||
use_internal_proxy: true,
|
use_internal_proxy: true,
|
||||||
wipe_login: false,
|
wipe_login: false,
|
||||||
horny_mode: false,
|
horny_mode: false,
|
||||||
|
auto_mongodb: false,
|
||||||
|
un_elevated: false,
|
||||||
}
|
}
|
||||||
})()
|
})()
|
||||||
|
|
||||||
@@ -41,16 +43,18 @@ export interface Configuration {
|
|||||||
last_ip: string
|
last_ip: string
|
||||||
last_port: string
|
last_port: string
|
||||||
language: string
|
language: string
|
||||||
customBackground: string
|
custom_background: string
|
||||||
cert_generated: boolean
|
cert_generated: boolean
|
||||||
theme: string
|
theme: string
|
||||||
https_enabled: boolean
|
https_enabled: boolean
|
||||||
debug_enabled: boolean
|
debug_enabled: boolean
|
||||||
patch_metadata: boolean
|
patch_rsa: boolean
|
||||||
use_internal_proxy: boolean
|
use_internal_proxy: boolean
|
||||||
wipe_login: boolean
|
wipe_login: boolean
|
||||||
horny_mode: boolean
|
horny_mode: boolean
|
||||||
swag_mode?: boolean
|
swag_mode?: boolean
|
||||||
|
auto_mongodb: boolean
|
||||||
|
un_elevated: boolean
|
||||||
|
|
||||||
// Swag stuff
|
// Swag stuff
|
||||||
akebi_path?: string
|
akebi_path?: string
|
||||||
|
|||||||
@@ -90,7 +90,7 @@ export default class DownloadHandler {
|
|||||||
} = payload
|
} = payload
|
||||||
|
|
||||||
// Find the download that is not extracting and set it's status as such
|
// 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)
|
const index = this.downloads.findIndex((download) => download.path === obj.file)
|
||||||
this.downloads[index].status = 'finished'
|
this.downloads[index].status = 'finished'
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@@ -101,16 +101,25 @@ export default class DownloadHandler {
|
|||||||
|
|
||||||
downloadingJar() {
|
downloadingJar() {
|
||||||
// Kinda hacky but it works
|
// Kinda hacky but it works
|
||||||
return this.downloads.some((d) => d.path.includes('grasscutter.zip'))
|
return this.downloads.some((d) => d.path.includes('grasscutter.zip') && d.status != ('finished' || 'error'))
|
||||||
|
}
|
||||||
|
|
||||||
|
downloadingFullBuild() {
|
||||||
|
// Kinda hacky but it works
|
||||||
|
return this.downloads.some((d) => d.path.includes('GrasscutterCulti') && d.status != ('finished' || 'error'))
|
||||||
}
|
}
|
||||||
|
|
||||||
downloadingResources() {
|
downloadingResources() {
|
||||||
// Kinda hacky but it works
|
// Kinda hacky but it works
|
||||||
return this.downloads.some((d) => d.path.includes('resources'))
|
return this.downloads.some((d) => d.path.includes('resources') && d.status != ('finished' || 'error'))
|
||||||
}
|
}
|
||||||
|
|
||||||
downloadingRepo() {
|
downloadingRepo() {
|
||||||
return this.downloads.some((d) => d.path.includes('grasscutter_repo.zip'))
|
return this.downloads.some((d) => d.path.includes('grasscutter_repo.zip') && d.status != ('finished' || 'error'))
|
||||||
|
}
|
||||||
|
|
||||||
|
downloadingMigoto() {
|
||||||
|
return this.downloads.some((d) => d.path.includes('3dmigoto') && d.status != ('finished' || 'error'))
|
||||||
}
|
}
|
||||||
|
|
||||||
addDownload(url: string, path: string, onFinish?: () => void) {
|
addDownload(url: string, path: string, onFinish?: () => void) {
|
||||||
|
|||||||
@@ -12,6 +12,17 @@ export async function getGameExecutable() {
|
|||||||
return pathArr[pathArr.length - 1]
|
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() {
|
export async function getGameFolder() {
|
||||||
const config = await getConfig()
|
const config = await getConfig()
|
||||||
|
|
||||||
|
|||||||
@@ -117,29 +117,42 @@ interface ModDownload {
|
|||||||
containsExe: boolean
|
containsExe: boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function getMods(mode: string) {
|
export async function getMods(mode: string, page: number, search: string) {
|
||||||
let modList: GamebananaResponse[] = []
|
let modList: GamebananaResponse[] = []
|
||||||
let hadMods = true
|
|
||||||
let page = 1
|
|
||||||
|
|
||||||
while (hadMods) {
|
if (search.length > 0) {
|
||||||
const resp = JSON.parse(
|
let hadMods = true
|
||||||
await invoke('list_submissions', {
|
let page = 1
|
||||||
mode,
|
|
||||||
page: '' + page,
|
|
||||||
})
|
|
||||||
)
|
|
||||||
|
|
||||||
if (resp.length === 0) hadMods = false
|
while (hadMods) {
|
||||||
|
const resp = JSON.parse(
|
||||||
|
await invoke('list_submissions', {
|
||||||
|
mode,
|
||||||
|
page: '' + page,
|
||||||
|
search: search,
|
||||||
|
})
|
||||||
|
)
|
||||||
|
|
||||||
modList = [...modList, ...resp]
|
const total = resp._aMetadata._nRecordCount
|
||||||
page++
|
|
||||||
|
|
||||||
console.log(page)
|
if (page > total / 15) hadMods = false
|
||||||
console.log(resp)
|
|
||||||
|
modList = [...modList, ...resp._aRecords]
|
||||||
|
page++
|
||||||
|
}
|
||||||
|
|
||||||
|
return formatGamebananaData(modList)
|
||||||
}
|
}
|
||||||
|
|
||||||
console.log(modList)
|
const resp = JSON.parse(
|
||||||
|
await invoke('list_submissions', {
|
||||||
|
mode,
|
||||||
|
page: '' + page,
|
||||||
|
search: '',
|
||||||
|
})
|
||||||
|
)
|
||||||
|
|
||||||
|
modList = [...modList, ...resp]
|
||||||
|
|
||||||
return formatGamebananaData(modList)
|
return formatGamebananaData(modList)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,237 +0,0 @@
|
|||||||
import { invoke } from '@tauri-apps/api'
|
|
||||||
import { dataDir } from '@tauri-apps/api/path'
|
|
||||||
import DownloadHandler from './download'
|
|
||||||
import { getGameDataFolder } 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) {
|
|
||||||
// Remove metadata backup, in case it invalid or something
|
|
||||||
await invoke('delete_file', {
|
|
||||||
path: (await getBackupMetadataPath()) + '\\global-metadata-unpatched.dat',
|
|
||||||
})
|
|
||||||
|
|
||||||
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 gameData = await getGameDataFolder()
|
|
||||||
|
|
||||||
if (!gameData) {
|
|
||||||
return null
|
|
||||||
}
|
|
||||||
|
|
||||||
return (gameData + '\\Managed\\Metadata').replace(/\\/g, '/')
|
|
||||||
}
|
|
||||||
|
|
||||||
export async function getBackupMetadataPath() {
|
|
||||||
return (await dataDir()) + 'cultivation\\metadata'
|
|
||||||
}
|
|
||||||
|
|
||||||
export async function globalMetadataLink() {
|
|
||||||
// TODO: Get metadata based on current game version.
|
|
||||||
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
|
|
||||||
}
|
|
||||||
50
src/utils/rsa.ts
Normal file
50
src/utils/rsa.ts
Normal file
@@ -0,0 +1,50 @@
|
|||||||
|
import { invoke } from '@tauri-apps/api'
|
||||||
|
import { getGameFolder } from './game'
|
||||||
|
// Patch file from: https://github.com/34736384/RSAPatch/
|
||||||
|
|
||||||
|
export async function patchGame() {
|
||||||
|
const patchPath = (await invoke('install_location')) + '\\patch\\version.dll'
|
||||||
|
// Are we already patched with mhypbase? If so, that's fine, just continue as normal
|
||||||
|
const gameIsPatched = await invoke('are_files_identical', {
|
||||||
|
path1: patchPath,
|
||||||
|
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: patchPath,
|
||||||
|
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, '/')
|
||||||
|
}
|
||||||
@@ -10,7 +10,7 @@ export async function toggleEncryption(path: string) {
|
|||||||
})
|
})
|
||||||
)
|
)
|
||||||
} catch (e) {
|
} 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
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -36,7 +36,7 @@ export async function encryptionEnabled(path: string) {
|
|||||||
})
|
})
|
||||||
)
|
)
|
||||||
} catch (e) {
|
} 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
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -89,31 +89,35 @@ export async function loadTheme(theme: ThemeList, document: Document) {
|
|||||||
const jsIncludes = theme.includes.js
|
const jsIncludes = theme.includes.js
|
||||||
|
|
||||||
// Load CSS files
|
// Load CSS files
|
||||||
cssIncludes.forEach((css) => {
|
if (cssIncludes) {
|
||||||
if (!css) return
|
cssIncludes?.forEach((css) => {
|
||||||
|
if (!css) return
|
||||||
|
|
||||||
const link = document.createElement('link')
|
const link = document.createElement('link')
|
||||||
|
|
||||||
link.rel = 'stylesheet'
|
link.rel = 'stylesheet'
|
||||||
link.href = convertFileSrc(theme.path + '/' + css)
|
link.href = convertFileSrc(theme.path + '/' + css)
|
||||||
head.appendChild(link)
|
head.appendChild(link)
|
||||||
})
|
})
|
||||||
|
}
|
||||||
|
|
||||||
// Load JS files
|
// Load JS files
|
||||||
jsIncludes.forEach((js) => {
|
if (jsIncludes) {
|
||||||
if (!js) return
|
jsIncludes.forEach((js) => {
|
||||||
|
if (!js) return
|
||||||
|
|
||||||
const script = document.createElement('script')
|
const script = document.createElement('script')
|
||||||
|
|
||||||
script.src = convertFileSrc(theme.path + '/' + js)
|
script.src = convertFileSrc(theme.path + '/' + js)
|
||||||
head.appendChild(script)
|
head.appendChild(script)
|
||||||
})
|
})
|
||||||
|
}
|
||||||
|
|
||||||
// Set custom background
|
// Set custom background
|
||||||
if (theme.customBackgroundURL) {
|
if (theme.customBackgroundURL) {
|
||||||
// If the custom bg is already set don't overwrite
|
// If the custom bg is already set don't overwrite
|
||||||
if (config.customBackground === '') {
|
if (config.custom_background === '') {
|
||||||
config.customBackground = theme.customBackgroundURL
|
config.custom_background = theme.customBackgroundURL
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -130,13 +134,13 @@ export async function loadTheme(theme: ThemeList, document: Document) {
|
|||||||
|
|
||||||
// Set the background
|
// Set the background
|
||||||
// If the custom bg is already set don't overwrite
|
// If the custom bg is already set don't overwrite
|
||||||
if (config.customBackground === '') {
|
if (config.custom_background === '') {
|
||||||
config.customBackground = bgPath + imageName
|
config.custom_background = bgPath + imageName
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Write config
|
// Write config
|
||||||
await setConfigOption('customBackground', config.customBackground)
|
await setConfigOption('custom_background', config.custom_background)
|
||||||
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|||||||
48
yarn.lock
48
yarn.lock
@@ -2385,7 +2385,7 @@ acorn@^7.0.0, acorn@^7.1.1:
|
|||||||
resolved "https://registry.yarnpkg.com/acorn/-/acorn-7.4.1.tgz#feaed255973d2e77555b83dbc08851a6c63520fa"
|
resolved "https://registry.yarnpkg.com/acorn/-/acorn-7.4.1.tgz#feaed255973d2e77555b83dbc08851a6c63520fa"
|
||||||
integrity sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A==
|
integrity sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A==
|
||||||
|
|
||||||
acorn@^8.2.4, acorn@^8.4.1, acorn@^8.5.0, acorn@^8.7.1:
|
acorn@^8.2.4, acorn@^8.5.0, acorn@^8.7.1:
|
||||||
version "8.7.1"
|
version "8.7.1"
|
||||||
resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.7.1.tgz#0197122c843d1bf6d0a5e83220a788f278f63c30"
|
resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.7.1.tgz#0197122c843d1bf6d0a5e83220a788f278f63c30"
|
||||||
integrity sha512-Xx54uLJQZ19lKygFXOWsscKUbsBZW0CPykPhVQdhIeIwrbPmJzqeASDInc8nKBnp/JT6igTs82qPXz069H8I/A==
|
integrity sha512-Xx54uLJQZ19lKygFXOWsscKUbsBZW0CPykPhVQdhIeIwrbPmJzqeASDInc8nKBnp/JT6igTs82qPXz069H8I/A==
|
||||||
@@ -3570,9 +3570,9 @@ decimal.js@^10.2.1:
|
|||||||
integrity sha512-V0pfhfr8suzyPGOx3nmq4aHqabehUZn6Ch9kyFpV79TGDTWFmHqUqXdabR7QHqxzrYolF4+tVmJhUG4OURg5dQ==
|
integrity sha512-V0pfhfr8suzyPGOx3nmq4aHqabehUZn6Ch9kyFpV79TGDTWFmHqUqXdabR7QHqxzrYolF4+tVmJhUG4OURg5dQ==
|
||||||
|
|
||||||
decode-uri-component@^0.2.0:
|
decode-uri-component@^0.2.0:
|
||||||
version "0.2.0"
|
version "0.2.2"
|
||||||
resolved "https://registry.yarnpkg.com/decode-uri-component/-/decode-uri-component-0.2.0.tgz#eb3913333458775cb84cd1a1fae062106bb87545"
|
resolved "https://registry.yarnpkg.com/decode-uri-component/-/decode-uri-component-0.2.2.tgz#e69dbe25d37941171dd540e024c444cd5188e1e9"
|
||||||
integrity sha512-hjf+xovcEn31w/EUYdTXQh/8smFL/dzYjohQGEIgjyNavaJfBY2p5F527Bo1VPATxv0VYTUC2bOcXvqFwk78Og==
|
integrity sha512-FqUYQ+8o158GyGTrMFJms9qh3CqTKvAqgqsTnkLI8sKu0028orqBhxNMFkFen0zGyg6epACD32pjVk58ngIErQ==
|
||||||
|
|
||||||
dedent@^0.7.0:
|
dedent@^0.7.0:
|
||||||
version "0.7.0"
|
version "0.7.0"
|
||||||
@@ -3859,10 +3859,10 @@ encodeurl@~1.0.2:
|
|||||||
resolved "https://registry.yarnpkg.com/encodeurl/-/encodeurl-1.0.2.tgz#ad3ff4c86ec2d029322f5a02c3a9a606c95b3f59"
|
resolved "https://registry.yarnpkg.com/encodeurl/-/encodeurl-1.0.2.tgz#ad3ff4c86ec2d029322f5a02c3a9a606c95b3f59"
|
||||||
integrity sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==
|
integrity sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==
|
||||||
|
|
||||||
enhanced-resolve@^5.9.3:
|
enhanced-resolve@^5.10.0:
|
||||||
version "5.10.0"
|
version "5.12.0"
|
||||||
resolved "https://registry.yarnpkg.com/enhanced-resolve/-/enhanced-resolve-5.10.0.tgz#0dc579c3bb2a1032e357ac45b8f3a6f3ad4fb1e6"
|
resolved "https://registry.yarnpkg.com/enhanced-resolve/-/enhanced-resolve-5.12.0.tgz#300e1c90228f5b570c4d35babf263f6da7155634"
|
||||||
integrity sha512-T0yTFjdpldGY8PmuXXR0PyQ1ufZpEGiHVrp7zHKB7jdR4qlmZHhONVM5AQOAWXuF/w3dnHbEQVrNptJgt7F+cQ==
|
integrity sha512-QHTXI/sZQmko1cbDoNAa3mJ5qhWUUNAq3vR0/YiD379fWQrcfuoX1+HW2S0MTt7XmoPLapdaDKUtelUSPic7hQ==
|
||||||
dependencies:
|
dependencies:
|
||||||
graceful-fs "^4.2.4"
|
graceful-fs "^4.2.4"
|
||||||
tapable "^2.2.0"
|
tapable "^2.2.0"
|
||||||
@@ -5867,9 +5867,9 @@ json-stable-stringify-without-jsonify@^1.0.1:
|
|||||||
integrity sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==
|
integrity sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==
|
||||||
|
|
||||||
json5@^1.0.1:
|
json5@^1.0.1:
|
||||||
version "1.0.1"
|
version "1.0.2"
|
||||||
resolved "https://registry.yarnpkg.com/json5/-/json5-1.0.1.tgz#779fb0018604fa854eacbf6252180d83543e3dbe"
|
resolved "https://registry.yarnpkg.com/json5/-/json5-1.0.2.tgz#63d98d60f21b313b77c4d6da18bfa69d80e1d593"
|
||||||
integrity sha512-aKS4WQjPenRxiQsC93MNfjx+nbF4PAdYzmd/1JIj8HYzqfbu86beTuNgXDzPknWk0n0uARlyewZo4s++ES36Ow==
|
integrity sha512-g1MWMLBiz8FKi1e4w0UyVL3w+iJceWAFBAaBnnGKOpNa5f8TLktkbre1+s6oICydWAm+HRUGTmI+//xv2hvXYA==
|
||||||
dependencies:
|
dependencies:
|
||||||
minimist "^1.2.0"
|
minimist "^1.2.0"
|
||||||
|
|
||||||
@@ -6002,9 +6002,9 @@ loader-runner@^4.2.0:
|
|||||||
integrity sha512-3R/1M+yS3j5ou80Me59j7F9IMs4PXs3VqRrm0TU3AbKPxlmpoY1TNscJV/oGJXo8qCatFGTfDbY6W6ipGOYXfg==
|
integrity sha512-3R/1M+yS3j5ou80Me59j7F9IMs4PXs3VqRrm0TU3AbKPxlmpoY1TNscJV/oGJXo8qCatFGTfDbY6W6ipGOYXfg==
|
||||||
|
|
||||||
loader-utils@^2.0.0:
|
loader-utils@^2.0.0:
|
||||||
version "2.0.2"
|
version "2.0.4"
|
||||||
resolved "https://registry.yarnpkg.com/loader-utils/-/loader-utils-2.0.2.tgz#d6e3b4fb81870721ae4e0868ab11dd638368c129"
|
resolved "https://registry.yarnpkg.com/loader-utils/-/loader-utils-2.0.4.tgz#8b5cb38b5c34a9a018ee1fc0e6a066d1dfcc528c"
|
||||||
integrity sha512-TM57VeHptv569d/GKh6TAYdzKblwDNiumOdkFnejjD0XwTH87K90w3O7AiJRqdQoXygvi1VQTJTLGhJl7WqA7A==
|
integrity sha512-xXqpXoINfFhgua9xiqD8fPFHgkoq1mmmpE92WlDbm9rNRd/EbRb+Gqf908T2DMfuHjjJlksiK2RbHVOdD/MqSw==
|
||||||
dependencies:
|
dependencies:
|
||||||
big.js "^5.2.2"
|
big.js "^5.2.2"
|
||||||
emojis-list "^3.0.0"
|
emojis-list "^3.0.0"
|
||||||
@@ -6248,9 +6248,9 @@ minimatch@^5.0.1:
|
|||||||
brace-expansion "^2.0.1"
|
brace-expansion "^2.0.1"
|
||||||
|
|
||||||
minimist@^1.2.0, minimist@^1.2.6:
|
minimist@^1.2.0, minimist@^1.2.6:
|
||||||
version "1.2.6"
|
version "1.2.7"
|
||||||
resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.6.tgz#8637a5b759ea0d6e98702cfb3a9283323c93af44"
|
resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.7.tgz#daa1c4d91f507390437c6a8bc01078e7000c4d18"
|
||||||
integrity sha512-Jsjnk4bw3YJqYzbdyBiNsPWHPfO++UGG749Cxs6peCu5Xg4nrena6OVxOYxrQTqww0Jmwt+Ref8rggumkTLz9Q==
|
integrity sha512-bzfL1YUZsP41gmu/qjrEk0Q6i2ix/cVeAhbCbqH9u3zYutS1cLg00qhrD0M2MVdCcx4Sc0UpP2eBWo9rotpq6g==
|
||||||
|
|
||||||
mkdirp@~0.5.1:
|
mkdirp@~0.5.1:
|
||||||
version "0.5.6"
|
version "0.5.6"
|
||||||
@@ -8826,7 +8826,7 @@ walker@^1.0.7:
|
|||||||
dependencies:
|
dependencies:
|
||||||
makeerror "1.0.12"
|
makeerror "1.0.12"
|
||||||
|
|
||||||
watchpack@^2.3.1:
|
watchpack@^2.4.0:
|
||||||
version "2.4.0"
|
version "2.4.0"
|
||||||
resolved "https://registry.yarnpkg.com/watchpack/-/watchpack-2.4.0.tgz#fa33032374962c78113f93c7f2fb4c54c9862a5d"
|
resolved "https://registry.yarnpkg.com/watchpack/-/watchpack-2.4.0.tgz#fa33032374962c78113f93c7f2fb4c54c9862a5d"
|
||||||
integrity sha512-Lcvm7MGST/4fup+ifyKi2hjyIAwcdI4HRgtvTpIUxBRhB+RFtUh8XtDOxUfctVCnhVi+QQj49i91OyvzkJl6cg==
|
integrity sha512-Lcvm7MGST/4fup+ifyKi2hjyIAwcdI4HRgtvTpIUxBRhB+RFtUh8XtDOxUfctVCnhVi+QQj49i91OyvzkJl6cg==
|
||||||
@@ -8937,20 +8937,20 @@ webpack-sources@^3.2.3:
|
|||||||
integrity sha512-/DyMEOrDgLKKIG0fmvtz+4dUX/3Ghozwgm6iPp8KRhvn+eQf9+Q7GWxVNMk3+uCPWfdXYC4ExGBckIXdFEfH1w==
|
integrity sha512-/DyMEOrDgLKKIG0fmvtz+4dUX/3Ghozwgm6iPp8KRhvn+eQf9+Q7GWxVNMk3+uCPWfdXYC4ExGBckIXdFEfH1w==
|
||||||
|
|
||||||
webpack@^5.64.4:
|
webpack@^5.64.4:
|
||||||
version "5.73.0"
|
version "5.76.1"
|
||||||
resolved "https://registry.yarnpkg.com/webpack/-/webpack-5.73.0.tgz#bbd17738f8a53ee5760ea2f59dce7f3431d35d38"
|
resolved "https://registry.yarnpkg.com/webpack/-/webpack-5.76.1.tgz#7773de017e988bccb0f13c7d75ec245f377d295c"
|
||||||
integrity sha512-svjudQRPPa0YiOYa2lM/Gacw0r6PvxptHj4FuEKQ2kX05ZLkjbVc5MnPs6its5j7IZljnIqSVo/OsY2X0IpHGA==
|
integrity sha512-4+YIK4Abzv8172/SGqObnUjaIHjLEuUasz9EwQj/9xmPPkYJy2Mh03Q/lJfSD3YLzbxy5FeTq5Uw0323Oh6SJQ==
|
||||||
dependencies:
|
dependencies:
|
||||||
"@types/eslint-scope" "^3.7.3"
|
"@types/eslint-scope" "^3.7.3"
|
||||||
"@types/estree" "^0.0.51"
|
"@types/estree" "^0.0.51"
|
||||||
"@webassemblyjs/ast" "1.11.1"
|
"@webassemblyjs/ast" "1.11.1"
|
||||||
"@webassemblyjs/wasm-edit" "1.11.1"
|
"@webassemblyjs/wasm-edit" "1.11.1"
|
||||||
"@webassemblyjs/wasm-parser" "1.11.1"
|
"@webassemblyjs/wasm-parser" "1.11.1"
|
||||||
acorn "^8.4.1"
|
acorn "^8.7.1"
|
||||||
acorn-import-assertions "^1.7.6"
|
acorn-import-assertions "^1.7.6"
|
||||||
browserslist "^4.14.5"
|
browserslist "^4.14.5"
|
||||||
chrome-trace-event "^1.0.2"
|
chrome-trace-event "^1.0.2"
|
||||||
enhanced-resolve "^5.9.3"
|
enhanced-resolve "^5.10.0"
|
||||||
es-module-lexer "^0.9.0"
|
es-module-lexer "^0.9.0"
|
||||||
eslint-scope "5.1.1"
|
eslint-scope "5.1.1"
|
||||||
events "^3.2.0"
|
events "^3.2.0"
|
||||||
@@ -8963,7 +8963,7 @@ webpack@^5.64.4:
|
|||||||
schema-utils "^3.1.0"
|
schema-utils "^3.1.0"
|
||||||
tapable "^2.1.1"
|
tapable "^2.1.1"
|
||||||
terser-webpack-plugin "^5.1.3"
|
terser-webpack-plugin "^5.1.3"
|
||||||
watchpack "^2.3.1"
|
watchpack "^2.4.0"
|
||||||
webpack-sources "^3.2.3"
|
webpack-sources "^3.2.3"
|
||||||
|
|
||||||
websocket-driver@>=0.5.1, websocket-driver@^0.7.4:
|
websocket-driver@>=0.5.1, websocket-driver@^0.7.4:
|
||||||
|
|||||||
Reference in New Issue
Block a user