mirror of
https://github.com/Grasscutters/GrassClipper.git
synced 2024-11-21 22:58:24 +00:00
Merge branch 'main' of https://github.com/actuallyeunha/GrassClipper
This commit is contained in:
commit
5e80d59ed7
29
.eslintrc.json
Normal file
29
.eslintrc.json
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
{
|
||||||
|
"env": {
|
||||||
|
"browser": true,
|
||||||
|
"commonjs": true,
|
||||||
|
"es2021": true
|
||||||
|
},
|
||||||
|
"extends": "eslint:recommended",
|
||||||
|
"parserOptions": {
|
||||||
|
"ecmaVersion": 13
|
||||||
|
},
|
||||||
|
"rules": {
|
||||||
|
"no-undef": 0,
|
||||||
|
"no-unused-vars": 0,
|
||||||
|
"no-case-declarations": 0,
|
||||||
|
|
||||||
|
"indent": [
|
||||||
|
"error",
|
||||||
|
2
|
||||||
|
],
|
||||||
|
"quotes": [
|
||||||
|
"error",
|
||||||
|
"single"
|
||||||
|
],
|
||||||
|
"semi": [
|
||||||
|
"error",
|
||||||
|
"never"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
1
.gitignore
vendored
1
.gitignore
vendored
@ -4,6 +4,7 @@ bin/
|
|||||||
dist/
|
dist/
|
||||||
ext/
|
ext/
|
||||||
temp/
|
temp/
|
||||||
|
gc*/
|
||||||
resources/js/neutralino.js
|
resources/js/neutralino.js
|
||||||
resources/bg/official
|
resources/bg/official
|
||||||
|
|
||||||
|
103
README.md
103
README.md
@ -1,7 +1,9 @@
|
|||||||
# GrassClipper
|
# GrassClipper
|
||||||
Grasscutter launcher for easily switching between Official and Private servers
|
Experimental Grasscutter launcher for easily switching between Official and Private servers
|
||||||
|
|
||||||
[Download Here!](https://github.com/Grasscutters/GrassClipper/releases/)
|
[Download Here!](https://github.com/Grasscutters/GrassClipper/releases/) (Supports Windows 8+)
|
||||||
|
|
||||||
|
*\*Note: some translations are outdated, so if random English text appears or an option seems misleading, this is why. If you notice an issue like this, feel free to make a pull request!*
|
||||||
|
|
||||||
# Table of Contents
|
# Table of Contents
|
||||||
|
|
||||||
@ -9,60 +11,132 @@ Grasscutter launcher for easily switching between Official and Private servers
|
|||||||
* [Setup (for development)](#setup-for-development)
|
* [Setup (for development)](#setup-for-development)
|
||||||
* [TODO](#todo)
|
* [TODO](#todo)
|
||||||
* [Common Problems](#having-problems)
|
* [Common Problems](#having-problems)
|
||||||
|
* [Proxy Installation not Opening/Failing](#manual-proxy-installation)
|
||||||
* [White Screen Fix](#white-screen-fix)
|
* [White Screen Fix](#white-screen-fix)
|
||||||
|
* [Error 502](#error-502)
|
||||||
|
* [Error 4206](#error-4206)
|
||||||
|
* [Infinite CMD Windows](#infinite-cmd-windows)
|
||||||
* [Broken Discord/Youtube](#my-discord-is-not-letting-me-send-messages-or-load-images-my-youtube-is-acting-strange)
|
* [Broken Discord/Youtube](#my-discord-is-not-letting-me-send-messages-or-load-images-my-youtube-is-acting-strange)
|
||||||
* [No internet](#i-have-no-internet-after-closing-everything-restarting-my-pc)
|
* [No internet](#i-have-no-internet-after-closing-everything-restarting-my-pc)
|
||||||
|
* [Languages and Translation Credits](#available-languages-and-translation-credits)
|
||||||
* [Screenshots](#screenshots)
|
* [Screenshots](#screenshots)
|
||||||
|
|
||||||
# Setup (for Users)
|
# Setup (for Users)
|
||||||
|
|
||||||
1. Download the zip file
|
1. Download the zip file
|
||||||
2. Extract the zip file somewhere
|
2. Extract the zip file somewhere
|
||||||
3. Run `GrassClipper.exe`, install the proxy server, and set your `Genshin Impact Game` folder!
|
3. Run `GrassClipper.exe`, install the proxy server, and set your game folder!
|
||||||
|
|
||||||
# Setup (for Development)
|
# Setup (for Development)
|
||||||
|
|
||||||
0. Clone the repository
|
0. Clone the repository
|
||||||
1. Ensure you have [NodeJS](https://nodejs.org/en/download/) installed.
|
1. Ensure you have [NodeJS](https://nodejs.org/en/download/) installed.
|
||||||
2. Install the `neu` CLI tool: `npm install -g @neutralinojs/neu`
|
2. Install the `neu` CLI tool: `npm install -g @neutralinojs/neu`
|
||||||
3. Install the dependencies: `npm install` AND `neu update`
|
3. Install the dependencies: `setup_win.cmd`
|
||||||
4. Compile and run:
|
4. Compile and run:
|
||||||
* For testing: `npm run dev`
|
* For testing: `npm run dev`
|
||||||
* For production: `npm run build`
|
* For production: `npm run build`
|
||||||
|
|
||||||
# TODO
|
# TODO
|
||||||
|
|
||||||
* Interface
|
* Interface/internals
|
||||||
* [x] UI
|
* [x] UI
|
||||||
* [x] Official and Private options
|
* [x] Official and Private options
|
||||||
* [x] Server IP input
|
* [x] Server IP input
|
||||||
* [x] Fun fancy CSS styling n stuff (CoD: MW 2019-style vertical menu for choosing between official and private servers? [See this](https://charlieintel.com/wp-content/uploads/2020/11/MW-new-menu.png))
|
* [x] Fun fancy CSS styling n stuff (CoD: MW 2019-style vertical menu for choosing between official and private servers? [See this](https://charlieintel.com/wp-content/uploads/2020/11/MW-new-menu.png))
|
||||||
* [ ] Custom images for private server sections (anyone is welcome to submit a pull request to add some!)
|
|
||||||
* [ ] Optional username/password creation for servers before entering (not implemented in GrassCutter yet)
|
|
||||||
* [x] Kill switch script (optional)
|
* [x] Kill switch script (optional)
|
||||||
* [x] Automatically run `install.cmd` when opening for the first time
|
* [x] Automatically run `install.cmd` when opening for the first time
|
||||||
* [ ] Fix Windows scaling issues?
|
* [x] Grasscutter auto-downloader
|
||||||
|
* [ ] Detect when in a folder that is inaccessible to the program (eg. `C:/Program Files`) and warn
|
||||||
|
* [ ] Custom images for private server sections (anyone is welcome to submit a pull request to add some!)
|
||||||
|
* [x] Optional username/password creation for servers before entering (not implemented in Grasscutter yet)
|
||||||
|
* [ ] Platform detection and bash scripts
|
||||||
|
* [ ] Integrated banner creator
|
||||||
* Proxy service
|
* Proxy service
|
||||||
* [x] Local proxy server
|
* [x] Local proxy server
|
||||||
* [x] Intercept and modify GI requests like with Fiddler, allow anything else to pass through
|
* [x] Intercept and modify GI requests like with Fiddler, allow anything else to pass through
|
||||||
* [ ] Fix Discord and YouTube issues when proxy is enabled (not sure what's up with them?)
|
* [ ] Fix Discord and YouTube issues when proxy is enabled (maybe fixed)
|
||||||
|
|
||||||
# Having problems?
|
# Having problems?
|
||||||
|
|
||||||
Below are some scenarios you may encounter and their solutions.
|
Below are some scenarios you may encounter and their solutions.
|
||||||
|
|
||||||
### White Screen Fix
|
# Manual Proxy Installation
|
||||||
|
|
||||||
Encountering a white screen? [Ensure WebView2 is installed](https://developer.microsoft.com/zh-cn/microsoft-edge/webview2/#download)
|
If you having trouble installing the proxy server, you can also install it manually. To do so:
|
||||||
|
1. Create a folder if it does not exist named `ext` in the GrassClipper folder.
|
||||||
|
2. Download and extract the contents of [this file](https://snapshots.mitmproxy.org/7.0.4/mitmproxy-7.0.4-windows.zip) into the `ext` folder
|
||||||
|
3. Double click `mitmdump.exe` and allow it to run for a few seconds to generate the certificate
|
||||||
|
4. Run this command as Administrator: `certutil -addstore root "%USERPROFILE%\.mitmproxy\mitmproxy-ca-cert.cer"`
|
||||||
|
5. Use GrassClipper like normal!
|
||||||
|
|
||||||
### My Discord is not letting me send messages or load images/My Youtube is acting strange!
|
## White Screen Fix
|
||||||
|
|
||||||
|
Encountering a white screen? [Ensure WebView2 is installed](https://developer.microsoft.com/en-us/microsoft-edge/webview2/#download)
|
||||||
|
|
||||||
|
You may also want to run this command as administrator:
|
||||||
|
`CheckNetIsolation.exe LoopbackExempt -a -n="Microsoft.Win32WebViewHost_cw5n1h2txyewy"`
|
||||||
|
|
||||||
|
If you have Chinese characters in your file path, this may crash it too! I am working on a fix.
|
||||||
|
|
||||||
|
You can also try running in Windows 8 compatitbility mode.
|
||||||
|
|
||||||
|
If all else fails, you can run GrassClipper in `chrome` or `browser` mode. To do so:
|
||||||
|
* Create a shortcut to `GrassClipper.exe`
|
||||||
|
* Right click the shortcut, click `properties`
|
||||||
|
* In the `Target` box, at the very end, add ` --mode=chrome` or ` --mode=browser`
|
||||||
|
* `chrome` only works if you have Chrome installed, and will create a Chrome window
|
||||||
|
* `brower` will, you guessed it, open GrassClipper in your default browser
|
||||||
|
* Click `Ok`
|
||||||
|
* Run GrassClipper using this shortcut from now on!
|
||||||
|
|
||||||
|
## Error 502
|
||||||
|
|
||||||
|
1. If you are running a local server, ensure the local server is running. Otherwise, ensure the server you are connecting to is actually running.
|
||||||
|
|
||||||
|
2. If you are able, [use the development branch of Grasscutter](https://github.com/Grasscutters/Grasscutter/tree/development). It is known to work better with GrassClipper.
|
||||||
|
|
||||||
|
If you are still getting an error 502 when attempting to log in to your own server, open your Grasscutter config and add the following to the `DispatchServer` section:
|
||||||
|
|
||||||
|
```json
|
||||||
|
"PublicPort": YOUR_PORT
|
||||||
|
```
|
||||||
|
where `YOUR_PORT` is the same port you use as the `Port` value already. This will probably be 443.
|
||||||
|
|
||||||
|
## Error 4206
|
||||||
|
|
||||||
|
Ensure you have the correct `keystore.p12` file that comes with your branch (`stable` or `development`). Also ensure the password is set properly in Grasscutters `config.json` (blank for `stable`, "123456" for `development`).
|
||||||
|
|
||||||
|
## Infinite CMD Windows
|
||||||
|
|
||||||
|
If you are getting infinite CMD windows for any of the scripts (such as the proxy installation, or private server start), ensure you have UAC (user access control) set to any option that requires asking. Ensure your user account can open things as Admin.
|
||||||
|
|
||||||
|
## My Discord is not letting me send messages or load images/My Youtube is acting strange!
|
||||||
|
|
||||||
Discord/YouTube (plus surely some others) does not seem to be a fan of the proxy server. You may need to disable it by either closing mitmdump or by disabling your proxy in the Windows proxy settings.
|
Discord/YouTube (plus surely some others) does not seem to be a fan of the proxy server. You may need to disable it by either closing mitmdump or by disabling your proxy in the Windows proxy settings.
|
||||||
|
|
||||||
### I have no internet after closing everything/restarting my PC!
|
## I have no internet after closing everything/restarting my PC!
|
||||||
|
|
||||||
The launcher most likely did not close correctly, and was unable to clean your proxy settings back to what they were. Disable your proxy in the Windows proxy settings.
|
The launcher most likely did not close correctly, and was unable to clean your proxy settings back to what they were. Disable your proxy in the Windows proxy settings.
|
||||||
|
|
||||||
|
# Available Languages and Translation Credits
|
||||||
|
|
||||||
|
Thank you to everyone who has provided translations! <3
|
||||||
|
|
||||||
|
* ZH - nuoxianCN, Scirese & MrAru
|
||||||
|
* ZH-TW - Kimi & KormiMeiko
|
||||||
|
* PT-BR - na.na
|
||||||
|
* VIE - labalityowo & lunaticwhat
|
||||||
|
* ID - Iqrar99 & nautilust
|
||||||
|
* FR - linsorak & memetrollsXD
|
||||||
|
* ES - memetrollsXD
|
||||||
|
* ND - memetrollsXD
|
||||||
|
* RU - fitiskin
|
||||||
|
* TR - lilmayofuksu
|
||||||
|
* JP - conochy
|
||||||
|
* HD - Arikatsu
|
||||||
|
* PL - zakhil-dev
|
||||||
|
|
||||||
# Screenshots
|
# Screenshots
|
||||||
|
|
||||||
![image](https://user-images.githubusercontent.com/25207995/164574276-645548c2-7ba6-47c3-8df4-77082003648f.png)
|
![image](https://user-images.githubusercontent.com/25207995/164574276-645548c2-7ba6-47c3-8df4-77082003648f.png)
|
||||||
@ -70,6 +144,7 @@ The launcher most likely did not close correctly, and was unable to clean your p
|
|||||||
![image](https://user-images.githubusercontent.com/25207995/164393040-4da72f29-6d59-4af4-bd60-072269f2ba2a.png)
|
![image](https://user-images.githubusercontent.com/25207995/164393040-4da72f29-6d59-4af4-bd60-072269f2ba2a.png)
|
||||||
![image](https://user-images.githubusercontent.com/25207995/164393024-56543ddf-7063-4c04-9a9f-0c6238f30e90.png)
|
![image](https://user-images.githubusercontent.com/25207995/164393024-56543ddf-7063-4c04-9a9f-0c6238f30e90.png)
|
||||||
![image](https://user-images.githubusercontent.com/25207995/164393118-de844e75-f9a2-491a-aea6-f2d563abecc7.png)
|
![image](https://user-images.githubusercontent.com/25207995/164393118-de844e75-f9a2-491a-aea6-f2d563abecc7.png)
|
||||||
![image](https://user-images.githubusercontent.com/25207995/164574339-50bbda5e-e25a-47c3-ae14-2b0b0b4ca92c.png)
|
![image](https://user-images.githubusercontent.com/25207995/164882735-77aa535c-0e93-4b32-af7c-f8b59888257a.png)
|
||||||
|
![image](https://user-images.githubusercontent.com/25207995/164882716-c9f16cd0-c0b6-4c0a-ae9e-4c95da9ef7f5.png)
|
||||||
|
|
||||||
|
|
||||||
|
150
README_PL.md
Normal file
150
README_PL.md
Normal file
@ -0,0 +1,150 @@
|
|||||||
|
# GrassClipper
|
||||||
|
Experymentalny launcher Grasscutter'a stworzony dla łatwego przełączania się pomiędzy serwerami oficjalnymi, a prywatnymi
|
||||||
|
|
||||||
|
[Pobierz tutaj!](https://github.com/Grasscutters/GrassClipper/releases/) (Wspiera Windows'a 8+)
|
||||||
|
|
||||||
|
*\*Uwaga: niektóre tłumaczenia są nie aktualne, więc jeżeli widzisz tekst po angielsku, lub jakiś tekst wprowadza w błąd, to to jest powodem. W przypadku zauważenia takiego błędu, śmiało otwórz pull request'a!*
|
||||||
|
|
||||||
|
# Spis treści
|
||||||
|
|
||||||
|
* [Instalacja (dla użytkownika)](#instalacja-dla-użytkownika)
|
||||||
|
* [Instalacja (dla developera)](#instalacja-dla-developera)
|
||||||
|
* [Do zrobienia](#do-zrobienia)
|
||||||
|
* [Częste problmy](#napotkałeś-problem?)
|
||||||
|
* [Instalacja proxy nie uruchamia się / nie powodzi się](#ręczna-instalacja-proxy)
|
||||||
|
* [Naprawa białego ekranu](#naprawa-białego-ekranu)
|
||||||
|
* [Błąd 502](#błąd-502)
|
||||||
|
* [Błąd 4206](#błąd-4206)
|
||||||
|
* [Nieskończone okna CMD](#nieskończone-okna-CMD)
|
||||||
|
* [Niedziałający Discord/Youtube](#discord-nie-pozwala-wysyłać-mi-wiadomości-lub-wczytywać-obrazków/YouTube-dziwnie-się-zachowuje)
|
||||||
|
* [Brak internetu](#nie-mam-dostępu-do-internetu-po-zamknięciu-wszystkiego/restarcie-komputera!)
|
||||||
|
* [Dostępne tłumaczenia oraz ich autorzy](#dostępne-tłumaczenia-oraz-ich-autorzy)
|
||||||
|
* [Zrzuty ekranu](#zrzuty-ekranu)
|
||||||
|
|
||||||
|
# Instalacja (dla użytkownika)
|
||||||
|
|
||||||
|
1. Pobierz plik zip
|
||||||
|
2. Wypakuj gdzieś plik zip
|
||||||
|
3. Uruchom `GrassClipper.exe`, zainstaluj serwer proxy oraz ustaw folder z grą!
|
||||||
|
|
||||||
|
# Instalacja (dla developera)
|
||||||
|
|
||||||
|
0. Sklonuj repozytorium
|
||||||
|
1. Upewnij się, że masz zainstalowanego [NodeJS'a](https://nodejs.org/en/download/).
|
||||||
|
2. Zainstaluj narzędzie `neu` CLI: `npm install -g @neutralinojs/neu`
|
||||||
|
3. Zainstaluj zależności: `setup_win.cmd`
|
||||||
|
4. Skompiluj i uruchom:
|
||||||
|
* Testowanie: `npm run dev`
|
||||||
|
* Produkcja: `npm run build`
|
||||||
|
|
||||||
|
# Do zrobienia
|
||||||
|
|
||||||
|
* Interfejst/wewnętrzne
|
||||||
|
* [x] UI
|
||||||
|
* [x] Opcje Oficjalny i Prywatny
|
||||||
|
* [x] Wprowadzanie IP serwera
|
||||||
|
* [x] Stylowanie CSS - Pionowe menu do wybierania pomiędzy oficjalnym, a prywatnym serwerem? (Stylizowane na CoD: MW 2019) [Zobacz tutaj](https://charlieintel.com/wp-content/uploads/2020/11/MW-new-menu.png))
|
||||||
|
* [x] Skrypt Kill Switch (opcjonalne)
|
||||||
|
* [x] Automatyczne uruchamianie `install.cmd`, jeżeli uruchamamy program po raz pierwszy
|
||||||
|
* [x] Auto-pobieranie Grasscutter'a
|
||||||
|
* [ ] Wykrywanie gdy program jest uruchomiony w folderze, do którego nie ma dostępu (np. `C:/Program Files`) i wysyłanie ostrzeżenia.
|
||||||
|
* [ ] Nowe obrazki dla sekcji serwera prywatnego (Pull request'y od wszystkich są mile widziane!)
|
||||||
|
* [x] Opcjonalne tworzenie loginu/hasła dla serwera przed połączeniem (nie zaimplementowane jeszcze w Grasscutter)
|
||||||
|
* [ ] Wykrywanie systemu i skrypty bash
|
||||||
|
* [ ] Zintegrowany kreator bannerów
|
||||||
|
* Usługa proxy
|
||||||
|
* [x] Lokalny serwer proxy
|
||||||
|
* [x] Przechwytywanie i modyfikowanie zapytań GI, np. przy użyciu Fiddler'a, przepuszczanie wszystkigo innego.
|
||||||
|
* [ ] Naprawienie problemów z Discordem i YouTube'm gdy proxy jest włączone (może naprawione)
|
||||||
|
|
||||||
|
# Napotkałeś problem?
|
||||||
|
|
||||||
|
Poniżej znajdują się najczęściej pojawiające się problemy oraz ich rozwiązania.
|
||||||
|
|
||||||
|
## Ręczna instalacja proxy
|
||||||
|
|
||||||
|
Jeżeli napotkałeś problemy z automatyczną instalacją proxy, możesz zainstalować je ręcznie. <br>
|
||||||
|
W tym celu:
|
||||||
|
1. Jeżeli nie istnieje stwórz folder `ext` w folderze GrassClipper'a.
|
||||||
|
2. Pobierz i wypakuj zawartość [tego pliku](https://snapshots.mitmproxy.org/7.0.4/mitmproxy-7.0.4-windows.zip) do folderu `ext`
|
||||||
|
3. Uruchom `mitmdump.exe` i poczekaj kilka chwil, żeby wygenerował certyfikat.
|
||||||
|
4. Wywołaj to polecenie jako Administrator: `certutil -addstore root "%USERPROFILE%\.mitmproxy\mitmproxy-ca-cert.cer"`
|
||||||
|
5. GrassClipper powinien działać poprawnie!
|
||||||
|
|
||||||
|
## Naprawa białego ekranu
|
||||||
|
|
||||||
|
Napotkałeś biały ekran? Upewnij się czy [WebView2](https://developer.microsoft.com/en-us/microsoft-edge/webview2/#download) jest zainstalowany.
|
||||||
|
|
||||||
|
Wywołanie tego polecenia jako Administrator również może pomóc:
|
||||||
|
`CheckNetIsolation.exe LoopbackExempt -a -n="Microsoft.Win32WebViewHost_cw5n1h2txyewy"`
|
||||||
|
|
||||||
|
Jeżeli w twojej ścieżce do pliku występują chińskie symbole, to mogą one być tego przyczyną. Pracujemy nad poprawką.
|
||||||
|
|
||||||
|
Możesz spróbować uruchomić aplikacje w trybie zgodności z Windows'em 8
|
||||||
|
|
||||||
|
Jeżeli nic z tego nie pomoże, możesz uruchomić GrassClipper'a w trybie `chrome` lub `browser`. W tym celu:
|
||||||
|
* Stwórz skrót do `GrassClipper.exe`
|
||||||
|
* Kliknij na niego prawym przyciskiem, wybierz `Właściwości`
|
||||||
|
* W polu `Element docelowy`, na samym końcu, dodaj ` --mode=chrome` lub ` --mode=browser`
|
||||||
|
* `chrome` działa tylko wtedy gdy masz zainstalowanego Chrome'a, otworzy okienko Chrome'a
|
||||||
|
* `brower` otworzy GrassClippera w twojej domyślnej przeglądarce
|
||||||
|
* Kliknij `Ok`
|
||||||
|
* Od teraz uruchamiaj GrassClippera nowo stworzonym skrótem.
|
||||||
|
|
||||||
|
## Błąd 502
|
||||||
|
|
||||||
|
1. Jeżeli dołączas na serwer lokalny to upewnij się czy jest włączony. W przeciwnym wypadku, upewnij się, czy serwer do którego się łączysz działa.
|
||||||
|
|
||||||
|
2. Jeżeli to możliwe, [użyj wersji developerskiej Grasscutter'a](https://github.com/Grasscutters/Grasscutter/tree/development). Z tego co wiadomo to działa lepiej z GrassClipper'em.
|
||||||
|
|
||||||
|
Jeżeli dalej dostajesz błąd 502 przy próbie logowania do własnego serwera, otwórz plik `config.json` w folderze Grasscutter'a i dodaj w nim w sekcji `DispatchServer`:
|
||||||
|
|
||||||
|
```json
|
||||||
|
"PublicPort": TWÓJ_PORT
|
||||||
|
```
|
||||||
|
|
||||||
|
`TWÓJ_PORT` jest tym samym portem co w polu `Port`. Jest to prawdopodobnie wartość 443.
|
||||||
|
|
||||||
|
## Błąd 4206
|
||||||
|
|
||||||
|
Upewnij się, że posiadasz odpowiedni plik `keystore.p12` dla twojej wersji. Sprawdź, czy hasło jest poprawnie ustawione w pliku `config.json` (puste dla wersji `stabilnej`, "123456" dla wersji `developerskiej`).
|
||||||
|
|
||||||
|
## Nieskończone okna CMD
|
||||||
|
|
||||||
|
Jeżeli jakikolwiek skrypt uruchamia nieskończoność okien CMD (np. instalator proxy albo starter serwera prywatnego), upewnij się, że UAC (User Access Control) jest ustawiony na dowolną opcje która wymaga potwierdzenia. Sprawdź czy twoje konto może uruchamiać programy jako Administrator.
|
||||||
|
|
||||||
|
## Discord nie pozwala wysyłać mi wiadomości lub wczytywać obrazków/YouTube dziwnie się zachowuje
|
||||||
|
|
||||||
|
Discord/Youtube (i na pewno pare innych) nie przepadają za serwerem proxy. Musisz go wyłączyć zamykając mitmdump, albo wyłączając proxy w ustawieniach Windowsa.
|
||||||
|
|
||||||
|
## Nie mam dostępu do internetu po zamknięciu wszystkiego/restarcie komputera!
|
||||||
|
|
||||||
|
Launcher najprawdopodobniej nie został poprawnie zamknięty, przez co nie był w stanie przywrócić oryginalnych ustawień proxy. Wyłącz proxy w ustawieniach Windows'a.
|
||||||
|
|
||||||
|
# Dostępne tłumaczenia oraz ich autorzy
|
||||||
|
|
||||||
|
Dziękujemy dla wszystkich, którzy pomagają w tłumaczeniu! <3
|
||||||
|
|
||||||
|
* ZH - nuoxianCN, Scirese & MrAru
|
||||||
|
* ZH-TW - Kimi & KormiMeiko
|
||||||
|
* PT-BR - na.na
|
||||||
|
* VIE - labalityowo
|
||||||
|
* ID - Iqrar99 & nautilust
|
||||||
|
* FR - linsorak & memetrollsXD
|
||||||
|
* ES - memetrollsXD
|
||||||
|
* ND - memetrollsXD
|
||||||
|
* RU - fitiskin
|
||||||
|
* TR - lilmayofuksu
|
||||||
|
* JP - conochy
|
||||||
|
* HD - Arikatsu
|
||||||
|
* PL - zakhil-dev
|
||||||
|
|
||||||
|
# Zrzuty ekranu
|
||||||
|
|
||||||
|
![image](https://user-images.githubusercontent.com/25207995/164574276-645548c2-7ba6-47c3-8df4-77082003648f.png)
|
||||||
|
![image](https://user-images.githubusercontent.com/25207995/164393190-f7e6633c-60bd-4186-bf0c-30d9f30871f4.png)
|
||||||
|
![image](https://user-images.githubusercontent.com/25207995/164393040-4da72f29-6d59-4af4-bd60-072269f2ba2a.png)
|
||||||
|
![image](https://user-images.githubusercontent.com/25207995/164393024-56543ddf-7063-4c04-9a9f-0c6238f30e90.png)
|
||||||
|
![image](https://user-images.githubusercontent.com/25207995/164393118-de844e75-f9a2-491a-aea6-f2d563abecc7.png)
|
||||||
|
![image](https://user-images.githubusercontent.com/25207995/164882735-77aa535c-0e93-4b32-af7c-f8b59888257a.png)
|
||||||
|
![image](https://user-images.githubusercontent.com/25207995/164882716-c9f16cd0-c0b6-4c0a-ae9e-4c95da9ef7f5.png)
|
23
build.sh
Normal file
23
build.sh
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
# !/bin/bash
|
||||||
|
|
||||||
|
# Clean dist
|
||||||
|
rm -rf ./dist
|
||||||
|
|
||||||
|
# build
|
||||||
|
neu build
|
||||||
|
|
||||||
|
# copy scripts and langs
|
||||||
|
cp -r ./scripts ./dist/GrassClipper
|
||||||
|
cp -r ./proxy ./dist/GrassClipper
|
||||||
|
cp -r ./languages ./dist/GrassClipper
|
||||||
|
|
||||||
|
# copy backgrounds
|
||||||
|
mkdir ./dist/GrassClipper/resources/
|
||||||
|
mkdir ./dist/GrassClipper/resources/bg
|
||||||
|
mkdir ./dist/GrassClipper/resources/bg/private
|
||||||
|
mkdir ./dist/GrassClipper/resources/bg/server
|
||||||
|
cp -r ./resources/bg/private/* ./dist/GrassClipper/resources/bg/private
|
||||||
|
cp -r ./resources/bg/server/* ./dist/GrassClipper/resources/bg/server
|
||||||
|
|
||||||
|
# rename exe
|
||||||
|
mv ./dist/GrassClipper/GrassClipper-win_x64.exe ./dist/GrassClipper/GrassClipper.exe
|
32
build_win.cmd
Normal file
32
build_win.cmd
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
@echo off
|
||||||
|
|
||||||
|
:: This binary is the SSL-secure release
|
||||||
|
set SSL_BINARY_URL="https://github.com/SpikeHD/neutralinojs/releases/download/v420.69.0/neutralino-win_x64.exe"
|
||||||
|
set NON_SSL_BINARY_URL="https://github.com/SpikeHD/neutralinojs/releases/download/v1337.0.0/neutralino-win_x64.exe"
|
||||||
|
|
||||||
|
:: Clean dist folder
|
||||||
|
del /s /q /f .\dist>nul
|
||||||
|
rd /s /q .\dist
|
||||||
|
|
||||||
|
:: Get the SSL-secure version of the binary
|
||||||
|
powershell Invoke-WebRequest -Uri %SSL_BINARY_URL% -OutFile "./bin/neutralino-win_x64.exe"
|
||||||
|
|
||||||
|
:: build
|
||||||
|
call neu build
|
||||||
|
|
||||||
|
:: Copy scripts and langs
|
||||||
|
xcopy .\languages\ .\dist\GrassClipper\languages\ /y /s
|
||||||
|
xcopy .\proxy\ .\dist\GrassClipper\proxy\ /y /s
|
||||||
|
xcopy .\scripts\ .\dist\GrassClipper\scripts\ /y /s
|
||||||
|
|
||||||
|
:: bgs
|
||||||
|
mkdir .\dist\GrassClipper\resources\bg\private
|
||||||
|
mkdir .\dist\GrassClipper\resources\bg\server
|
||||||
|
xcopy .\resources\bg\private\ .\dist\GrassClipper\resources\bg\private\ /y /s
|
||||||
|
xcopy .\resources\bg\server\ .\dist\GrassClipper\resources\bg\server\ /y /s
|
||||||
|
|
||||||
|
:: rename exe
|
||||||
|
move .\dist\GrassClipper\GrassClipper-win_x64.exe .\dist\GrassClipper\GrassClipper.exe
|
||||||
|
|
||||||
|
:: Re-use non-ssl secure version
|
||||||
|
powershell Invoke-WebRequest -Uri %NON_SSL_BINARY_URL% -OutFile "./bin/neutralino-win_x64.exe"
|
@ -3,14 +3,15 @@
|
|||||||
"appName": "GrassClipper",
|
"appName": "GrassClipper",
|
||||||
|
|
||||||
"playOfficial": "Play Official",
|
"playOfficial": "Play Official",
|
||||||
"playPrivate": "Play Private",
|
"playPrivate": "Play on Grasscutter",
|
||||||
"launchLocalServer": "Launch Local Server",
|
"launchLocalServer": "Launch Local Server",
|
||||||
|
|
||||||
"genshinFolderSet": "Set \"Genshin Impact Game\" folder",
|
"gameExeSet": "Set game executable",
|
||||||
"grasscutterFileSet": "Set \"GrassCutter\" .jar file",
|
"grasscutterFileSet": "Set \"Grasscutter\" .jar file",
|
||||||
"folderNotSet": "Not set",
|
"folderNotSet": "Not set",
|
||||||
|
|
||||||
"ipPlaceholder": "IP Address",
|
"ipPlaceholder": "Server Address...",
|
||||||
|
"portPlaceholder": "Port",
|
||||||
"noFavorites": "No favorites set",
|
"noFavorites": "No favorites set",
|
||||||
|
|
||||||
"settingsTitle": "Settings",
|
"settingsTitle": "Settings",
|
||||||
@ -20,20 +21,71 @@
|
|||||||
"proxyOption": "Proxy",
|
"proxyOption": "Proxy",
|
||||||
"proxySubtitle": "Install the proxy server via the install script",
|
"proxySubtitle": "Install the proxy server via the install script",
|
||||||
"updateOption": "Update",
|
"updateOption": "Update",
|
||||||
"updateSubtitle": "Auto updating is temporarily disabled. Check GitHub for the newest releease.",
|
"updateSubtitle": "Auto updating is temporarily disabled. Check GitHub for the newest release.",
|
||||||
"languageOption": "Language",
|
"languageOption": "Language",
|
||||||
"languageSubtitle": "Select your language!",
|
"languageSubtitle": "Select your language!",
|
||||||
"enableServerLauncherOption": "Enable Server Launcher",
|
"enableServerLauncherOption": "Enable Server Launcher",
|
||||||
"enableServerLauncherSubtitle": "Enable to server launcher tile for launching a local Grasscutter instance.",
|
"enableServerLauncherSubtitle": "Enable to server launcher tile for launching a local Grasscutter instance.",
|
||||||
|
"httpsOption": "Use HTTPS",
|
||||||
|
"httpsSubtitle": "Choose between using HTTPS or HTTP.",
|
||||||
|
|
||||||
"introSen1": "Looks like this is your first time opening GrassClipper!",
|
"introSen1": "Looks like this is your first time opening GrassClipper!",
|
||||||
"introSen2": "First of all, welcome, happy to see you here! :)",
|
"introSen2": "First of all, welcome, happy to see you here! :)",
|
||||||
"introSen3": "Would you like to run the proxy installer?",
|
"introSen3": "Would you like to run the proxy installer?",
|
||||||
"introSen4": "(required to connect to private servers)",
|
"introSen4": "(required to connect to servers)",
|
||||||
|
|
||||||
|
"updateBtn": "Update",
|
||||||
"proxyInstallBtn": "Install",
|
"proxyInstallBtn": "Install",
|
||||||
"proxyInstallDeny": "No thanks",
|
"proxyInstallDeny": "No thanks",
|
||||||
|
|
||||||
"genshinFolderDialog": "Select Genshin Impact Game folder",
|
"gameFolderDialog": "Select game folder",
|
||||||
"grasscutterFileDialog": "Select GrassCutter server jar file"
|
"grasscutterFileDialog": "Select Grasscutter server jar file",
|
||||||
}
|
|
||||||
|
"loggingInTo": "Logging in to: ",
|
||||||
|
"registeringFor": "Registering for: ",
|
||||||
|
"authUsername": "Username: ",
|
||||||
|
"authPassword": "Password: ",
|
||||||
|
"authConfirmPassword": "Confirm Password: ",
|
||||||
|
"authLoginBtn": "Login",
|
||||||
|
"authRegisterBtn": "Register",
|
||||||
|
"authLoginTitle": "Login",
|
||||||
|
"authRegisterTitle": "Register",
|
||||||
|
"launchWithoutAuth": "Launch without Authentication",
|
||||||
|
|
||||||
|
"alertInvalid": "Invalid username or password",
|
||||||
|
"alertNoPass": "No password set, please change password",
|
||||||
|
"alertUnknown": "Unknown error, contact server owner",
|
||||||
|
"alertAuthNoLogin": "Authentication is disabled, no need to log in!",
|
||||||
|
"alertLoginSuccess": "Login successful! Token copied to clipboard. Paste this token into the username field of the game to log in.",
|
||||||
|
|
||||||
|
"alertUserTaken": "Username is taken",
|
||||||
|
"alertPassMismatch": "Password and password confirmation do not match",
|
||||||
|
"alertAuthNoRegister": "Authentication is disabled, no need to register!",
|
||||||
|
"alertRegisterSuccess": "Registration successful!",
|
||||||
|
|
||||||
|
"downloadTitle": "Downloads",
|
||||||
|
"grassclipperTitle": "GrassClipper",
|
||||||
|
"grasscutterTitle": "Grasscutter",
|
||||||
|
"installerTitle": "Installer",
|
||||||
|
"installerSubtitle": "Installs proxy and other tools. Required for Grasscutter servers.",
|
||||||
|
"downloadStable": "Download Grasscutter Stable Build",
|
||||||
|
"stableSubtitle": "Install Grasscutter stable branch. This build usually has less bugs, but also less features.",
|
||||||
|
"downloadDev": "Download Grasscutter Development Build",
|
||||||
|
"devSubtitle": "Install Grasscutter development branch. This build sometimes has bugs, and is frequently updated. Use at your own risk.",
|
||||||
|
"downloadResources": "Download Grasscutter Resources",
|
||||||
|
"resourceSubtitle": "Downloads Grasscutter resources into the currently set Grasscutter folder. This should be done unless you plan on getting resources externally.",
|
||||||
|
|
||||||
|
"gcScriptRunning": "Running...",
|
||||||
|
"stableInstall": "Download",
|
||||||
|
"devInstall": "Download",
|
||||||
|
|
||||||
|
"updateNotifText": "A new update is available! Newest version: ",
|
||||||
|
|
||||||
|
"chineseCharacterAlert": "The file path set contains Chinese characters, this may cause problems!",
|
||||||
|
|
||||||
|
"dialogOk": "OK",
|
||||||
|
"dialogNo": "NO",
|
||||||
|
"serverEnableDialogTitle": "You found the Grasscutter server launcher!",
|
||||||
|
"serverEnableDialogText": "If you do not have an existing Grasscutter installation to set, would you like to download a build?"
|
||||||
|
|
||||||
|
}
|
||||||
|
39
languages/es.json
Normal file
39
languages/es.json
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
{
|
||||||
|
"fullLangName": "Español (México)",
|
||||||
|
"appName": "GrassClipper",
|
||||||
|
|
||||||
|
"playOfficial": "Jugar en oficial",
|
||||||
|
"playPrivate": "Jugar en Grasscutter",
|
||||||
|
"launchLocalServer": "Iniciar servidor local",
|
||||||
|
|
||||||
|
"gameExeSet": "Establece el folder game",
|
||||||
|
"grasscutterFileSet": "Establece el archivo .jar de \"Grasscutter\"",
|
||||||
|
"folderNotSet": "No establecido",
|
||||||
|
|
||||||
|
"ipPlaceholder": "Dirección IP",
|
||||||
|
"noFavorites": "No hay favoritos",
|
||||||
|
|
||||||
|
"settingsTitle": "Ajustes",
|
||||||
|
"scriptsSectionTitle": "Scripts",
|
||||||
|
"killswitchOption": "Kill Switch",
|
||||||
|
"killswitchSubtitle": "Solo para aquellos muy paranoicos con bans. Cierra el proceso del juego *y tu Internet* si algo le sucede al proxy.",
|
||||||
|
"proxyOption": "Proxy",
|
||||||
|
"proxySubtitle": "Instale el servidor proxy a través del script de instalación",
|
||||||
|
"updateOption": "Update",
|
||||||
|
"updateSubtitle": "Las actualizaciónes automáticas están deshabilitadas temporalmente. Consulte GitHub para ver la versión más reciente.",
|
||||||
|
"languageOption": "Idioma",
|
||||||
|
"languageSubtitle": "Seleccióne su idioma!",
|
||||||
|
"enableServerLauncherOption": "Habilita el launcher del servidor",
|
||||||
|
"enableServerLauncherSubtitle": "Habilite el tile del launche del servidor para iniciar una instancia local de Grasscutter.",
|
||||||
|
|
||||||
|
"introSen1": "¡Parece que es la primera vez que abres GrassClipper!",
|
||||||
|
"introSen2": "En primer lugar, ¡bienvenido, feliz de verte por aquí! :)",
|
||||||
|
"introSen3": "Quieres ejecutar el instalador de proxy?",
|
||||||
|
"introSen4": "(necesario para conectarse a los servidores)",
|
||||||
|
|
||||||
|
"proxyInstallBtn": "Instalar",
|
||||||
|
"proxyInstallDeny": "No gracias",
|
||||||
|
|
||||||
|
"gameFolderDialog": "Selecciona la carpeta game",
|
||||||
|
"grasscutterFileDialog": "Selecciona el archivo .jar de Grasscutter"
|
||||||
|
}
|
39
languages/fr.json
Normal file
39
languages/fr.json
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
{
|
||||||
|
"fullLangName": "Français",
|
||||||
|
"appName": "GrassClipper",
|
||||||
|
|
||||||
|
"playOfficial": "Jouer sur Officiel",
|
||||||
|
"playPrivate": "Jouer sur Grasscutter",
|
||||||
|
"launchLocalServer": "Lancer le serveur en local",
|
||||||
|
|
||||||
|
"gameExeSet": "Définir le dossier game",
|
||||||
|
"grasscutterFileSet": "Définir le fichier .jar \"Grasscutter\"",
|
||||||
|
"folderNotSet": "Pas encore défini",
|
||||||
|
|
||||||
|
"ipPlaceholder": "Adresse IP",
|
||||||
|
"noFavorites": "Aucun favori n'est défini",
|
||||||
|
|
||||||
|
"settingsTitle": "Paramètres",
|
||||||
|
"scriptsSectionTitle": "Scripts",
|
||||||
|
"killswitchOption": "Coupe-Circuit",
|
||||||
|
"killswitchSubtitle": "Seulement pour ceux qui sont très paranoïaques au sujet des bannissements. Tuez le processus de jeu *et coupez votre Internet* si le proxy ne fonctionne pas correctement.",
|
||||||
|
"proxyOption": "Proxy",
|
||||||
|
"proxySubtitle": "Install the proxy server via the install script",
|
||||||
|
"updateOption": "Mise à jour",
|
||||||
|
"updateSubtitle": "La mise à jour automatique est temporairement désactivée. Consultez GitHub pour la dernière version.",
|
||||||
|
"languageOption": "Langages",
|
||||||
|
"languageSubtitle": "Sélectionnez votre langue!",
|
||||||
|
"enableServerLauncherOption": "Activer le lanceur de serveur",
|
||||||
|
"enableServerLauncherSubtitle": "Autoriser le lanceur à executer une instance locale de Grasscutter",
|
||||||
|
|
||||||
|
"introSen1": "On dirait que c’est la première fois que vous ouvrez GrassClipper!",
|
||||||
|
"introSen2": "Bienvenue! Nous sommes heureux de vous voir parmis nous :)",
|
||||||
|
"introSen3": "Voulez-vous lancer l’installateur du proxy?",
|
||||||
|
"introSen4": "(nécessaire pour se connecter sur des serveurs Grasscutter)",
|
||||||
|
|
||||||
|
"proxyInstallBtn": "Installer",
|
||||||
|
"proxyInstallDeny": "Non merci",
|
||||||
|
|
||||||
|
"gameFolderDialog": "Sélectionnez le dossier contenant le game",
|
||||||
|
"grasscutterFileDialog": "Selectionnez le fichier jar du serveur de Grasscutter"
|
||||||
|
}
|
85
languages/hd.json
Normal file
85
languages/hd.json
Normal file
@ -0,0 +1,85 @@
|
|||||||
|
{
|
||||||
|
"fullLangName": "Hindi",
|
||||||
|
"appName": "ग्रासक्लिपर",
|
||||||
|
|
||||||
|
"playOfficial": "खेलो असली गेम",
|
||||||
|
"playPrivate": "खेलो ग्रासकटर पर",
|
||||||
|
"launchLocalServer": "शुरू करे लोकल सर्वर",
|
||||||
|
|
||||||
|
"gameExeSet": "गेम का निष्पादन करे",
|
||||||
|
"grasscutterFileSet": "लगाए \"ग्रासकटर \" .jar फ़ाइल",
|
||||||
|
"folderNotSet": "नहीं लगा है",
|
||||||
|
|
||||||
|
"ipPlaceholder": "आय पी एड्रेस",
|
||||||
|
"portPlaceholder": "पोर्ट",
|
||||||
|
"noFavorites": "कोई पसन्दीदार नहीं लगे",
|
||||||
|
|
||||||
|
"settingsTitle": "सेटिंग्स",
|
||||||
|
"scriptsSectionTitle": "स्क्रिप्ट्स",
|
||||||
|
"killswitchOption": "किल स्विच",
|
||||||
|
"killswitchSubtitle": "सिर्फ उनके लिए जो सोचते हैं की यह सब असली अकाउंट को बन कर देग। यह गेम और *इंटरनेट कनेक्शन* मर देता है अगर कुछ प्रॉक्सी को होता है। ",
|
||||||
|
"proxyOption": "प्रॉक्सी",
|
||||||
|
"proxySubtitle": "प्रॉक्सी सर्वर इनस्टॉल करे प्रॉक्सी स्क्रिप्ट्स से",
|
||||||
|
"updateOption": "अपडेट ",
|
||||||
|
"updateSubtitle": "अपने आप अपडेट होने का फीचर अभी बंद है। GitHub देखे नए रिलीज़ के लिए। ",
|
||||||
|
"languageOption": "भाषा ",
|
||||||
|
"languageSubtitle": "अपनी भाषा चुने!",
|
||||||
|
"enableServerLauncherOption": "सर्वर लांचर शुरू करे",
|
||||||
|
"enableServerLauncherSubtitle": "सक्षम करे सर्वर लांचर टाइल ताकि ये एक लोकल ग्रासकटर इंस्टैंस को शुरू कर सके। ",
|
||||||
|
"httpsOption": "HTTPS का उपयोग करे",
|
||||||
|
"httpsSubtitle": "चयन करे HTTPS या HTTP",
|
||||||
|
|
||||||
|
"introSen1": "लग रहा है यह तुम्हारा पहली बार खोलना हुआ है ग्रास्सक्लीप्पेर!",
|
||||||
|
"introSen2": "पहली बात, स्वागत है, अच्छा लगा आपको देख के यहाँ! :)",
|
||||||
|
"introSen3": "प्रॉक्सी इंस्टालर शुरू करे?",
|
||||||
|
"introSen4": "(ज़रुरत है इसकी सर्वर से कनेक्ट करने के लिए)",
|
||||||
|
|
||||||
|
"updateBtn": "अपडेट ",
|
||||||
|
"proxyInstallBtn": "इनस्टॉल ",
|
||||||
|
"proxyInstallDeny": "कोई ज़रुरत नहीं",
|
||||||
|
|
||||||
|
"gameFolderDialog": "गेम फोल्डर सेट करे",
|
||||||
|
"grasscutterFileDialog": "ग्रासकटर jar फाइल सेलेक्ट करे",
|
||||||
|
|
||||||
|
"loggingInTo": "लोग हो रहे हैं इसमें: ",
|
||||||
|
"registeringFor": "रजिस्टर करे इसके लिए: ",
|
||||||
|
"authUsername": "यूजरनाम: ",
|
||||||
|
"authPassword": "पासवर्ड: ",
|
||||||
|
"authConfirmPassword": "पासवर्ड पक्का करे: ",
|
||||||
|
"authLoginBtn": "लॉगिन",
|
||||||
|
"authRegisterBtn": "रजिस्टर",
|
||||||
|
"authLoginTitle": "लॉगिन",
|
||||||
|
"authRegisterTitle": "रजिस्टर",
|
||||||
|
"launchWithoutAuth": "शुरू करे बिना ऑथेंटिकेशन के",
|
||||||
|
|
||||||
|
"alertInvalid": "गलत यूजरनाम या पासवर्ड",
|
||||||
|
"alertNoPass": "कोई पासवर्ड नहीं लगा, कृपया पासवर्ड चेंज करे",
|
||||||
|
"alertUnknown": "अनजान एरर। सर्वर के मालिक से बात करे",
|
||||||
|
"alertAuthNoLogin": "ऑथेंटिकेशन बंद है, लॉगिन की कोई ज़रुरत नहीं!",
|
||||||
|
"alertLoginSuccess": "लॉगिन पूरा हुआ! टोकन क्लिपबोर्ड पर कॉपी हो गया है। यह टोकन पेस्ट करे यूजरनाम की जगह पर गेम के अंदर ताकि लॉगिन हो सके। ",
|
||||||
|
|
||||||
|
"alertUserTaken": "यूजरनाम ले लिया गया है",
|
||||||
|
"alertPassMismatch": "पासवर्ड और पासवर्ड कन्फर्मेशन मैच नहीं कर रहे",
|
||||||
|
"alertAuthNoRegister": "ऑथेंटिकेशन बंद है, रजिस्ट्रेशन की कोई ज़रुरत नहीं!",
|
||||||
|
"alertRegisterSuccess": "रजिस्ट्रेशन समाप्त हुआ!",
|
||||||
|
|
||||||
|
"downloadTitle": "डौन्लोडस",
|
||||||
|
"grassclipperTitle": "ग्रासक्लिपर",
|
||||||
|
"grasscutterTitle": "ग्रासकटर",
|
||||||
|
"installerTitle": "इंस्टालर",
|
||||||
|
"installerSubtitle": "ये प्रॉक्सी और कुछ और टूल्स इनस्टॉल करता है। ज़रुरत है इसकी ग्रासकटर सर्वर्स के लिए",
|
||||||
|
"downloadStable": "डाउनलोड ग्रासकटर स्टेबल बिल्ड",
|
||||||
|
"stableSubtitle": "इनस्टॉल ग्रासकटर स्टेबल ब्रांच। यह बिल्ड में काम फीचर्स है पर काम बग्स भी है।",
|
||||||
|
"downloadDev": "डाउनलोड ग्रासकटर डेवलपमेंट बिल्ड",
|
||||||
|
"devSubtitle": "इनस्टॉल ग्रासकटर डेवलपमेंट ब्रांच। इस बिल्ड में काफी बार बग्स होते हैं पर जल्दी जल्दी अपडेट होता है। अपने बूते पे इस्तेमाल करे।",
|
||||||
|
"downloadResources": "डाउनलोड ग्रसससटर रिसोर्सेज",
|
||||||
|
"resourceSubtitle": "आपभी सेट हुए ग्रासकटर फोल्डर में ग्रासकटर के रिसोर्सेज डाउनलोड करता है। यह हो जाना चाहिए अगर आपने कही और से रिसोर्सेज नहीं लिये।",
|
||||||
|
|
||||||
|
"gcScriptRunning": "चल रहा है...",
|
||||||
|
"stableInstall": "डाउनलोड",
|
||||||
|
"devInstall": "डाउनलोड",
|
||||||
|
|
||||||
|
"updateNotifText": "नया अपडेट आया है! नया वर्शन: ",
|
||||||
|
|
||||||
|
"chineseCharacterAlert": "इस फाइल पथ में चीनी चरक्टेर्स हैं, यह समस्या दे सकता है!"
|
||||||
|
}
|
90
languages/id.json
Normal file
90
languages/id.json
Normal file
@ -0,0 +1,90 @@
|
|||||||
|
{
|
||||||
|
"fullLangName": "Bahasa Indonesia",
|
||||||
|
"appName": "GrassClipper",
|
||||||
|
|
||||||
|
"playOfficial": "Mainkan Official",
|
||||||
|
"playPrivate": "Mainkan di Grasscutter",
|
||||||
|
"launchLocalServer": "Luncurkan Server Lokal",
|
||||||
|
|
||||||
|
"gameExeSet": "Atur eksekusi game",
|
||||||
|
"grasscutterFileSet": "Atur file \"Grasscutter\" .jar",
|
||||||
|
"folderNotSet": "Belum diatur",
|
||||||
|
|
||||||
|
"ipPlaceholder": "Alamat IP",
|
||||||
|
"portPlaceholder": "Port",
|
||||||
|
"noFavorites": "Tidak ada favorit yang diatur",
|
||||||
|
|
||||||
|
"settingsTitle": "Pengaturan",
|
||||||
|
"scriptsSectionTitle": "Skrip",
|
||||||
|
"killswitchOption": "Saklar Mati",
|
||||||
|
"killswitchSubtitle": "Hanya untuk yang paranoid terhadap ban. Mematikan proses game *dan internetmu* jika terjadi sesuatu pada proxy nya.",
|
||||||
|
"proxyOption": "Proxy",
|
||||||
|
"proxySubtitle": "Pasang server proxy melalui skrip pemasangan",
|
||||||
|
"updateOption": "Pembaruan",
|
||||||
|
"updateSubtitle": "Pembaruan otomatis dinonaktifkan untuk sementara. Cek GitHub untuk rilisan terbaru.",
|
||||||
|
"languageOption": "Bahasa",
|
||||||
|
"languageSubtitle": "Pilih bahasamu!",
|
||||||
|
"enableServerLauncherOption": "Aktifkan Peluncur Server",
|
||||||
|
"enableServerLauncherSubtitle": "Aktifkan ke tile peluncur server untuk meluncurkan instance Grasscutter secara lokal.",
|
||||||
|
"httpsOption": "Gunakan HTTPS",
|
||||||
|
"httpsSubtitle": "Pilih antara menggunakan HTTPS atau HTTP.",
|
||||||
|
|
||||||
|
"introSen1": "Sepertinya ini pertama kalinya kamu membuka GrassClipper!",
|
||||||
|
"introSen2": "Pertama-tama, selamat datang, senang bertemu denganmu disini! :)",
|
||||||
|
"introSen3": "Apakah kamu ingin menjalankan pemasangan proxy?",
|
||||||
|
"introSen4": "(diperlukan untuk menghubungkan ke server)",
|
||||||
|
|
||||||
|
"updateBtn": "Perbarui",
|
||||||
|
"proxyInstallBtn": "Pasang",
|
||||||
|
"proxyInstallDeny": "Tidak, terima kasih",
|
||||||
|
|
||||||
|
"gameFolderDialog": "Pilih folder game",
|
||||||
|
"grasscutterFileDialog": "Pilih file jar server Grasscutter",
|
||||||
|
|
||||||
|
"loggingInTo": "Masuk ke: ",
|
||||||
|
"registeringFor": "Mendaftar untuk: ",
|
||||||
|
"authUsername": "Nama Pengguna: ",
|
||||||
|
"authPassword": "Kata Sandi: ",
|
||||||
|
"authConfirmPassword": "Konfirmasi Kata Sandi: ",
|
||||||
|
"authLoginBtn": "Masuk",
|
||||||
|
"authRegisterBtn": "Daftar",
|
||||||
|
"authLoginTitle": "Masuk",
|
||||||
|
"authRegisterTitle": "Daftar",
|
||||||
|
"launchWithoutAuth": "Luncurkan Tanpa Autentikasi",
|
||||||
|
|
||||||
|
"alertInvalid": "Nama pengguna atau kata sandi tidak valid",
|
||||||
|
"alertNoPass": "Tidak ada kata sandi yang diatur, silahkan isi kata sandi",
|
||||||
|
"alertUnknown": "Kesalahan yang tidak diketahui, hubungi pemilik server",
|
||||||
|
"alertAuthNoLogin": "Autentikasi dinonaktifkan, tidak perlu masuk!",
|
||||||
|
"alertLoginSuccess": "Berhasil masuk! Token disalin ke papan klip. Tempelkan token ini ke dalam bidang nama pengguna di game untuk masuk.",
|
||||||
|
|
||||||
|
"alertUserTaken": "Nama pengguna sudah terpakai",
|
||||||
|
"alertPassMismatch": "Kata sandi dan konfirmasi kata sandi tidak cocok",
|
||||||
|
"alertAuthNoRegister": "Autentikasi dinonaktifkan, tidak perlu mendaftar!",
|
||||||
|
"alertRegisterSuccess": "Pendaftaran berhasil!",
|
||||||
|
|
||||||
|
"downloadTitle": "Unduhan",
|
||||||
|
"grassclipperTitle": "GrassClipper",
|
||||||
|
"grasscutterTitle": "Grasscutter",
|
||||||
|
"installerTitle": "Pemasang",
|
||||||
|
"installerSubtitle": "Pasang proxy dan alat lainnya. Diperlukan untuk server Grasscutter.",
|
||||||
|
"downloadStable": "Unduh Versi Stabil Grasscutter",
|
||||||
|
"stableSubtitle": "Pasang versi stabil Grasscutter. Versi ini biasanya tidak punya banyak bug, tetapi juga fiturnya lebih sedikit.",
|
||||||
|
"downloadDev": "Unduh Versi Pengembangan Grasscutter",
|
||||||
|
"devSubtitle": "Pasang versi pengembangan Grasscutter. Versi ini biasanya mempunyai banyak bug dan sering diperbarui. Gunakan dengan resikomu sendiri.",
|
||||||
|
"downloadResources": "Unduh Sumber Daya Grasscutter",
|
||||||
|
"resourceSubtitle": "Unduh sumber daya Grasscutter ke dalam folder Grasscutter yang sudah diatur. Ini harus dilakukan kecuali kamu berencana untuk mendapatkan sumber daya secara eksternal.",
|
||||||
|
|
||||||
|
"gcScriptRunning": "Menjalankan...",
|
||||||
|
"stableInstall": "Unduh",
|
||||||
|
"devInstall": "Unduh",
|
||||||
|
|
||||||
|
"updateNotifText": "Pembaruan baru tersedia! Versi terbaru: ",
|
||||||
|
|
||||||
|
"chineseCharacterAlert": "Jalur file yang diatur berisi karakter Cina, ini dapat menyebabkan masalah!",
|
||||||
|
|
||||||
|
"dialogOk": "OK",
|
||||||
|
"dialogNo": "TIDAK",
|
||||||
|
"serverEnableDialogTitle": "Kamu menemukan peluncur server Grasscutter!",
|
||||||
|
"serverEnableDialogText": "Jika kamu tidak memiliki pemasangan Grasscutter untuk diatur, apakah kamu mau mengunduh build nya?"
|
||||||
|
}
|
85
languages/jp.json
Normal file
85
languages/jp.json
Normal file
@ -0,0 +1,85 @@
|
|||||||
|
{
|
||||||
|
"fullLangName": "日本語",
|
||||||
|
"appName": "GrassClipper",
|
||||||
|
|
||||||
|
"playOfficial": "起動 (Official)",
|
||||||
|
"playPrivate": "起動 (Grasscutter)",
|
||||||
|
"launchLocalServer": "ローカルサーバーを起動",
|
||||||
|
|
||||||
|
"gameExeSet": "GenshinImpact.exe の場所を指定",
|
||||||
|
"grasscutterFileSet": "grasscutter.jar の場所を指定",
|
||||||
|
"folderNotSet": "原神のフォルダーが指定されていません。",
|
||||||
|
|
||||||
|
"ipPlaceholder": "IP アドレス",
|
||||||
|
"portPlaceholder": "ポート",
|
||||||
|
"noFavorites": "お気に入りはまだ登録されていません",
|
||||||
|
|
||||||
|
"settingsTitle": "設定",
|
||||||
|
"scriptsSectionTitle": "スクリプト",
|
||||||
|
"killswitchOption": "強制終了",
|
||||||
|
"killswitchSubtitle": "緊急時のみ。プロキシにエラーが発生した場合に、ゲームを終了し、インターネットを遮断します。",
|
||||||
|
"proxyOption": "プロキシ",
|
||||||
|
"proxySubtitle": "プロキシサーバーをインストールします。",
|
||||||
|
"updateOption": "更新",
|
||||||
|
"updateSubtitle": "自動更新は現在無効化されています。GitHub で最新バージョンを確認してください。",
|
||||||
|
"languageOption": "言語",
|
||||||
|
"languageSubtitle": "言語を選択してください。",
|
||||||
|
"enableServerLauncherOption": "サーバーランチャーを有効化します",
|
||||||
|
"enableServerLauncherSubtitle": "Grasscutter を起動するための、ランチャータイルを有効化します。",
|
||||||
|
"httpsOption": "HTTPS を使用",
|
||||||
|
"httpsSubtitle": "HTTP と HTTPS のどちらを使用するか選択します。このオプションは、プライベートサーバーでプレイするときにのみ適用されます。",
|
||||||
|
|
||||||
|
"introSen1": "GrassClipper へようこそ!",
|
||||||
|
"introSen2": "GrassClipper をご利用いただきありがとうございます。",
|
||||||
|
"introSen3": "プロキシサーバーをインストールしますか?",
|
||||||
|
"introSen4": "(インターネットへの接続が必要)",
|
||||||
|
|
||||||
|
"updateBtn": "更新",
|
||||||
|
"proxyInstallBtn": "インストールする",
|
||||||
|
"proxyInstallDeny": "インストールしない",
|
||||||
|
|
||||||
|
"gameFolderDialog": "原神のゲームフォルダーを指定",
|
||||||
|
"grasscutterFileDialog": "grasscutter.jar の場所を指定",
|
||||||
|
|
||||||
|
"loggingInTo": "ログイン先: ",
|
||||||
|
"registeringFor": "登録先: ",
|
||||||
|
"authUsername": "ユーザー名: ",
|
||||||
|
"authPassword": "パスワード: ",
|
||||||
|
"authConfirmPassword": "パスワード(確認): ",
|
||||||
|
"authLoginBtn": "ログイン",
|
||||||
|
"authRegisterBtn": "登録",
|
||||||
|
"authLoginTitle": "ログイン",
|
||||||
|
"authRegisterTitle": "登録",
|
||||||
|
"launchWithoutAuth": "認証せず起動",
|
||||||
|
|
||||||
|
"alertInvalid": "ユーザー名またはパスワードが誤っています。",
|
||||||
|
"alertNoPass": "パスワードが設定されていません。パスワードを設定してください。",
|
||||||
|
"alertUnknown": "不明なエラーが発生しました。サーバーの管理者に連絡してください。",
|
||||||
|
"alertAuthNoLogin": "認証が無効になっています。ログインする必要はありません。",
|
||||||
|
"alertLoginSuccess": "ログインに成功しました。トークンをクリップボードにコピーしたので、ゲームのユーザー名欄に貼り付けてログインしてください。",
|
||||||
|
|
||||||
|
"alertUserTaken": "そのユーザー名はすでに使用されています。",
|
||||||
|
"alertPassMismatch": "パスワードが一致していません。",
|
||||||
|
"alertAuthNoRegister": "認証が無効になっています。ログインする必要はありません。",
|
||||||
|
"alertRegisterSuccess": "登録成功",
|
||||||
|
|
||||||
|
"downloadTitle": "ダウンロード",
|
||||||
|
"grassclipperTitle": "GrassClipper",
|
||||||
|
"grasscutterTitle": "Grasscutter",
|
||||||
|
"installerTitle": "インストーラー",
|
||||||
|
"installerSubtitle": "Grasscutter に必要な、プロキシなどのツールをインストールします。",
|
||||||
|
"downloadStable": "Grasscutter の安定版をインストール",
|
||||||
|
"stableSubtitle": "Grasscutter の安定版をインストールします。バグは少ないですが、新機能も少ないです。",
|
||||||
|
"downloadDev": "Grasscutter の開発版をインストール",
|
||||||
|
"devSubtitle": "Grasscutter の開発版をインストールします。バグが多い恐れがあります。また、頻繁に更新されます。",
|
||||||
|
"downloadResources": "Grasscutter のリソースをダウンロード",
|
||||||
|
"resourceSubtitle": "Grasscutter のリソースをダウンロードします。ほかのところから入手しない場合は、これをダウンロードする必要があります。",
|
||||||
|
|
||||||
|
"gcScriptRunning": "実行中...",
|
||||||
|
"stableInstall": "ダウンロード",
|
||||||
|
"devInstall": "ダウンロード",
|
||||||
|
|
||||||
|
"updateNotifText": "新しいバージョンが利用できます。Version: ",
|
||||||
|
|
||||||
|
"chineseCharacterAlert": "ファイルパスに漢字やひらがななどの全角文字が含まれているため、正常に動作しない場合があります。"
|
||||||
|
}
|
39
languages/nl.json
Normal file
39
languages/nl.json
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
{
|
||||||
|
"fullLangName": "Nederlands",
|
||||||
|
"appName": "GrassClipper",
|
||||||
|
|
||||||
|
"playOfficial": "Normaal spelen",
|
||||||
|
"playPrivate": "Op Grasscutter spelen",
|
||||||
|
"launchLocalServer": "Lokale server starten",
|
||||||
|
|
||||||
|
"gameExeSet": "Selecteer de game folder",
|
||||||
|
"grasscutterFileSet": "Selecteer het \"Grasscutter\" .jar bestand",
|
||||||
|
"folderNotSet": "Niet geselecteerd",
|
||||||
|
|
||||||
|
"ipPlaceholder": "IP adres",
|
||||||
|
"noFavorites": "Geen favorieten",
|
||||||
|
|
||||||
|
"settingsTitle": "Instellingen",
|
||||||
|
"scriptsSectionTitle": "Scripts",
|
||||||
|
"killswitchOption": "Kill Switch",
|
||||||
|
"killswitchSubtitle": "Alleen voor de paranoïde gebruikers. Als iets met de proxy gebeurt, sluit dit jouw spel *en je internet* af.",
|
||||||
|
"proxyOption": "Proxy",
|
||||||
|
"proxySubtitle": "Installeer de proxy server via de installeer script",
|
||||||
|
"updateOption": "Update",
|
||||||
|
"updateSubtitle": "Automatisch updaten is tijdelijk uitgeschakeld. Ga naar de GitHub voor de nieuwste release.",
|
||||||
|
"languageOption": "Taal",
|
||||||
|
"languageSubtitle": "Selecteer je taal!",
|
||||||
|
"enableServerLauncherOption": "Server Launcher inschakelen",
|
||||||
|
"enableServerLauncherSubtitle": "Schakel dit in om de server launcher tile te gebruiken om een lokale Grasscutter server te starten.",
|
||||||
|
|
||||||
|
"introSen1": "Het lijkt erop dat het de eerste keer is dat je Grasscutter gebruikt!",
|
||||||
|
"introSen2": "Ten eerste, welkom, goed om jou hier te zien! :)",
|
||||||
|
"introSen3": "Wil je de proxy installer uitvoeren?",
|
||||||
|
"introSen4": "(vereist om te verbinden naar servers)",
|
||||||
|
|
||||||
|
"proxyInstallBtn": "Installeren",
|
||||||
|
"proxyInstallDeny": "Nee dank je",
|
||||||
|
|
||||||
|
"gameFolderDialog": "Selecteer de game folder",
|
||||||
|
"grasscutterFileDialog": "Selecteer het Grasscutter server jar bestand"
|
||||||
|
}
|
91
languages/pl.json
Normal file
91
languages/pl.json
Normal file
@ -0,0 +1,91 @@
|
|||||||
|
{
|
||||||
|
"fullLangName": "Polski",
|
||||||
|
"appName": "GrassClipper",
|
||||||
|
|
||||||
|
"playOfficial": "Serwer oficjalny",
|
||||||
|
"playPrivate": "Serwer prywatny",
|
||||||
|
"launchLocalServer": "Uruchom serwer prywatny",
|
||||||
|
|
||||||
|
"gameExeSet": "Ustaw plik gry",
|
||||||
|
"grasscutterFileSet": "Ustaw plik \"Grasscutter\" .jar ",
|
||||||
|
"folderNotSet": "Nie ustawiono",
|
||||||
|
|
||||||
|
"ipPlaceholder": "Adres serwera",
|
||||||
|
"portPlaceholder": "Port",
|
||||||
|
"noFavorites": "Nie dodano żadnych ulubionych",
|
||||||
|
|
||||||
|
"settingsTitle": "Ustawienia",
|
||||||
|
"scriptsSectionTitle": "Skrypty",
|
||||||
|
"killswitchOption": "Kill Switch",
|
||||||
|
"killswitchSubtitle": "Tylko dla tych, którzy boją się o dostanie bana. Wyłącza gre *oraz twój internet* jeżeli coś stanie się z proxy.",
|
||||||
|
"proxyOption": "Proxy",
|
||||||
|
"proxySubtitle": "Zainstaluj serwer proxy poprzez skrypt instalacyjny",
|
||||||
|
"updateOption": "Aktualizacja",
|
||||||
|
"updateSubtitle": "Auto-aktualizacje są niedostępne. Sprawdź GitHub'a projektu po nowe wersje.",
|
||||||
|
"languageOption": "Język",
|
||||||
|
"languageSubtitle": "Wybierz swój język!",
|
||||||
|
"enableServerLauncherOption": "Włącz Launcher Serwera",
|
||||||
|
"enableServerLauncherSubtitle": "Enable to server launcher tile for launching a local Grasscutter instance.",
|
||||||
|
"httpsOption": "Używaj HTTPS",
|
||||||
|
"httpsSubtitle": "Wybierz pomiędzy używaniem HTTPS, a HTTP.",
|
||||||
|
|
||||||
|
"introSen1": "Wygląda na to, że po raz pierwszy uruchomiłeś GrassClipper'a!",
|
||||||
|
"introSen2": "Po pierwsze, miło Cię tu widzieć! :)",
|
||||||
|
"introSen3": "Czy chcesz uruchomić instalator proxy?",
|
||||||
|
"introSen4": "(proxy wymagane jest do łączenia się z serwerem prywatnym)",
|
||||||
|
|
||||||
|
"updateBtn": "Aktualizuj",
|
||||||
|
"proxyInstallBtn": "Instaluj",
|
||||||
|
"proxyInstallDeny": "Nie, dziękuję",
|
||||||
|
|
||||||
|
"gameFolderDialog": "Ustaw folder gry",
|
||||||
|
"grasscutterFileDialog": "Wybierz plik serwera Grasscutter",
|
||||||
|
|
||||||
|
"loggingInTo": "Logowanie do: ",
|
||||||
|
"registeringFor": "Rejestracja do: ",
|
||||||
|
"authUsername": "Nazwa użytkownika: ",
|
||||||
|
"authPassword": "Hasło: ",
|
||||||
|
"authConfirmPassword": "Potwierdź hasło: ",
|
||||||
|
"authLoginBtn": "Zaloguj",
|
||||||
|
"authRegisterBtn": "Zarejestruj",
|
||||||
|
"authLoginTitle": "Logowanie",
|
||||||
|
"authRegisterTitle": "Rejestracja",
|
||||||
|
"launchWithoutAuth": "Uruchom bez autentykacji",
|
||||||
|
|
||||||
|
"alertInvalid": "Błędna nazwa użytkownika lub hasło",
|
||||||
|
"alertNoPass": "Nie ustawiono hasła, proszę ustawić hasło",
|
||||||
|
"alertUnknown": "Nieznany błąd, skontaktuj się z właścicielem serwera",
|
||||||
|
"alertAuthNoLogin": "Autentykacja jest wyłączona, nie ma potrzeby logowania się!",
|
||||||
|
"alertLoginSuccess": "Pomyślnie zalogowano! Twój Token został skopiowany do schowka. Wklej go w polu nazwy użytkownika w grze aby się zalogować.",
|
||||||
|
|
||||||
|
"alertUserTaken": "Ta nazwa jest już zajęta",
|
||||||
|
"alertPassMismatch": "Hasła nie zgadzają się",
|
||||||
|
"alertAuthNoRegister": "Autentykacja jest wyłączona, nie ma potrzeby rejestrowania się!",
|
||||||
|
"alertRegisterSuccess": "Pomyślnie zarejestrowano!",
|
||||||
|
|
||||||
|
"downloadTitle": "Pobieranie",
|
||||||
|
"grassclipperTitle": "GrassClipper",
|
||||||
|
"grasscutterTitle": "Grasscutter",
|
||||||
|
"installerTitle": "Instalator",
|
||||||
|
"installerSubtitle": "Instaluje proxy oraz inne niezbędne narzędzia do grania na serwerach Grasscutter'a.",
|
||||||
|
"downloadStable": "Pobierz stabilną wersję Grasscutter'a",
|
||||||
|
"stableSubtitle": "Zainstaluj stabilną wersję Grasscutter'a. Ma ona mało błędów, ale też mniej funkcji.",
|
||||||
|
"downloadDev": "Pobierz wersję developerską Grasscutter'a",
|
||||||
|
"devSubtitle": "Zainstaluj wersję developerską Grasscutter'a. Może posiadać sporo błędów, ma najnowsze funkcje, używaj na własne ryzyko.",
|
||||||
|
"downloadResources": "Pobierz Zasoby Grasscutter'a",
|
||||||
|
"resourceSubtitle": "Pobiera zasoby Grasscuterra do aktualnie wybranego folderu. Nie powinno się tego uruchamiać gdy zasoby chcesz pobierać z zewnątrz.",
|
||||||
|
|
||||||
|
"gcScriptRunning": "Pracuje...",
|
||||||
|
"stableInstall": "Pobierz wersje stabilną",
|
||||||
|
"devInstall": "Pobierz wersje deweloperską",
|
||||||
|
|
||||||
|
"updateNotifText": "Nowa wersja jest dostępna! Najnowsza wersja: ",
|
||||||
|
|
||||||
|
"chineseCharacterAlert": "Ścieżka pliku zawierająca chińskie symbole może powodować błędy.",
|
||||||
|
|
||||||
|
"dialogOk": "Tak",
|
||||||
|
"dialogNo": "Nie",
|
||||||
|
"serverEnableDialogTitle": "Znalazłeś Launcher Serwera",
|
||||||
|
"serverEnableDialogText": "Jeżeli nie posiadasz instalacji Grasscutter'a, możesz ją teraz pobrać. Uruchomić pobieranie?"
|
||||||
|
|
||||||
|
}
|
40
languages/ru.json
Normal file
40
languages/ru.json
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
{
|
||||||
|
"fullLangName": "Русский",
|
||||||
|
"appName": "GrassClipper",
|
||||||
|
|
||||||
|
"playOfficial": "Официальная игра",
|
||||||
|
"playPrivate": "Grasscutter",
|
||||||
|
"launchLocalServer": "Локальный сервер",
|
||||||
|
|
||||||
|
"gameExeSet": "Папка с игрой",
|
||||||
|
"grasscutterFileSet": "Путь до файла \"Grasscutter\" (.jar)",
|
||||||
|
"folderNotSet": "Папка не выбрана",
|
||||||
|
|
||||||
|
"ipPlaceholder": "IP-адрес",
|
||||||
|
"portPlaceholder": "Порт (опционально)",
|
||||||
|
"noFavorites": "В избранном пусто",
|
||||||
|
|
||||||
|
"settingsTitle": "Настройки",
|
||||||
|
"scriptsSectionTitle": "Скрипты",
|
||||||
|
"killswitchOption": "Аварийное прерывание",
|
||||||
|
"killswitchSubtitle": "Опция для тех, кто очень беспокоится по поводу банов. Снимает задачу с игрой *и разрывает ваше интернет-соединение*, когда возникают неполадки с прокси",
|
||||||
|
"proxyOption": "Прокси",
|
||||||
|
"proxySubtitle": "Запустить установочный скрипт для настройки прокси-сервера",
|
||||||
|
"updateOption": "Обновление",
|
||||||
|
"updateSubtitle": "Автоматическое обновление временно отключено. Вы можете проверить наличие обновлений на GitHub",
|
||||||
|
"languageOption": "Язык",
|
||||||
|
"languageSubtitle": "Выберите предпочитаемый язык приложения",
|
||||||
|
"enableServerLauncherOption": "Включить запуск сервера",
|
||||||
|
"enableServerLauncherSubtitle": "Использовать лаунчер для локального запуска Grasscutter",
|
||||||
|
|
||||||
|
"introSen1": "Кажется, вы впервые запустили GrassClipper!",
|
||||||
|
"introSen2": "Добро пожаловать, мы рады вас видеть! :)",
|
||||||
|
"introSen3": "Запустить установщик прокси?",
|
||||||
|
"introSen4": "(необходим для подключения к приватным серверам)",
|
||||||
|
|
||||||
|
"proxyInstallBtn": "Установить",
|
||||||
|
"proxyInstallDeny": "Нет, спасибо",
|
||||||
|
|
||||||
|
"gameFolderDialog": "Выбор папки с игрой",
|
||||||
|
"grasscutterFileDialog": "Выбор пути до файла Grasscutter (.jar)"
|
||||||
|
}
|
43
languages/tr.json
Normal file
43
languages/tr.json
Normal file
@ -0,0 +1,43 @@
|
|||||||
|
{
|
||||||
|
"fullLangName": "Türkçe",
|
||||||
|
"appName": "GrassClipper",
|
||||||
|
|
||||||
|
"playOfficial": "Resmi sunucuda oyna",
|
||||||
|
"playPrivate": "Grasscutter sunucusunda oyna",
|
||||||
|
"launchLocalServer": "Yerel sunucuyu başlat",
|
||||||
|
|
||||||
|
"gameExeSet": "Oyun çalıştırıcısını seç",
|
||||||
|
"grasscutterFileSet": "\"Grasscutter\" .jar dosyasını seç",
|
||||||
|
"folderNotSet": "Seçilmedi",
|
||||||
|
|
||||||
|
"ipPlaceholder": "IP Adresi",
|
||||||
|
"portPlaceholder": "Port",
|
||||||
|
"noFavorites": "Favori sunucunuz yok.",
|
||||||
|
|
||||||
|
"settingsTitle": "Ayarlar",
|
||||||
|
"scriptsSectionTitle": "Scriptler",
|
||||||
|
"killswitchOption": "Kill Switch",
|
||||||
|
"killswitchSubtitle": "Sadece banlanmaktan paranoyak olanlar için. Proxy'ye bir şey olduğunda oyunu kapatıp *internet bağlantınızı* keser.",
|
||||||
|
"proxyOption": "Proxy",
|
||||||
|
"proxySubtitle": "Proxy sunucusunu bir script ile kur.",
|
||||||
|
"updateOption": "Güncelle",
|
||||||
|
"updateSubtitle": "Otomatik güncellemeler geçici olarak devre dışıdır. Yeni sürümler için GitHub'ı kontrol edin.",
|
||||||
|
"languageOption": "Dil",
|
||||||
|
"languageSubtitle": "Dilinizi seçin!",
|
||||||
|
"enableServerLauncherOption": "Sunucu başlatıcısını etkinleştir",
|
||||||
|
"enableServerLauncherSubtitle": "Yerel Grasscutter sunucusunu başlatmak için kutucuğunu etkinleştirin.",
|
||||||
|
"httpsOption": "HTTPS kullan",
|
||||||
|
"httpsSubtitle": "HTTPS veya HTTP arasında geçiş yap.",
|
||||||
|
|
||||||
|
"introSen1": "Görünüşe göre GrassClipper'ı ilk kez açıyorsunuz!",
|
||||||
|
"introSen2": "İlk olarak, hoşgeldiniz. Sizi burada görmek bizi mutlu ediyor! :)",
|
||||||
|
"introSen3": "Proxy kurucusunu çalıştırmak ister misiniz?",
|
||||||
|
"introSen4": "(sunuculara bağlanmak için gerekli)",
|
||||||
|
|
||||||
|
"updateBtn": "Güncelle",
|
||||||
|
"proxyInstallBtn": "Kur",
|
||||||
|
"proxyInstallDeny": "Hayır, teşekkürler.",
|
||||||
|
|
||||||
|
"gameFolderDialog": "Oyun klasörünü seç",
|
||||||
|
"grasscutterFileDialog": "Grasscutter.jar dosyasını seç"
|
||||||
|
}
|
90
languages/vie.json
Normal file
90
languages/vie.json
Normal file
@ -0,0 +1,90 @@
|
|||||||
|
{
|
||||||
|
"fullLangName": "Tiếng Việt",
|
||||||
|
"appName": "GrassClipper",
|
||||||
|
|
||||||
|
"playOfficial": "Máy chủ chính thức",
|
||||||
|
"playPrivate": "Máy chủ riêng",
|
||||||
|
"launchLocalServer": "Khởi động máy chủ cục bộ",
|
||||||
|
|
||||||
|
"gameExeSet": "Chọn tệp \"GenshinImpact.exe\"",
|
||||||
|
"grasscutterFileSet": "Chọn tệp \"Grasscutter.jar\"",
|
||||||
|
"folderNotSet": "Chưa chọn tệp",
|
||||||
|
|
||||||
|
"ipPlaceholder": "Địa chỉ máy chủ",
|
||||||
|
"portPlaceholder": "Cổng",
|
||||||
|
"noFavorites": "Không có máy chủ yêu thích",
|
||||||
|
|
||||||
|
"settingsTitle": "Cài đặt",
|
||||||
|
"scriptsSectionTitle": "Scripts",
|
||||||
|
"killswitchOption": "Thoát khẩn cấp",
|
||||||
|
"killswitchSubtitle": "Để phòng tránh bị ban, thoát game *và ngắt internet* của bạn ngay lập tức nếu có vấn đề xảy ra.",
|
||||||
|
"proxyOption": "Proxy",
|
||||||
|
"proxySubtitle": "Dùng để kết nối vào máy chủ riêng",
|
||||||
|
"updateOption": "Cập nhật",
|
||||||
|
"updateSubtitle": "Tính năng tự động cập nhập tạm thời chưa khả dụng. Kiểm tra Github để biết thêm chi tiết",
|
||||||
|
"languageOption": "Ngôn ngữ",
|
||||||
|
"languageSubtitle": "Thay đổi ngôn ngữ",
|
||||||
|
"enableServerLauncherOption": "Grasscutter",
|
||||||
|
"enableServerLauncherSubtitle": "Thêm lựa chọn bật máy chủ grasscutter",
|
||||||
|
"httpsOption": "Dùng HTTPS",
|
||||||
|
"httpsSubtitle": "Thay đổi giao thức giữa HTTPS và HTTP",
|
||||||
|
|
||||||
|
"introSen1": "Hình như đây là lần đầu tiên bạn dùng ứng dụng này!",
|
||||||
|
"introSen2": "Trước tiên xin chào và cảm ơn bạn đã sử dụng! :)",
|
||||||
|
"introSen3": "Bạn có muốn cài đặt ngay proxy không?",
|
||||||
|
"introSen4": "(cần thiết để vào máy chủ riêng)",
|
||||||
|
|
||||||
|
"updateBtn": "Cập nhật",
|
||||||
|
"proxyInstallBtn": "Cài đặt",
|
||||||
|
"proxyInstallDeny": "Huỷ bỏ",
|
||||||
|
|
||||||
|
"gameFolderDialog": "Chọn tệp GenshinImpact.exe",
|
||||||
|
"grasscutterFileDialog": "Chọn tệp Grasscutter.jar",
|
||||||
|
"loggingInTo": "Đang đăng nhập với tên: ",
|
||||||
|
"registeringFor": "Đang đăng ký tài khoản: ",
|
||||||
|
"authUsername": "Tài khoản: ",
|
||||||
|
"authPassword": "Mật khẩu: ",
|
||||||
|
"authConfirmPassword": "Xác nhận lại mật khẩu: ",
|
||||||
|
"authLoginBtn": "Đăng nhập",
|
||||||
|
"authRegisterBtn": "Đăng ký",
|
||||||
|
"authLoginTitle": "Đăng nhập",
|
||||||
|
"authRegisterTitle": "Đăng ký",
|
||||||
|
"launchWithoutAuth": "Khởi chạy không cần hệ thống xác thực",
|
||||||
|
|
||||||
|
"alertInvalid": "Tài khoản hoặc mật khẩu không đúng",
|
||||||
|
"alertNoPass": "Chưa có mật khẩu. Vui lòng thiết lập mật khẩu",
|
||||||
|
"alertUnknown": "Lỗi không xác định, báo cáo với người quản lý máy chủ",
|
||||||
|
"alertAuthNoLogin": "Đã vô hiệu hoá hệ thống xác thực, không cần đăng nhập.",
|
||||||
|
"alertLoginSuccess": "Đăng nhập thành công! Token đã được sao chép vào bộ nhớ tạm. Dán token vào phần tài khoản trong game để đăng nhập.",
|
||||||
|
|
||||||
|
"alertUserTaken": "Tên tài khoản đã tồn tại",
|
||||||
|
"alertPassMismatch": "Mật khẩu xác thực không trùng khớp",
|
||||||
|
"alertAuthNoRegister": "Đã vô hiệu hoá hệ thống xác thực, không cần đăng ký.",
|
||||||
|
"alertRegisterSuccess": "Đăng ký thành công!",
|
||||||
|
|
||||||
|
"downloadTitle": "Tải về",
|
||||||
|
"grassclipperTitle": "GrassClipper",
|
||||||
|
"grasscutterTitle": "Grasscutter",
|
||||||
|
"installerTitle": "Phần cài đặt",
|
||||||
|
"installerSubtitle": "Cài đặt proxy và những phần khác. Cần thiết cho những máy chủ Grasscutter.",
|
||||||
|
"downloadStable": "Tải về Grasscutter phiên bản Stable",
|
||||||
|
"stableSubtitle": "Cài đặt Grasscutter phiên bản Stable. Đây là phiên bản ổn định, tuy nhiên sẽ có ít tính năng.",
|
||||||
|
"downloadDev": "Tải về Grasscutter phiên bản Development",
|
||||||
|
"devSubtitle": "Cài đặt Grasscutter phiên bản Development. Đây là phiên bản mới hơn, thường xuyên cập nhập hơn nhưng có thể sẽ xuất hiện lõi trong quá trình sử dụng.",
|
||||||
|
"downloadResources": "Tài về tài nguyên cho Grasscutter",
|
||||||
|
"resourceSubtitle": "Tải về phần tài nguyên cần thiết cho Grasscutter. Sử dụng tính năng này để tài về phần tài nguyên tự động nếu bạn không muốn tải về thủ công.",
|
||||||
|
|
||||||
|
"gcScriptRunning": "Đang chạy...",
|
||||||
|
"stableInstall": "Tải về",
|
||||||
|
"devInstall": "Tải về",
|
||||||
|
|
||||||
|
"updateNotifText": "Phiên bản mới nhất đang khả dụng: ",
|
||||||
|
|
||||||
|
"chineseCharacterAlert": "Đường dẫn tới tệp tin chứa ký tự phức tạp, có thể gây lỗi!",
|
||||||
|
|
||||||
|
"dialogOk": "OK",
|
||||||
|
"dialogNo": "Không",
|
||||||
|
"serverEnableDialogTitle": "Đã tìm thấy trình khởi chạy Grasscutter!",
|
||||||
|
"serverEnableDialogText": "Không tìm thấy phần cài đặt Grasscutter hiện tại, bạn có muốn tải về?"
|
||||||
|
|
||||||
|
}
|
91
languages/zh-tw.json
Normal file
91
languages/zh-tw.json
Normal file
@ -0,0 +1,91 @@
|
|||||||
|
{
|
||||||
|
"fullLangName": "繁體中文",
|
||||||
|
"appName": "GrassClipper啟動器",
|
||||||
|
|
||||||
|
"playOfficial": "啟動官方伺服器",
|
||||||
|
"playPrivate": "啟動私人伺服器",
|
||||||
|
"launchLocalServer": "啟動本地伺服器",
|
||||||
|
|
||||||
|
"gameExeSet": "設定\"GenshinImpact\".exe執行檔",
|
||||||
|
"grasscutterFileSet": "設定 \"Grasscutter\".jar 檔案",
|
||||||
|
"folderNotSet": "尚未設定Genshin Impact game資料夾",
|
||||||
|
|
||||||
|
"ipPlaceholder": "IP地址",
|
||||||
|
"portPlaceholder": "端口",
|
||||||
|
"noFavorites": "没有收藏",
|
||||||
|
|
||||||
|
"settingsTitle": "設定",
|
||||||
|
"scriptsSectionTitle": "程式設定",
|
||||||
|
"killswitchOption": "終止服務進程",
|
||||||
|
"killswitchSubtitle": "這是給對於封號非常敏感的人們,啟用此選項將會在你Proxy中發生任何錯誤時會終止你的遊戲(還有你的網路。)",
|
||||||
|
"proxyOption": "Proxy",
|
||||||
|
"proxySubtitle": "通過批次檔來安裝Proxy伺服器。",
|
||||||
|
"updateOption": "更新",
|
||||||
|
"updateSubtitle": "自動更新暫時無法使用。查看GitHub以獲取最新的Release。",
|
||||||
|
"languageOption": "語言",
|
||||||
|
"languageSubtitle": "選擇你最熟悉的語言!",
|
||||||
|
"enableServerLauncherOption": "啟用本地伺服器",
|
||||||
|
"enableServerLauncherSubtitle": "啟動器內將會為你顯示啟動本地伺服器選項。",
|
||||||
|
"httpsOption": "使用 HTTPS",
|
||||||
|
"httpsSubtitle": "選擇使用HTTP或HTTPS,此選項適用於私人伺服器。",
|
||||||
|
|
||||||
|
"introSen1": "看來這是你第一次使用GrassClipper啟動器!",
|
||||||
|
"introSen2": "首先,歡迎你使用GrassClipper啟動器,其次很高興在這裡見到你! :)",
|
||||||
|
"introSen3": "是否要運行Proxy安裝程序?",
|
||||||
|
"introSen4": "(需要連接到私人伺服器)",
|
||||||
|
|
||||||
|
"updateBtn": "更新",
|
||||||
|
"proxyInstallBtn": "安裝",
|
||||||
|
"proxyInstallDeny": "不用了,謝謝。",
|
||||||
|
|
||||||
|
"gameFolderDialog": "選擇Genshin Impact game資料夾",
|
||||||
|
"grasscutterFileDialog": "選擇Grasscutter.jar檔案",
|
||||||
|
|
||||||
|
"loggingInTo": "登錄至:",
|
||||||
|
"registeringFor": "註冊至:",
|
||||||
|
"authUsername": "用戶名: ",
|
||||||
|
"authPassword": "密碼: ",
|
||||||
|
"authConfirmPassword": "再次確認密碼: ",
|
||||||
|
"authLoginBtn": "登錄",
|
||||||
|
"authRegisterBtn": "註冊",
|
||||||
|
"authLoginTitle": "登錄",
|
||||||
|
"authRegisterTitle": "註冊",
|
||||||
|
"launchWithoutAuth": "在未認證的情況下啟動",
|
||||||
|
|
||||||
|
"alertInvalid": "用戶名或密碼不正確。",
|
||||||
|
"alertNoPass": "未設定密碼,請先更改密碼",
|
||||||
|
"alertUnknown": "未知錯誤,請聯系伺服器管理員",
|
||||||
|
"alertAuthNoLogin": "未啟用認證,無需登錄!",
|
||||||
|
"alertLoginSuccess": "登錄成功!Token 已複製至剪貼簿。將 Token 複製到用戶名處即可登錄。",
|
||||||
|
|
||||||
|
"alertUserTaken": "用戶名已被占用",
|
||||||
|
"alertPassMismatch": "兩組密碼不一致",
|
||||||
|
"alertAuthNoRegister": "未啟用認證,無需註冊!",
|
||||||
|
"alertRegisterSuccess": "註冊成功!",
|
||||||
|
|
||||||
|
"downloadTitle": "安裝清單",
|
||||||
|
"grassclipperTitle": "GrassClipper",
|
||||||
|
"grasscutterTitle": "Grasscutter",
|
||||||
|
"installerTitle": "安裝程式",
|
||||||
|
"installerSubtitle": "安裝Proxy和其他工具。 Grasscutter伺服器連接時需要。",
|
||||||
|
"downloadStable": "安裝 Stable 構建",
|
||||||
|
"stableSubtitle": "安裝 Stable 分支. 此構建通常具有較少的錯誤,但功能也很少。",
|
||||||
|
"downloadDev": "安裝 Development 構建",
|
||||||
|
"devSubtitle": "安裝 Development 分支. 此版本有時存在錯誤,並且經常更新。 使用此版本風險自負。",
|
||||||
|
"downloadResources": "下載 resources 資料夾",
|
||||||
|
"resourceSubtitle": "將 resources 安裝在目前設置的 Grasscutter 資料夾中。 除非您計劃從外部獲取resources,否則應該這樣做。",
|
||||||
|
|
||||||
|
"gcScriptRunning": "執行中...",
|
||||||
|
"stableInstall": "安裝",
|
||||||
|
"devInstall": "安装",
|
||||||
|
|
||||||
|
"updateNotifText": "有新的GrassClipper更新可用! 最新版本: ",
|
||||||
|
|
||||||
|
"chineseCharacterAlert": "此路徑含有中文字體,這可能會導致問題的發生。",
|
||||||
|
|
||||||
|
"dialogOk": "好的",
|
||||||
|
"dialogNo": "不要",
|
||||||
|
"serverEnableDialogTitle": "您找到了Grasscutter本地伺服器啟動器!",
|
||||||
|
"serverEnableDialogText": "如果你現在還沒有把Grasscutter安裝在電腦上面的話,要不要直接從網路上下載?"
|
||||||
|
|
||||||
|
}
|
84
languages/zh.json
Normal file
84
languages/zh.json
Normal file
@ -0,0 +1,84 @@
|
|||||||
|
{
|
||||||
|
"fullLangName": "简体中文",
|
||||||
|
"appName": "GrassClipper 启动器",
|
||||||
|
|
||||||
|
"playOfficial": "启动官方服务器",
|
||||||
|
"playPrivate": "启动私人服务器",
|
||||||
|
"launchLocalServer": "启动本地服务器",
|
||||||
|
|
||||||
|
"gameExeSet": "选择游戏的可执行文件",
|
||||||
|
"grasscutterFileSet": "选择 \"Grasscutter\" .jar 文件",
|
||||||
|
"folderNotSet": "没有选择游戏可执行文件或 Grasscutter.jar 文件",
|
||||||
|
|
||||||
|
"ipPlaceholder": "服务器 IP 地址",
|
||||||
|
"portPlaceholder": "端口",
|
||||||
|
"noFavorites": "没有收藏任何 IP 地址",
|
||||||
|
|
||||||
|
"settingsTitle": "设置",
|
||||||
|
"scriptsSectionTitle": "脚本",
|
||||||
|
"killswitchOption": "杀死服务进程",
|
||||||
|
"killswitchSubtitle": "为那些害怕被封号的人准备。启用此选项可以结束游戏进程(还可以让你断网,如果修改了代理设置的话)。",
|
||||||
|
"proxyOption": "代理",
|
||||||
|
"proxySubtitle": "通过批处理文件 (install.cmd) 以安装代理服务器。",
|
||||||
|
"updateOption": "更新",
|
||||||
|
"updateSubtitle": "自动更新暂时不可用。查看 GitHub 以获取最新的版本。",
|
||||||
|
"languageOption": "语言",
|
||||||
|
"languageSubtitle": "选择你熟悉的语言以应用 GrassClipper 启动器!",
|
||||||
|
"enableServerLauncherOption": "启用本地服务器",
|
||||||
|
"enableServerLauncherSubtitle": "在启动器内显示启动本地服务器选项。",
|
||||||
|
"httpsOption": "使用 HTTPS",
|
||||||
|
"httpsSubtitle": "选择是否使用 HTTP(S)",
|
||||||
|
|
||||||
|
"introSen1": "看来这是你第一次使用 GrassClipper 启动器!",
|
||||||
|
"introSen2": "首先,欢迎你使用 GrassClipper 启动器,其次很高兴见到你! :)",
|
||||||
|
"introSen3": "是否要运行安装脚本来安装代理服务器?",
|
||||||
|
"introSen4": "(此选项仅提供给需要连接到私人服务器的用户)",
|
||||||
|
"updateBtn": "更新",
|
||||||
|
"proxyInstallBtn": "安装",
|
||||||
|
"proxyInstallDeny": "不用了,谢谢",
|
||||||
|
|
||||||
|
"gameFolderDialog": "选择游戏文件夹",
|
||||||
|
"grasscutterFileDialog": "选择 Grasscutter.jar 文件",
|
||||||
|
|
||||||
|
"loggingInTo": "登录至:",
|
||||||
|
"registeringFor": "注册至:",
|
||||||
|
"authUsername": "用户名: ",
|
||||||
|
"authPassword": "密码: ",
|
||||||
|
"authConfirmPassword": "再次确认密码: ",
|
||||||
|
"authLoginBtn": "登录",
|
||||||
|
"authRegisterBtn": "注册",
|
||||||
|
"authLoginTitle": "登录",
|
||||||
|
"authRegisterTitle": "注册",
|
||||||
|
"launchWithoutAuth": "在未认证的情况下启动",
|
||||||
|
|
||||||
|
"alertInvalid": "无效的用户名或密码",
|
||||||
|
"alertNoPass": "未设置密码,请先更改密码",
|
||||||
|
"alertUnknown": "未知错误,请联系服务器管理员",
|
||||||
|
"alertAuthNoLogin": "未启用认证,无需登录!",
|
||||||
|
"alertLoginSuccess": "登录成功!Token 已复制至剪切板。将 Token 粘贴至用户名处即可登录。",
|
||||||
|
|
||||||
|
"alertUserTaken": "用户名已被占用",
|
||||||
|
"alertPassMismatch": "密码与确认密码不匹配",
|
||||||
|
"alertAuthNoRegister": "未启用认证,无需注册!",
|
||||||
|
"alertRegisterSuccess": "注册成功!",
|
||||||
|
|
||||||
|
"downloadTitle": "下载",
|
||||||
|
"grassclipperTitle": "GrassClipper",
|
||||||
|
"grasscutterTitle": "Grasscutter",
|
||||||
|
"installerTitle": "安装",
|
||||||
|
"installerSubtitle": "安装Grasscutter所需的代理和其他工具。",
|
||||||
|
"downloadStable": "下载Grasscutter稳定版",
|
||||||
|
"stableSubtitle": "安装稳定版的Grasscutter。此版本更加稳定,但是功能相对较少。",
|
||||||
|
"downloadDev": "下载Grasscutter开发版",
|
||||||
|
"devSubtitle": "安装开发版的Grasscutter. 这个版本更新更加频繁,但是有时会有些bug。使用风险自负.",
|
||||||
|
"downloadResources": "下载Grasscutter资源",
|
||||||
|
"resourceSubtitle": "将所需资源下载至当前设置的Grasscutter文件夹。 除非你自己放置了资源,否则你应该执行此操作.",
|
||||||
|
|
||||||
|
"gcScriptRunning": "运行中...",
|
||||||
|
"stableInstall": "下载",
|
||||||
|
"devInstall": "下载",
|
||||||
|
|
||||||
|
"updateNotifText": "有新版本可用!当前最新的版本是: ",
|
||||||
|
|
||||||
|
"chineseCharacterAlert": "当前文件路径含有中文字符,可能会导致一些问题。"
|
||||||
|
}
|
@ -1,5 +1,5 @@
|
|||||||
{
|
{
|
||||||
"applicationId": "js.grassclipper.app",
|
"applicationId": "js.grassclipper.app",
|
||||||
"version": "0.6.0",
|
"version": "0.9.4",
|
||||||
"resourcesURL": "https://github.com/Grasscutters/GrassClipper/releases/latest/download/resources.neu"
|
"resourcesURL": "https://github.com/Grasscutters/GrassClipper/releases/latest/download/resources.neu"
|
||||||
}
|
}
|
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"applicationId": "js.grassclipper.app",
|
"applicationId": "js.grassclipper.app",
|
||||||
"version": "0.6.0",
|
"version": "0.9.4",
|
||||||
"defaultMode": "window",
|
"defaultMode": "window",
|
||||||
"port": 0,
|
"port": 0,
|
||||||
"documentRoot": "/resources/",
|
"documentRoot": "/resources/",
|
||||||
@ -18,6 +18,7 @@
|
|||||||
"filesystem.*",
|
"filesystem.*",
|
||||||
"storage.*",
|
"storage.*",
|
||||||
"window.*",
|
"window.*",
|
||||||
|
"clipboard.*",
|
||||||
"debug.log"
|
"debug.log"
|
||||||
],
|
],
|
||||||
"modes": {
|
"modes": {
|
||||||
@ -45,9 +46,21 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
"chrome": {
|
"chrome": {
|
||||||
"width": 1000,
|
"title": "GrassClipper",
|
||||||
"height": 800,
|
"width": 1280,
|
||||||
"args": "--user-agent=\"Neutralinojs chrome mode\""
|
"height": 720,
|
||||||
|
"minWidth": 400,
|
||||||
|
"minHeight": 200,
|
||||||
|
"fullScreen": false,
|
||||||
|
"alwaysOnTop": false,
|
||||||
|
"icon": "/resources/icons/appIcon.png",
|
||||||
|
"enableInspector": true,
|
||||||
|
"borderless": true,
|
||||||
|
"maximize": false,
|
||||||
|
"hidden": false,
|
||||||
|
"resizable": false,
|
||||||
|
"exitProcessOnClose": true,
|
||||||
|
"args": "--user-agent=\"Neutralinojs chrome mode\" --disable-web-security"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"cli": {
|
"cli": {
|
||||||
|
1585
package-lock.json
generated
Normal file
1585
package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load Diff
15
package.json
15
package.json
@ -1,15 +1,16 @@
|
|||||||
{
|
{
|
||||||
"name": "GrassClipper",
|
"name": "grassclipper",
|
||||||
"version": "0.6.0",
|
"version": "0.9.4",
|
||||||
"repository": "https://github.com/Grasscutters/GrassClipper.git",
|
"repository": "https://github.com/Grasscutters/GrassClipper.git",
|
||||||
"author": "SpikeHD <spikegdofficial@gmail.com>",
|
"author": "SpikeHD <spikegdofficial@gmail.com>",
|
||||||
"license": "Apache-2.0",
|
"license": "Apache-2.0",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"dev": "neu run",
|
"dev": "neu run",
|
||||||
"build": "npm run clean_dist && neu build && npm run move_files && npm run move_bgs && npm run rename_exe",
|
"build-win": ".\\build_win.cmd",
|
||||||
"move_files": "cp -r ./languages ./dist/GrassClipper && cp -r ./proxy ./dist/GrassClipper && cp -r ./scripts ./dist/GrassClipper",
|
"build-linux": "./build.sh",
|
||||||
"move_bgs": "mkdir dist\\GrassClipper\\resources\\bg\\private && cp -r ./resources/bg/private ./dist/GrassClipper/resources/bg && cp -r ./resources/bg/server ./dist/GrassClipper/resources/bg",
|
"build": "echo !! Run build-win or build-linux to build for your platform !!\n"
|
||||||
"rename_exe": "mv ./dist/GrassClipper/GrassClipper-win_x64.exe ./dist/GrassClipper/GrassClipper.exe",
|
},
|
||||||
"clean_dist": "rm -rf ./dist"
|
"devDependencies": {
|
||||||
|
"eslint": "^8.14.0"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -6,7 +6,7 @@
|
|||||||
|
|
||||||
##
|
##
|
||||||
#
|
#
|
||||||
# Genshin Impact script for mitmproxy
|
# Anime game script for mitmproxy
|
||||||
#
|
#
|
||||||
# https://github.com/MlgmXyysd/
|
# https://github.com/MlgmXyysd/
|
||||||
#
|
#
|
||||||
@ -24,19 +24,34 @@ import string
|
|||||||
from mitmproxy import ctx
|
from mitmproxy import ctx
|
||||||
from mitmproxy import http
|
from mitmproxy import http
|
||||||
|
|
||||||
class MlgmXyysd_Genshin_Impact_Proxy:
|
class MlgmXyysd_Anime_Game_Proxy:
|
||||||
|
|
||||||
def load(self, loader):
|
def load(self, loader):
|
||||||
loader.add_option(
|
loader.add_option(
|
||||||
name = "ip",
|
name = "ip",
|
||||||
typespec = str,
|
typespec = str,
|
||||||
default = "localhost",
|
default = "127.0.0.1",
|
||||||
help = "IP address to replace",
|
help = "IP address to replace",
|
||||||
)
|
)
|
||||||
|
|
||||||
|
loader.add_option(
|
||||||
|
name = "port",
|
||||||
|
typespec = int,
|
||||||
|
default = 443,
|
||||||
|
help = "Port to replace",
|
||||||
|
)
|
||||||
|
|
||||||
|
loader.add_option(
|
||||||
|
name = "use_https",
|
||||||
|
typespec = bool,
|
||||||
|
default = True,
|
||||||
|
help = "Use HTTPS",
|
||||||
|
)
|
||||||
|
|
||||||
def request(self, flow: http.HTTPFlow) -> None:
|
def request(self, flow: http.HTTPFlow) -> None:
|
||||||
# This can also be replaced with another IP address.
|
# This can also be replaced with another IP address.
|
||||||
REMOTE_HOST = ctx.options.ip
|
REMOTE_HOST = ctx.options.ip
|
||||||
|
REMOTE_PORT = ctx.options.port
|
||||||
|
|
||||||
LIST_DOMAINS = [
|
LIST_DOMAINS = [
|
||||||
"api-os-takumi.mihoyo.com",
|
"api-os-takumi.mihoyo.com",
|
||||||
@ -63,12 +78,24 @@ class MlgmXyysd_Genshin_Impact_Proxy:
|
|||||||
"sdk-os-static.hoyoverse.com",
|
"sdk-os-static.hoyoverse.com",
|
||||||
"api-account-os.hoyoverse.com",
|
"api-account-os.hoyoverse.com",
|
||||||
"hk4e-sdk-os.hoyoverse.com",
|
"hk4e-sdk-os.hoyoverse.com",
|
||||||
"overseauspider.yuanshen.com"
|
"overseauspider.yuanshen.com",
|
||||||
|
"gameapi-account.mihoyo.com",
|
||||||
|
"minor-api.mihoyo.com",
|
||||||
|
"public-data-api.mihoyo.com",
|
||||||
|
"uspider.yuanshen.com",
|
||||||
|
"sdk-static.mihoyo.com",
|
||||||
|
"abtest-api-data-sg.hoyoverse.com",
|
||||||
|
"log-upload-os.hoyoverse.com"
|
||||||
]
|
]
|
||||||
|
|
||||||
if flow.request.host in LIST_DOMAINS:
|
if flow.request.host in LIST_DOMAINS:
|
||||||
|
if ctx.options.use_https:
|
||||||
|
flow.request.scheme = "https"
|
||||||
|
else:
|
||||||
|
flow.request.scheme = "http"
|
||||||
flow.request.host = REMOTE_HOST
|
flow.request.host = REMOTE_HOST
|
||||||
|
flow.request.port = REMOTE_PORT
|
||||||
|
|
||||||
addons = [
|
addons = [
|
||||||
MlgmXyysd_Genshin_Impact_Proxy()
|
MlgmXyysd_Anime_Game_Proxy()
|
||||||
]
|
]
|
Binary file not shown.
Before Width: | Height: | Size: 12 KiB After Width: | Height: | Size: 12 KiB |
12
resources/icons/alert.svg
Normal file
12
resources/icons/alert.svg
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" width="256" height="256" viewBox="0 0 256 256" xml:space="preserve">
|
||||||
|
<desc>Created with Fabric.js 1.7.22</desc>
|
||||||
|
<defs>
|
||||||
|
</defs>
|
||||||
|
<g transform="translate(128 128) scale(0.72 0.72)" style="">
|
||||||
|
<g style="stroke: none; stroke-width: 0; stroke-dasharray: none; stroke-linecap: butt; stroke-linejoin: miter; stroke-miterlimit: 10; fill: none; fill-rule: nonzero; opacity: 1;" transform="translate(-175.05 -175.05) scale(3.89 3.89)" >
|
||||||
|
<path d="M 53.789 81.581 C 53.583 86.264 49.733 90 45 90 s -8.584 -3.736 -8.789 -8.419 H 53.789 z" style="stroke: none; stroke-width: 1; stroke-dasharray: none; stroke-linecap: butt; stroke-linejoin: miter; stroke-miterlimit: 10; fill: rgb(0,0,0); fill-rule: nonzero; opacity: 1;" transform=" matrix(1 0 0 1 0 0) " stroke-linecap="round" />
|
||||||
|
<path d="M 76.051 64.035 c -1.71 -1.463 -2.691 -3.592 -2.691 -5.841 V 36.035 c 0 -15.638 -12.722 -28.36 -28.36 -28.36 c -15.638 0 -28.36 12.722 -28.36 28.36 v 22.159 c 0 2.25 -0.981 4.379 -2.692 5.84 c -2.564 2.189 -4.034 5.378 -4.034 8.751 c 0 2.739 2.228 4.967 4.967 4.967 h 60.235 c 2.74 0 4.968 -2.228 4.968 -4.967 C 80.085 69.412 78.614 66.223 76.051 64.035 z M 48.709 60.728 c 0 2.048 -1.66 3.709 -3.709 3.709 c -2.048 0 -3.709 -1.66 -3.709 -3.709 v -1.422 c 0 -2.048 1.66 -3.709 3.709 -3.709 c 2.048 0 3.709 1.66 3.709 3.709 V 60.728 z M 48.795 49.091 c -0.048 2.062 -1.733 3.708 -3.795 3.708 c -2.062 0 -3.747 -1.646 -3.795 -3.708 L 40.67 26.075 c -0.049 -2.13 1.664 -3.884 3.795 -3.884 h 1.069 c 2.131 0 3.844 1.754 3.795 3.884 L 48.795 49.091 z" style="stroke: none; stroke-width: 1; stroke-dasharray: none; stroke-linecap: butt; stroke-linejoin: miter; stroke-miterlimit: 10; fill: rgb(0,0,0); fill-rule: nonzero; opacity: 1;" transform=" matrix(1 0 0 1 0 0) " stroke-linecap="round" />
|
||||||
|
<path d="M 38.985 12.2 c -1.057 0 -1.915 -0.857 -1.915 -1.915 V 7.929 C 37.07 3.557 40.628 0 45 0 s 7.929 3.557 7.929 7.929 v 2.355 c 0 1.057 -0.857 1.915 -1.915 1.915 s -1.915 -0.857 -1.915 -1.915 V 7.929 c 0 -2.26 -1.839 -4.1 -4.099 -4.1 c -2.26 0 -4.1 1.839 -4.1 4.1 v 2.356 C 40.9 11.343 40.043 12.2 38.985 12.2 z" style="stroke: none; stroke-width: 1; stroke-dasharray: none; stroke-linecap: butt; stroke-linejoin: miter; stroke-miterlimit: 10; fill: rgb(0,0,0); fill-rule: nonzero; opacity: 1;" transform=" matrix(1 0 0 1 0 0) " stroke-linecap="round" />
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 2.4 KiB |
11
resources/icons/download.svg
Normal file
11
resources/icons/download.svg
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" width="256" height="256" viewBox="0 0 256 256" xml:space="preserve">
|
||||||
|
<desc>Created with Fabric.js 1.7.22</desc>
|
||||||
|
<defs>
|
||||||
|
</defs>
|
||||||
|
<g transform="translate(128 128) scale(0.72 0.72)" style="">
|
||||||
|
<g style="stroke: none; stroke-width: 0; stroke-dasharray: none; stroke-linecap: butt; stroke-linejoin: miter; stroke-miterlimit: 10; fill: none; fill-rule: nonzero; opacity: 1;" transform="translate(-175.05 -175.05) scale(3.89 3.89)" >
|
||||||
|
<path d="M 66.848 42.103 c -2.111 -1.782 -5.266 -1.512 -7.046 0.599 L 50 54.325 V 5 c 0 -2.761 -2.238 -5 -5 -5 c -2.761 0 -5 2.239 -5 5 v 49.325 l -9.802 -11.623 c -1.78 -2.111 -4.934 -2.378 -7.046 -0.599 c -2.111 1.78 -2.379 4.935 -0.599 7.046 l 18.624 22.085 c 0.075 0.088 0.156 0.168 0.236 0.251 c 0.028 0.029 0.054 0.059 0.083 0.088 c 0.2 0.196 0.416 0.372 0.643 0.531 c 0.044 0.031 0.088 0.061 0.133 0.091 c 0.23 0.15 0.471 0.282 0.722 0.392 c 0.05 0.022 0.101 0.04 0.151 0.061 c 0.264 0.105 0.534 0.193 0.813 0.252 c 0.035 0.007 0.071 0.011 0.106 0.018 c 0.306 0.058 0.618 0.094 0.935 0.094 c 0.317 0 0.628 -0.036 0.934 -0.094 c 0.035 -0.007 0.071 -0.01 0.106 -0.018 c 0.279 -0.059 0.549 -0.147 0.812 -0.252 c 0.052 -0.021 0.103 -0.039 0.154 -0.062 c 0.25 -0.11 0.49 -0.241 0.719 -0.39 c 0.047 -0.03 0.092 -0.061 0.137 -0.093 c 0.226 -0.158 0.441 -0.333 0.64 -0.528 c 0.03 -0.029 0.057 -0.061 0.086 -0.091 c 0.079 -0.082 0.161 -0.161 0.234 -0.249 l 18.624 -22.085 C 69.227 47.037 68.958 43.883 66.848 42.103 z" style="stroke: none; stroke-width: 1; stroke-dasharray: none; stroke-linecap: butt; stroke-linejoin: miter; stroke-miterlimit: 10; fill: rgb(0,0,0); fill-rule: nonzero; opacity: 1;" transform=" matrix(1 0 0 1 0 0) " stroke-linecap="round" />
|
||||||
|
<path d="M 79.692 90 H 10.307 c -2.761 0 -5 -2.239 -5 -5 l 0 -20.352 c 0 -2.611 1.909 -4.943 4.508 -5.191 c 2.982 -0.285 5.492 2.053 5.492 4.976 v 14.528 c 0 0.574 0.465 1.039 1.039 1.039 h 57.308 c 0.574 0 1.039 -0.465 1.039 -1.039 V 64.648 c 0 -2.612 1.91 -4.943 4.509 -5.191 c 2.982 -0.284 5.491 2.053 5.491 4.977 V 85 C 84.692 87.761 82.454 90 79.692 90 z" style="stroke: none; stroke-width: 1; stroke-dasharray: none; stroke-linecap: butt; stroke-linejoin: miter; stroke-miterlimit: 10; fill: rgb(0,0,0); fill-rule: nonzero; opacity: 1;" transform=" matrix(1 0 0 1 0 0) " stroke-linecap="round" />
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 2.3 KiB |
10
resources/icons/folder.svg
Normal file
10
resources/icons/folder.svg
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" width="256" height="256" viewBox="0 0 256 256" xml:space="preserve">
|
||||||
|
<desc>Created with Fabric.js 1.7.22</desc>
|
||||||
|
<defs>
|
||||||
|
</defs>
|
||||||
|
<g transform="translate(128 128) scale(0.72 0.72)" style="">
|
||||||
|
<g style="stroke: none; stroke-width: 0; stroke-dasharray: none; stroke-linecap: butt; stroke-linejoin: miter; stroke-miterlimit: 10; fill: none; fill-rule: nonzero; opacity: 1;" transform="translate(-175.05 -175.05) scale(3.89 3.89)" >
|
||||||
|
<path d="M 89.475 31.953 c -0.56 -0.816 -1.485 -1.304 -2.475 -1.304 H 73.97 v -9.181 c 0 -1.657 -1.343 -3 -3 -3 H 30.857 l -2.408 -4.916 c -0.504 -1.028 -1.549 -1.68 -2.694 -1.68 H 3 c -1.657 0 -3 1.343 -3 3 v 60.254 c 0 0.071 0.016 0.137 0.021 0.207 c 0.006 0.083 0.012 0.164 0.025 0.246 c 0.024 0.158 0.063 0.309 0.11 0.458 c 0.02 0.061 0.033 0.123 0.057 0.183 c 0.083 0.211 0.184 0.412 0.31 0.596 c 0.001 0.002 0.002 0.005 0.003 0.007 c 0.009 0.013 0.022 0.023 0.031 0.036 c 0.118 0.166 0.254 0.318 0.402 0.456 c 0.052 0.049 0.109 0.091 0.164 0.135 c 0.119 0.096 0.243 0.183 0.376 0.26 c 0.065 0.038 0.129 0.074 0.196 0.107 c 0.143 0.069 0.292 0.124 0.446 0.17 c 0.062 0.019 0.12 0.042 0.184 0.057 c 0.218 0.05 0.442 0.083 0.675 0.083 h 67.97 c 1.239 0 2.352 -0.763 2.798 -1.919 l 16.03 -41.477 C 90.155 33.808 90.034 32.769 89.475 31.953 z M 23.884 17.873 l 2.408 4.916 c 0.504 1.028 1.549 1.68 2.694 1.68 H 67.97 v 6.181 H 19.03 c -1.24 0 -2.352 0.762 -2.798 1.918 L 6 59.042 v -41.17 H 23.884 z" style="stroke: none; stroke-width: 1; stroke-dasharray: none; stroke-linecap: butt; stroke-linejoin: miter; stroke-miterlimit: 10; fill: rgb(0,0,0); fill-rule: nonzero; opacity: 1;" transform=" matrix(1 0 0 1 0 0) " stroke-linecap="round" />
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 1.7 KiB |
10
resources/icons/refresh.svg
Normal file
10
resources/icons/refresh.svg
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" width="256" height="256" viewBox="0 0 256 256" xml:space="preserve">
|
||||||
|
<desc>Created with Fabric.js 1.7.22</desc>
|
||||||
|
<defs>
|
||||||
|
</defs>
|
||||||
|
<g transform="translate(128 128) scale(0.72 0.72)" style="">
|
||||||
|
<g style="stroke: none; stroke-width: 0; stroke-dasharray: none; stroke-linecap: butt; stroke-linejoin: miter; stroke-miterlimit: 10; fill: none; fill-rule: nonzero; opacity: 1;" transform="translate(-175.05 -175.05) scale(3.89 3.89)" >
|
||||||
|
<path d="M 75.702 53.014 c -2.142 7.995 -7.27 14.678 -14.439 18.816 c -7.168 4.138 -15.519 5.239 -23.514 3.095 c -16.505 -4.423 -26.335 -21.448 -21.913 -37.953 C 20.258 20.467 37.286 10.64 53.79 15.06 c 4.213 1.129 8.076 3.118 11.413 5.809 l -8.349 8.35 h 26.654 V 2.565 l -8.354 8.354 c -5.1 -4.405 -11.133 -7.61 -17.74 -9.381 C 33.451 -4.882 8.735 9.389 2.314 33.35 c -6.42 23.961 7.851 48.678 31.811 55.098 C 38.001 89.486 41.934 90 45.842 90 c 7.795 0 15.488 -2.044 22.42 -6.046 c 10.407 -6.008 17.851 -15.709 20.962 -27.317 L 75.702 53.014 z" style="stroke: none; stroke-width: 1; stroke-dasharray: none; stroke-linecap: butt; stroke-linejoin: miter; stroke-miterlimit: 10; fill: rgb(0,0,0); fill-rule: nonzero; opacity: 1;" transform=" matrix(1 0 0 1 0 0) " stroke-linecap="round" />
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 1.3 KiB |
@ -2,13 +2,37 @@
|
|||||||
<html>
|
<html>
|
||||||
<head>
|
<head>
|
||||||
<link rel="stylesheet" type="text/css" href="style/index.css" />
|
<link rel="stylesheet" type="text/css" href="style/index.css" />
|
||||||
|
<script src="https://unpkg.com/axios/dist/axios.min.js"></script>
|
||||||
<script src="js/neutralino.js"></script>
|
<script src="js/neutralino.js"></script>
|
||||||
<script src="js/windowDrag.js"></script>
|
<script src="js/windowDrag.js"></script>
|
||||||
<script src="js/hoverEvt.js"></script>
|
<script src="js/hoverEvt.js"></script>
|
||||||
|
<script src="js/helpers.js"></script>
|
||||||
<script src="js/index.js"></script>
|
<script src="js/index.js"></script>
|
||||||
|
<script src="js/gcdownloader.js"></script>
|
||||||
|
<script src="js/onLoad.js"></script>
|
||||||
|
<script src="js/alerts.js"></script>
|
||||||
|
<script src="js/options.js"></script>
|
||||||
|
<script src="js/login.js"></script>
|
||||||
|
<script src="js/authAlert.js"></script>
|
||||||
<script src="js/translation.js"></script>
|
<script src="js/translation.js"></script>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
|
<!-- Alert Box -->
|
||||||
|
<div id="alert">
|
||||||
|
<span id="alertText">This is a test alert</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Misc dialog -->
|
||||||
|
<div id="miscDialog" style="display: none">
|
||||||
|
<span id="dialogTitle">Dialog!</span>
|
||||||
|
<span id="dialogContent">This is dialog content!</span>
|
||||||
|
<div id="dialogBtns">
|
||||||
|
<button class="altBtn" id="dialogButtonAffirm">OK</button>
|
||||||
|
<button class="altBtn" id="dialogButtonNeg">NO</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- First time setup -->
|
||||||
<div id="firstTimeNotice" style="display: none">
|
<div id="firstTimeNotice" style="display: none">
|
||||||
<span>
|
<span>
|
||||||
</span>
|
</span>
|
||||||
@ -17,6 +41,150 @@
|
|||||||
<button class="altBtn" id="firstTimeDenyBtn" onclick="closeFirstTimePopup()">No thanks</button>
|
<button class="altBtn" id="firstTimeDenyBtn" onclick="closeFirstTimePopup()">No thanks</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<!-- Login popup -->
|
||||||
|
<div id="loginPanel" style="display: none;">
|
||||||
|
<div id="loginPopupTitle">
|
||||||
|
<span>
|
||||||
|
<span id="loginSectionTitle" class="selectedTitle" onclick="setLoginSection()">Login</span>
|
||||||
|
<span id="registerSectionTitle" onclick="setRegisterSection()">Register</span>
|
||||||
|
</span>
|
||||||
|
<div id="loginPopupCloseBtn" onclick="closeLogin()">
|
||||||
|
<img src="icons/close.svg" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Login section -->
|
||||||
|
<div id="loginPopupContentBody" class="authBody">
|
||||||
|
<div id="loginPopupContentBodyText">
|
||||||
|
<span id="loggingInTo">
|
||||||
|
<span id="loggingInToIndicator">Logging in to </span><span id="loginPopupServer"></span>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<div id="loginPopupContentBodyInputs" class="authInputs">
|
||||||
|
<div id="loginPopupContentBodyInputsUsername">
|
||||||
|
<span id="loginUsernameIndicator">
|
||||||
|
Username:
|
||||||
|
</span>
|
||||||
|
<input type="text" id="loginUsername" />
|
||||||
|
</div>
|
||||||
|
<div id="loginPopupContentBodyInputsPassword">
|
||||||
|
<span id="loginPasswordIndicator">
|
||||||
|
Password:
|
||||||
|
</span>
|
||||||
|
<input type="password" id="loginPassword" onsubmit="login()" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div id="loginPopupContentBodyBtns">
|
||||||
|
<button class="playBtn" id="loginPopupContentBodyBtnLogin" onclick="login()">Login</button>
|
||||||
|
<button class="altBtn" id="loginPopupContentBodyBtnRegister" onclick="setRegisterSection(true)">Register</button>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<button class="altBtn" id="noLoginBtn" onclick="(() => { launchPrivate(); closeLogin()})()">
|
||||||
|
Launch without Authentication
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Alert element for any problems that arise -->
|
||||||
|
<div id="loginAlert" style="display: none">
|
||||||
|
<img src="icons/alert.svg" />
|
||||||
|
<span id="loginAlertText"></span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Register section -->
|
||||||
|
<div id="registerPopupContentBody" style="display: none" class="authBody">
|
||||||
|
<div id="registerPopupContentBodyText">
|
||||||
|
<span id="registeringTo">
|
||||||
|
<span id="registeringToIndicator">Registering for </span><span id="registerPopupServer"></span>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<div id="registerPopupContentBodyInputs" class="authInputs">
|
||||||
|
<div id="registerPopupContentBodyInputsUsername">
|
||||||
|
<span id="registerUsernameIndicator">
|
||||||
|
Username:
|
||||||
|
</span>
|
||||||
|
<input type="text" id="registerUsername" />
|
||||||
|
</div>
|
||||||
|
<div id="registerPopupContentBodyInputsPassword">
|
||||||
|
<span id="registerPasswordIndicator">
|
||||||
|
Password:
|
||||||
|
</span>
|
||||||
|
<input type="password" id="registerPassword" />
|
||||||
|
</div>
|
||||||
|
<div id="registerPopupContentBodyInputsPasswordConfirm">
|
||||||
|
<span id="registerConfirmIndicator">
|
||||||
|
Confirm Password:
|
||||||
|
</span>
|
||||||
|
<input type="password" id="registerPasswordConfirm" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div id="registerPopupContentBodyBtns">
|
||||||
|
<button class="playBtn" id="registerPopupContentBodyBtnRegister" onclick="register()">Register</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Alert element for any problems that arise -->
|
||||||
|
<div id="registerAlert" style="display: none">
|
||||||
|
<img src="icons/alert.svg" />
|
||||||
|
<span id="registerAlertText"></span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div id="downloadPanel" style="display: none;">
|
||||||
|
<div id="downloadTitleBar">
|
||||||
|
<span id="downloadTitle">Downloads</span>
|
||||||
|
<div id="downloadClose" onclick="closeDownloads()">
|
||||||
|
<img src="icons/close.svg" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div id="downloadPanelInner">
|
||||||
|
<div class="downloadTitle", id="grassclipperTitle">
|
||||||
|
GrassClipper
|
||||||
|
</div>
|
||||||
|
<div class="downloadRow">
|
||||||
|
<div class="downloadSection">
|
||||||
|
<span class="downloadLabel" id="installerTitle">Installer</span>
|
||||||
|
<button class="smolBtn" onclick="runInstallScript()" id="proxyInstall">Install</button>
|
||||||
|
</div>
|
||||||
|
<span class="downloadSubtitle" id="installerSubtitle">
|
||||||
|
Installs proxy and other tools. Required for Grasscutter servers.
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<div class="downloadTitle", id="grasscutterTitle">
|
||||||
|
Grasscutter
|
||||||
|
</div>
|
||||||
|
<div class="downloadRow">
|
||||||
|
<div class="downloadSection">
|
||||||
|
<span class="downloadLabel" id="downloadStable">Download Grasscutter Stable Build</span>
|
||||||
|
<button class="smolBtn" onclick="downloadGC('stable')" id="stableInstall">Download</button>
|
||||||
|
</div>
|
||||||
|
<span class="downloadSubtitle" id="stableSubtitle">
|
||||||
|
Install Grasscutter stable branch. This build usually has less bugs, but also less features.
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<div class="downloadRow">
|
||||||
|
<div class="downloadSection">
|
||||||
|
<span class="downloadLabel" id="downloadDev">Download Grasscutter Development Build</span>
|
||||||
|
<button class="smolBtn" onclick="downloadGC('development')" id="devInstall">Download</button>
|
||||||
|
</div>
|
||||||
|
<span class="downloadSubtitle" id="devSubtitle">
|
||||||
|
Install Grasscutter development branch. This build sometimes has bugs, and is frequently updated. Use at your own risk.
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<div class="downloadRow">
|
||||||
|
<div class="downloadSection">
|
||||||
|
<span class="downloadLabel" id="downloadResources">Download Grasscutter Resources</span>
|
||||||
|
<button class="smolBtn" onclick="downloadGC('development')" id="resourceInstall">Download</button>
|
||||||
|
</div>
|
||||||
|
<span class="downloadSubtitle" id="resourceSubtitle">
|
||||||
|
Downloads Grasscutter resources into the currently set Grasscutter folder. This should be done unless you plan on getting resources externally.
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Setting panel -->
|
||||||
<div id="settingsPanel" style="display: none;">
|
<div id="settingsPanel" style="display: none;">
|
||||||
<div id="settingsTitleBar">
|
<div id="settingsTitleBar">
|
||||||
<span id="fullSettingsTitle">Settings</span>
|
<span id="fullSettingsTitle">Settings</span>
|
||||||
@ -74,13 +242,31 @@
|
|||||||
Select your language!
|
Select your language!
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="settingsRow">
|
||||||
|
<div class="settingSection">
|
||||||
|
<span class="settingLabel", id="httpsTitle">Use HTTPS</span>
|
||||||
|
<input type="checkbox" id="httpsOption" onchange="toggleHttps()" />
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
<span class="settingSubtitle" id="httpsSubtitle">
|
||||||
|
Choose between using HTTPS or HTTP.
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<!-- Top bar (title, version, setting btn, close btn, etc.) -->
|
||||||
<div id="controlBar">
|
<div id="controlBar">
|
||||||
<span id="titleSection">
|
<span id="titleSection">
|
||||||
GrassClipper
|
GrassClipper
|
||||||
<span id="version">0.4.5</span>
|
<span id="version">0.0.0</span>
|
||||||
</span>
|
</span>
|
||||||
|
<div id="refreshBtn" onclick="window.location.reload()">
|
||||||
|
<img src="icons/refresh.svg" />
|
||||||
|
</div>
|
||||||
|
<div id="downloadBtn" onclick="openDownloads()">
|
||||||
|
<img src="icons/download.svg" />
|
||||||
|
</div>
|
||||||
<div id="settingsBtn" onclick="openSettings()">
|
<div id="settingsBtn" onclick="openSettings()">
|
||||||
<img src="icons/cog.svg" />
|
<img src="icons/cog.svg" />
|
||||||
</div>
|
</div>
|
||||||
@ -91,37 +277,59 @@
|
|||||||
<img src="icons/close.svg" />
|
<img src="icons/close.svg" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<!-- Main content -->
|
||||||
<div id="panelContainer">
|
<div id="panelContainer">
|
||||||
|
<!-- Official play btn -->
|
||||||
<div id="firstPanel">
|
<div id="firstPanel">
|
||||||
<button class="playBtn" id="playOfficial" onclick="launchOfficial()">Play Official</button>
|
<button class="playBtn" id="playOfficial" onclick="launchOfficial()">Play Official</button>
|
||||||
</div>
|
</div>
|
||||||
|
<!-- Grasscutter play btn -->
|
||||||
<div id="secondPanel">
|
<div id="secondPanel">
|
||||||
<div id="ipList" style="display: none;"></div>
|
<div id="ipList" style="display: none;"></div>
|
||||||
<div id="secondControlContainer">
|
<div id="secondControlContainer">
|
||||||
|
<!-- ip/server input -->
|
||||||
<div id="serverInput">
|
<div id="serverInput">
|
||||||
<input type="text" id="ip" placeholder="IP Address" oninput="handleFavoriteInput()"/>
|
<input type="text" id="ip" placeholder="IP Address" oninput="handleFavoriteInput()"/>
|
||||||
|
<input type="text" id="port" placeholder="Port" oninput="handleFavoriteInput()"/>
|
||||||
<img src="icons/star_empty.svg" id="star" onclick="setFavorite()" />
|
<img src="icons/star_empty.svg" id="star" onclick="setFavorite()" />
|
||||||
<img src="icons/list.svg" id="star" onclick="handleFavoriteList()" />
|
<img src="icons/list.svg" id="star" onclick="handleFavoriteList()" />
|
||||||
</div>
|
</div>
|
||||||
<button class="playBtn" id="playPrivate" onclick="launchPrivate()">Play Private</button>
|
<button class="playBtn" id="playPrivate" onclick="openLogin()">Play Private</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<!-- Grasscutter server launcher -->
|
||||||
<div id="thirdPanel" style="display: none;">
|
<div id="thirdPanel" style="display: none;">
|
||||||
<button class="playBtn" id="serverLaunch" onclick="launchLocalServer()">Launch Local Server</button>
|
<button class="playBtn" id="serverLaunch" onclick="launchLocalServer()">Launch Local Server</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<!-- Bottom section (folder setting btns and such) -->
|
||||||
<div id="bottomBar">
|
<div id="bottomBar">
|
||||||
<div class="bottomSection">
|
<div class="bottomSection">
|
||||||
<div>
|
<div>
|
||||||
<button class="smolBtn" onclick="setGenshinImpactFolder()" id="genshinFolderSet">Set "Genshin Impact Game" folder</button>
|
<div>
|
||||||
<span id="genshinPath" style="margin-top: 4px;"></span>
|
<button class="smolBtn" onclick="setGameExe()" id="gameExeSet">Set game folder</button>
|
||||||
|
<img src="icons/folder.svg" class="openFolderIcon" onclick="openGameFolder()"/>
|
||||||
|
</div>
|
||||||
|
<span id="gamePath" style="margin-top: 4px;"></span>
|
||||||
</div>
|
</div>
|
||||||
<div style="display: none;">
|
<div style="display: none;">
|
||||||
<button class="smolBtn" onclick="setGrassCutterFolder()" id="grasscutterFileSet">Set "GrassCutter" .jar file</button>
|
<div>
|
||||||
|
<button class="smolBtn" onclick="setGrasscutterFolder()" id="grasscutterFileSet">Set "Grasscutter" .jar file</button>
|
||||||
|
<img src="icons/folder.svg" class="openFolderIcon" onclick="openGrasscutterFolder()"/>
|
||||||
|
</div>
|
||||||
<span id="serverPath" style="margin-top: 4px;"></span>
|
<span id="serverPath" style="margin-top: 4px;"></span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<!-- Download notification -->
|
||||||
|
<div id="downloadNotif" onclick="openLatestDownload()">
|
||||||
|
<img src="icons/alert.svg" />
|
||||||
|
<span id="updateNotifText">
|
||||||
|
A new update is available! Newest version:
|
||||||
|
</span><span id="newestVersion"></span>
|
||||||
|
</div>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
19
resources/js/alerts.js
Normal file
19
resources/js/alerts.js
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
function displayAlert(message, clear = 4000) {
|
||||||
|
const alert = document.getElementById('alert')
|
||||||
|
const alertText = document.getElementById('alertText')
|
||||||
|
|
||||||
|
alert.classList.add('show')
|
||||||
|
|
||||||
|
alertText.innerText = message
|
||||||
|
|
||||||
|
setTimeout(() => {
|
||||||
|
hideAlert()
|
||||||
|
}, clear)
|
||||||
|
}
|
||||||
|
|
||||||
|
function hideAlert() {
|
||||||
|
const alert = document.getElementById('alert')
|
||||||
|
const alertText = document.getElementById('alertText')
|
||||||
|
|
||||||
|
alert.classList.remove('show')
|
||||||
|
}
|
45
resources/js/authAlert.js
Normal file
45
resources/js/authAlert.js
Normal file
@ -0,0 +1,45 @@
|
|||||||
|
let alertTimeout, alertCooldown = 3000
|
||||||
|
|
||||||
|
async function displayLoginAlert(message, type, cooldown = null) {
|
||||||
|
displayAuthAlert(message, type, cooldown, 'login')
|
||||||
|
}
|
||||||
|
|
||||||
|
async function displayRegisterAlert(message, type, cooldown = null) {
|
||||||
|
displayAuthAlert(message, type, cooldown, 'register')
|
||||||
|
}
|
||||||
|
|
||||||
|
function displayAuthAlert(message, type, cooldown, name) {
|
||||||
|
const elm = document.getElementById(`${name}Alert`)
|
||||||
|
const text = document.getElementById(`${name}AlertText`)
|
||||||
|
|
||||||
|
elm.style.removeProperty('display')
|
||||||
|
|
||||||
|
// Remove classification classes
|
||||||
|
elm.classList.remove('error')
|
||||||
|
elm.classList.remove('success')
|
||||||
|
elm.classList.remove('warn')
|
||||||
|
|
||||||
|
switch(type) {
|
||||||
|
case 'error':
|
||||||
|
elm.classList.add('error')
|
||||||
|
break
|
||||||
|
|
||||||
|
case 'success':
|
||||||
|
elm.classList.add('success')
|
||||||
|
break
|
||||||
|
|
||||||
|
case 'warn':
|
||||||
|
default:
|
||||||
|
elm.classList.add('warn')
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
text.innerText = message
|
||||||
|
|
||||||
|
clearTimeout(alertTimeout)
|
||||||
|
|
||||||
|
// Disappear after cooldown
|
||||||
|
alertTimeout = setTimeout(() => {
|
||||||
|
elm.style.display = 'none'
|
||||||
|
}, cooldown || alertCooldown)
|
||||||
|
}
|
105
resources/js/gcdownloader.js
Normal file
105
resources/js/gcdownloader.js
Normal file
@ -0,0 +1,105 @@
|
|||||||
|
async function clearGCInstallation() {
|
||||||
|
Neutralino.os.execCommand('del /s /q "./gc"')
|
||||||
|
}
|
||||||
|
|
||||||
|
async function setDownloadButtonsToLoading() {
|
||||||
|
const stableBtn = document.querySelector('#stableInstall')
|
||||||
|
const devBtn = document.querySelector('#devInstall')
|
||||||
|
|
||||||
|
stableBtn.innerText = localeObj.gcScriptRunning || 'Running...'
|
||||||
|
|
||||||
|
devBtn.innerText = localeObj.gcScriptRunning || 'Running...'
|
||||||
|
|
||||||
|
// Set btns to disabled
|
||||||
|
stableBtn.disabled = true
|
||||||
|
stableBtn.classList.add('disabled')
|
||||||
|
|
||||||
|
devBtn.disabled = true
|
||||||
|
devBtn.classList.add('disabled')
|
||||||
|
}
|
||||||
|
|
||||||
|
async function resetDownloadButtons() {
|
||||||
|
const stableBtn = document.querySelector('#stableInstall')
|
||||||
|
const devBtn = document.querySelector('#devInstall')
|
||||||
|
|
||||||
|
stableBtn.innerText = localeObj.stableInstall || 'Download'
|
||||||
|
devBtn.innerText = localeObj.devInstall || 'Download'
|
||||||
|
|
||||||
|
// Set btns to enabled
|
||||||
|
stableBtn.disabled = false
|
||||||
|
stableBtn.classList.remove('disabled')
|
||||||
|
|
||||||
|
devBtn.disabled = false
|
||||||
|
devBtn.classList.remove('disabled')
|
||||||
|
}
|
||||||
|
|
||||||
|
async function downloadGC(branch) {
|
||||||
|
const config = await getCfg()
|
||||||
|
|
||||||
|
// If we are pulling from a new branch, delete the old installation
|
||||||
|
if (config.grasscutterBranch !== branch) await clearGCInstallation()
|
||||||
|
|
||||||
|
// Set current installation in config
|
||||||
|
config.grasscutterBranch = branch
|
||||||
|
|
||||||
|
// Set gc path for people with launcher enabled
|
||||||
|
config.serverFolder = `${NL_CWD}\\gc-${branch}\\grasscutter.jar`
|
||||||
|
|
||||||
|
// Enable server launcher
|
||||||
|
config.serverLaunchPanel = true
|
||||||
|
|
||||||
|
Neutralino.storage.setData('config', JSON.stringify(config))
|
||||||
|
|
||||||
|
setDownloadButtonsToLoading()
|
||||||
|
|
||||||
|
// Keystore for branch (since they can differ)
|
||||||
|
const keystoreUrl = `https://github.com/Grasscutters/Grasscutter/raw/${branch}/keystore.p12`
|
||||||
|
|
||||||
|
// External service that allows un-authed artifact downloading
|
||||||
|
const artiUrl = `https://nightly.link/Grasscutters/Grasscutter/workflows/build/${branch}/Grasscutter.zip`
|
||||||
|
|
||||||
|
// For data files
|
||||||
|
const dataFiles = await axios.get(`https://api.github.com/repos/Grasscutters/Grasscutter/contents/data?ref=${branch}`)
|
||||||
|
const dataList = dataFiles.data
|
||||||
|
.map(file => ({ path: file.path, filename: file.name }))
|
||||||
|
.map(o => ({ url: `https://raw.githubusercontent.com/Grasscutters/Grasscutter/${branch}/${o.path}`, filename: o.filename }))
|
||||||
|
|
||||||
|
// For key files
|
||||||
|
const keyFiles = await axios.get(`https://api.github.com/repos/Grasscutters/Grasscutter/contents/keys?ref=${branch}`)
|
||||||
|
const keyList = keyFiles.data
|
||||||
|
.map(file => ({ path: file.path, filename: file.name }))
|
||||||
|
.map(o => ({ url: `https://raw.githubusercontent.com/Grasscutters/Grasscutter/${branch}/${o.path}`, filename: o.filename }))
|
||||||
|
|
||||||
|
const serverFolderFixed = config.serverFolder.match(/.*\\|.*\//g, '')[0].replace(/\//g, '\\')
|
||||||
|
|
||||||
|
// Ensure data and key folders exist
|
||||||
|
|
||||||
|
console.log(config.serverFolder)
|
||||||
|
console.log(serverFolderFixed)
|
||||||
|
|
||||||
|
await Neutralino.os.execCommand(`mkdir ${serverFolderFixed}\\data`)
|
||||||
|
await Neutralino.os.execCommand(`mkdir ${serverFolderFixed}\\keys`)
|
||||||
|
|
||||||
|
// Download data files
|
||||||
|
for (const o of dataList) {
|
||||||
|
const folder = 'data'
|
||||||
|
await Neutralino.os.execCommand(`powershell Invoke-WebRequest -Uri ${o.url} -OutFile "${serverFolderFixed}\\${folder}\\${o.filename}"`)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Download key files
|
||||||
|
for (const o of keyList) {
|
||||||
|
const folder = 'keys'
|
||||||
|
await Neutralino.os.execCommand(`powershell Invoke-WebRequest -Uri ${o.url} -OutFile "${serverFolderFixed}\\${folder}\\${o.filename}"`)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Run installer
|
||||||
|
createCmdWindow(`.\\scripts\\gc_download.cmd ${artiUrl} ${keystoreUrl} ${branch}`)
|
||||||
|
|
||||||
|
// Fix buttons
|
||||||
|
resetDownloadButtons()
|
||||||
|
|
||||||
|
// Display folder after saving config
|
||||||
|
displayServerFolder()
|
||||||
|
enableServerButton()
|
||||||
|
displayServerLaunchSection()
|
||||||
|
}
|
169
resources/js/helpers.js
Normal file
169
resources/js/helpers.js
Normal file
@ -0,0 +1,169 @@
|
|||||||
|
/**
|
||||||
|
* Get configuration
|
||||||
|
*
|
||||||
|
* @returns {Promise<string>}
|
||||||
|
*/
|
||||||
|
async function getCfg() {
|
||||||
|
const defaultConf = {
|
||||||
|
gameexe: '',
|
||||||
|
serverFolder: '',
|
||||||
|
lastConnect: '',
|
||||||
|
enableKillswitch: false,
|
||||||
|
serverLaunchPanel: false,
|
||||||
|
language: 'en',
|
||||||
|
useHttps: true,
|
||||||
|
grasscutterBranch: '',
|
||||||
|
}
|
||||||
|
const cfgStr = await Neutralino.storage.getData('config').catch(e => {
|
||||||
|
// The data isn't set, so this is our first time opening
|
||||||
|
Neutralino.storage.setData('config', JSON.stringify(defaultConf))
|
||||||
|
|
||||||
|
// Show the first time notice if there is no config
|
||||||
|
document.querySelector('#firstTimeNotice').style.display = 'block'
|
||||||
|
})
|
||||||
|
|
||||||
|
const config = cfgStr ? JSON.parse(cfgStr) : defaultConf
|
||||||
|
|
||||||
|
return config
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the list of favorite IPs
|
||||||
|
*
|
||||||
|
* @returns {Promise<string[]>}
|
||||||
|
*/
|
||||||
|
async function getFavIps() {
|
||||||
|
const ipStr = await Neutralino.storage.getData('favorites').catch(e => {
|
||||||
|
// The data isn't set, so this is our first time opening
|
||||||
|
Neutralino.storage.setData('favorites', JSON.stringify([]))
|
||||||
|
})
|
||||||
|
|
||||||
|
const ipArr = ipStr ? JSON.parse(ipStr) : []
|
||||||
|
|
||||||
|
return ipArr
|
||||||
|
}
|
||||||
|
|
||||||
|
async function proxyIsInstalled() {
|
||||||
|
// Check if the proxy server is installed
|
||||||
|
const curDirList = await filesystem.readDirectory(NL_CWD)
|
||||||
|
|
||||||
|
if (curDirList.find(f => f.entry === 'ext')) {
|
||||||
|
const extFiles = await filesystem.readDirectory(NL_CWD + '/ext')
|
||||||
|
|
||||||
|
if (extFiles.find(f => f.entry === 'mitmdump.exe')) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
async function checkForUpdates() {
|
||||||
|
const url = 'https://api.github.com/repos/Grasscutters/GrassClipper/releases/latest'
|
||||||
|
|
||||||
|
const { data } = await axios.get(url)
|
||||||
|
const latest = data.tag_name
|
||||||
|
|
||||||
|
return latest
|
||||||
|
}
|
||||||
|
|
||||||
|
async function displayUpdate() {
|
||||||
|
const latest = await checkForUpdates()
|
||||||
|
const versionDisplay = document.querySelector('#newestVersion')
|
||||||
|
const notif = document.querySelector('#downloadNotif')
|
||||||
|
|
||||||
|
if (latest === `v${NL_APPVERSION}`) return
|
||||||
|
|
||||||
|
versionDisplay.innerText = latest
|
||||||
|
|
||||||
|
notif.classList.add('displayed')
|
||||||
|
|
||||||
|
setTimeout(() => {
|
||||||
|
notif.classList.remove('displayed')
|
||||||
|
}, 5000)
|
||||||
|
}
|
||||||
|
|
||||||
|
async function openLatestDownload() {
|
||||||
|
const downloadLink = 'https://github.com/Grasscutters/GrassClipper/releases/latest/'
|
||||||
|
|
||||||
|
Neutralino.os.open(downloadLink)
|
||||||
|
}
|
||||||
|
|
||||||
|
async function openGameFolder() {
|
||||||
|
const config = await getCfg()
|
||||||
|
const folder = config.gameexe.match(/.*\\|.*\//g, '')
|
||||||
|
|
||||||
|
if (folder.length > 0) openInExplorer(folder[0].replace(/\//g, '\\'))
|
||||||
|
}
|
||||||
|
|
||||||
|
async function openGrasscutterFolder() {
|
||||||
|
const config = await getCfg()
|
||||||
|
const folder = config.serverFolder.match(/.*\\|.*\//g, '')
|
||||||
|
|
||||||
|
if (folder.length > 0) openInExplorer(folder[0].replace(/\//g, '\\'))
|
||||||
|
}
|
||||||
|
|
||||||
|
// https://www.jimzhao.us/2015/09/javascript-detect-chinese-character.html
|
||||||
|
function hasChineseChars(str) {
|
||||||
|
let re1 = new RegExp(/[\u4E00-\uFA29]/) //Chinese character range
|
||||||
|
let re2 = new RegExp(/[\uE7C7-\uE7F3]/) //non Chinese character range
|
||||||
|
str = str.replace(/\s/g, '')
|
||||||
|
|
||||||
|
if (!re1.test(str) || re2.test(str)) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
function openDialog(title, message, negBtn = false, affirmBtn = closeDialog) {
|
||||||
|
const dialog = document.getElementById('miscDialog')
|
||||||
|
const titleElm = document.getElementById('dialogTitle')
|
||||||
|
const contents = document.getElementById('dialogContent')
|
||||||
|
const noBtn = document.getElementById('dialogButtonNeg')
|
||||||
|
const yesBtn = document.getElementById('dialogButtonAffirm')
|
||||||
|
|
||||||
|
if (!negBtn) {
|
||||||
|
noBtn.style.display = 'none'
|
||||||
|
} else {
|
||||||
|
noBtn.style.removeProperty('display')
|
||||||
|
noBtn.onclick = () => closeDialog()
|
||||||
|
}
|
||||||
|
|
||||||
|
yesBtn.innerText = localeObj.dialogYes || 'OK'
|
||||||
|
noBtn.innerText = localeObj.dialogNo || 'NO'
|
||||||
|
|
||||||
|
yesBtn.onclick = () => {
|
||||||
|
affirmBtn()
|
||||||
|
closeDialog()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set title and message
|
||||||
|
titleElm.innerText = title
|
||||||
|
contents.innerText = message
|
||||||
|
|
||||||
|
// Show the dialog
|
||||||
|
dialog.style.display = 'block'
|
||||||
|
}
|
||||||
|
|
||||||
|
function closeDialog() {
|
||||||
|
const dialog = document.getElementById('miscDialog')
|
||||||
|
|
||||||
|
dialog.style.display = 'none'
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Minimize the window
|
||||||
|
*/
|
||||||
|
function minimizeWin() {
|
||||||
|
Neutralino.window.minimize()
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Close the window
|
||||||
|
*/
|
||||||
|
function closeWin() {
|
||||||
|
Neutralino.app.exit()
|
||||||
|
|
||||||
|
window.close()
|
||||||
|
}
|
@ -1,127 +1,15 @@
|
|||||||
Neutralino.init();
|
Neutralino.init()
|
||||||
|
|
||||||
let localeObj;
|
NL_CWD = NL_CWD.replace(/\//g, '\\')
|
||||||
|
|
||||||
|
let localeObj
|
||||||
const filesystem = Neutralino.filesystem
|
const filesystem = Neutralino.filesystem
|
||||||
|
const createCmdWindow = async (command) => {
|
||||||
/**
|
Neutralino.os.execCommand(`cmd.exe /c start "" ${command}`, { background: true })
|
||||||
* Every autofill, such as backgrounds and the game folder,
|
|
||||||
* should be done here to ensure DOM contents are loaded.
|
|
||||||
*/
|
|
||||||
document.addEventListener('DOMContentLoaded', async () => {
|
|
||||||
setBackgroundImage();
|
|
||||||
displayGenshinFolder();
|
|
||||||
displayServerFolder();
|
|
||||||
|
|
||||||
// Set title version
|
|
||||||
document.querySelector('#version').innerHTML = NL_APPVERSION
|
|
||||||
|
|
||||||
const config = await getCfg()
|
|
||||||
const ipArr = await getFavIps()
|
|
||||||
|
|
||||||
if (config.serverLaunchPanel) {
|
|
||||||
displayServerLaunchSection()
|
|
||||||
}
|
|
||||||
|
|
||||||
// Set last connect
|
|
||||||
document.querySelector('#ip').value = config.lastConnect
|
|
||||||
|
|
||||||
if (ipArr.includes(config.lastConnect)) {
|
|
||||||
document.querySelector('#star').src = 'icons/star_filled.svg'
|
|
||||||
}
|
|
||||||
|
|
||||||
// Disable private game launch if proxy IP or proxy server is not found
|
|
||||||
const playPriv = document.querySelector('#playPrivate')
|
|
||||||
|
|
||||||
if (!(await proxyIsInstalled())) {
|
|
||||||
playPriv.classList.add('disabled')
|
|
||||||
playPriv.disabled = true
|
|
||||||
}
|
|
||||||
|
|
||||||
// Exit favorites list and settings panel when clicking outside of it
|
|
||||||
window.addEventListener("click", function(e) {
|
|
||||||
const favList = document.querySelector('#ipList')
|
|
||||||
const settingsPanel = document.querySelector('#settingsPanel')
|
|
||||||
|
|
||||||
// This will close the favorites list no matter what is clicked
|
|
||||||
if (favList.style.display !== 'none') {
|
|
||||||
favList.style.display = 'none'
|
|
||||||
favList.style.transform = ''
|
|
||||||
}
|
|
||||||
|
|
||||||
// This will close the settings panel no matter what is clicked
|
|
||||||
let settingCheckElm = e.target
|
|
||||||
|
|
||||||
while(settingCheckElm.tagName !== 'BODY') {
|
|
||||||
if (settingCheckElm.id === 'settingsPanel'
|
|
||||||
|| settingCheckElm.id === 'settingsBtn') {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
settingCheckElm = settingCheckElm.parentElement
|
|
||||||
}
|
|
||||||
|
|
||||||
// We travelled through the parents, so if we are at the body, we clicked outside of the settings panel
|
|
||||||
if (settingCheckElm.tagName === 'BODY') {
|
|
||||||
// This will close the settings panel only when something outside of it is clicked
|
|
||||||
if (settingsPanel.style.display !== 'none') {
|
|
||||||
settingsPanel.style.display = 'none'
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// Ensure we do the translation at the very end, after everything else has loaded
|
|
||||||
await doTranslation()
|
|
||||||
|
|
||||||
if (!config.genshinImpactFolder) {
|
|
||||||
handleGenshinFolderNotSet()
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!config.serverFolder) {
|
|
||||||
handleServerNotSet()
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get the list of favorite IPs
|
|
||||||
*
|
|
||||||
* @returns {Promise<string[]>}
|
|
||||||
*/
|
|
||||||
async function getFavIps() {
|
|
||||||
const ipStr = await Neutralino.storage.getData('favorites').catch(e => {
|
|
||||||
// The data isn't set, so this is our first time opening
|
|
||||||
Neutralino.storage.setData('favorites', JSON.stringify([]))
|
|
||||||
})
|
|
||||||
|
|
||||||
const ipArr = ipStr ? JSON.parse(ipStr) : []
|
|
||||||
|
|
||||||
return ipArr
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
const openInExplorer = async (path) => {
|
||||||
* Get configuration
|
createCmdWindow(`explorer.exe "${path}"`)
|
||||||
*
|
|
||||||
* @returns {Promise<string>}
|
|
||||||
*/
|
|
||||||
async function getCfg() {
|
|
||||||
const defaultConf = {
|
|
||||||
genshinImpactFolder: '',
|
|
||||||
serverFolder: '',
|
|
||||||
lastConnect: '',
|
|
||||||
enableKillswitch: false,
|
|
||||||
serverLaunchPanel: false,
|
|
||||||
language: 'en'
|
|
||||||
}
|
|
||||||
const cfgStr = await Neutralino.storage.getData('config').catch(e => {
|
|
||||||
// The data isn't set, so this is our first time opening
|
|
||||||
Neutralino.storage.setData('config', JSON.stringify(defaultConf))
|
|
||||||
|
|
||||||
// Show the first time notice if there is no config
|
|
||||||
document.querySelector('#firstTimeNotice').style.display = 'block'
|
|
||||||
})
|
|
||||||
|
|
||||||
const config = cfgStr ? JSON.parse(cfgStr) : defaultConf
|
|
||||||
|
|
||||||
return config
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -144,7 +32,7 @@ async function enableButtons() {
|
|||||||
/**
|
/**
|
||||||
* Enable server launch button
|
* Enable server launch button
|
||||||
*/
|
*/
|
||||||
async function enableServerButton() {
|
async function enableServerButton() {
|
||||||
const serverBtn = document.querySelector('#serverLaunch')
|
const serverBtn = document.querySelector('#serverLaunch')
|
||||||
|
|
||||||
serverBtn.classList.remove('disabled')
|
serverBtn.classList.remove('disabled')
|
||||||
@ -154,12 +42,12 @@ async function enableButtons() {
|
|||||||
/**
|
/**
|
||||||
* Disable buttons when the game folder is not set
|
* Disable buttons when the game folder is not set
|
||||||
*/
|
*/
|
||||||
async function handleGenshinFolderNotSet() {
|
async function handleGameNotSet() {
|
||||||
// Set buttons to greyed out and disable
|
// Set buttons to greyed out and disable
|
||||||
document.querySelector('#genshinPath').innerHTML = localeObj.folderNotSet
|
document.querySelector('#gamePath').innerHTML = localeObj.folderNotSet
|
||||||
|
|
||||||
// Set official server background to default
|
// Set official server background to default
|
||||||
document.querySelector('#firstPanel').style.backgroundImage = `url("../bg/private/default.png")`
|
document.querySelector('#firstPanel').style.backgroundImage = 'url("../bg/private/default.png")'
|
||||||
|
|
||||||
const offBtn = document.querySelector('#playOfficial')
|
const offBtn = document.querySelector('#playOfficial')
|
||||||
const privBtn = document.querySelector('#playPrivate')
|
const privBtn = document.querySelector('#playPrivate')
|
||||||
@ -186,35 +74,20 @@ async function handleServerNotSet() {
|
|||||||
privBtn.disabled = true
|
privBtn.disabled = true
|
||||||
}
|
}
|
||||||
|
|
||||||
async function proxyIsInstalled() {
|
|
||||||
// Check if the proxy server is installed
|
|
||||||
const curDirList = await filesystem.readDirectory(NL_CWD)
|
|
||||||
|
|
||||||
if (curDirList.find(f => f.entry === 'ext')) {
|
|
||||||
const extFiles = await filesystem.readDirectory(NL_CWD + '/ext')
|
|
||||||
|
|
||||||
if (extFiles.find(f => f.entry === 'mitmdump.exe')) {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Show the game folder under the select button
|
* Show the game folder under the select button
|
||||||
*/
|
*/
|
||||||
async function displayGenshinFolder() {
|
async function displayGameFolder() {
|
||||||
const elm = document.querySelector('#genshinPath')
|
const elm = document.querySelector('#gamePath')
|
||||||
const config = await getCfg()
|
const config = await getCfg()
|
||||||
|
|
||||||
elm.innerHTML = config.genshinImpactFolder
|
elm.innerHTML = config.gameexe
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Show the server folder under the select button
|
* Show the server folder under the select button
|
||||||
*/
|
*/
|
||||||
async function displayServerFolder() {
|
async function displayServerFolder() {
|
||||||
const elm = document.querySelector('#serverPath')
|
const elm = document.querySelector('#serverPath')
|
||||||
const config = await getCfg()
|
const config = await getCfg()
|
||||||
|
|
||||||
@ -234,7 +107,7 @@ async function setBackgroundImage() {
|
|||||||
const servImage = servImages[Math.floor(Math.random() * servImages.length)].entry
|
const servImage = servImages[Math.floor(Math.random() * servImages.length)].entry
|
||||||
|
|
||||||
// Set default image, it will change if the bg folder exists
|
// Set default image, it will change if the bg folder exists
|
||||||
document.querySelector('#firstPanel').style.backgroundImage = `url("https://webstatic.hoyoverse.com/upload/event/2020/11/04/7fd661b5184e1734f91f628b6f89a31f_7367318474207189623.png")`
|
document.querySelector('#firstPanel').style.backgroundImage = 'url("https://webstatic.hoyoverse.com/upload/event/2020/11/04/7fd661b5184e1734f91f628b6f89a31f_7367318474207189623.png")'
|
||||||
|
|
||||||
// Set the private background image
|
// Set the private background image
|
||||||
document.querySelector('#secondPanel').style.backgroundImage = `url("../bg/private/${privImage}")`
|
document.querySelector('#secondPanel').style.backgroundImage = `url("../bg/private/${privImage}")`
|
||||||
@ -262,17 +135,17 @@ async function setBackgroundImage() {
|
|||||||
await filesystem.createDirectory(NL_CWD + '/resources/bg/official')
|
await filesystem.createDirectory(NL_CWD + '/resources/bg/official')
|
||||||
}
|
}
|
||||||
|
|
||||||
if (config.genshinImpactFolder) {
|
if (config.gameexe) {
|
||||||
// See if bg folder exists in parent dir
|
// See if bg folder exists in parent dir
|
||||||
const parentDir = await filesystem.readDirectory(config.genshinImpactFolder + '/..')
|
const parentDir = await filesystem.readDirectory(config.gameexe + '/..')
|
||||||
|
|
||||||
if (parentDir.find(dir => dir.entry === 'bg')) {
|
if (parentDir.find(dir => dir.entry === 'bg')) {
|
||||||
|
|
||||||
const officialImages = (await filesystem.readDirectory(config.genshinImpactFolder + '/../bg')).filter(file => file.type === 'FILE')
|
const officialImages = (await filesystem.readDirectory(config.gameexe + '/../bg')).filter(file => file.type === 'FILE')
|
||||||
|
|
||||||
if (officialImages.length > 0) {
|
if (officialImages.length > 0) {
|
||||||
for (const bg of officialImages) {
|
for (const bg of officialImages) {
|
||||||
const path = config.genshinImpactFolder.replace('\\', '/') + '/../bg/' + bg.entry
|
const path = config.gameexe.replace('\\', '/') + '/../bg/' + bg.entry
|
||||||
|
|
||||||
// See if the file exists already
|
// See if the file exists already
|
||||||
const currentBgs = (await filesystem.readDirectory(NL_CWD + '/resources/bg/official/')).filter(file => file.type === 'FILE')
|
const currentBgs = (await filesystem.readDirectory(NL_CWD + '/resources/bg/official/')).filter(file => file.type === 'FILE')
|
||||||
@ -300,9 +173,12 @@ async function setBackgroundImage() {
|
|||||||
*/
|
*/
|
||||||
async function handleFavoriteInput() {
|
async function handleFavoriteInput() {
|
||||||
const ip = document.querySelector('#ip').value
|
const ip = document.querySelector('#ip').value
|
||||||
|
const port = document.querySelector('#port').value || '443'
|
||||||
const ipArr = await getFavIps()
|
const ipArr = await getFavIps()
|
||||||
|
|
||||||
if (!ip || !ipArr.includes(ip)) {
|
const addr = `${ip}:${port}`
|
||||||
|
|
||||||
|
if (!ip || !ipArr.includes(addr)) {
|
||||||
document.querySelector('#star').src = 'icons/star_empty.svg'
|
document.querySelector('#star').src = 'icons/star_empty.svg'
|
||||||
} else {
|
} else {
|
||||||
document.querySelector('#star').src = 'icons/star_filled.svg'
|
document.querySelector('#star').src = 'icons/star_filled.svg'
|
||||||
@ -316,13 +192,18 @@ async function handleFavoriteInput() {
|
|||||||
*/
|
*/
|
||||||
async function setIp(ip) {
|
async function setIp(ip) {
|
||||||
const ipInput = document.querySelector('#ip')
|
const ipInput = document.querySelector('#ip')
|
||||||
|
const portInput = document.querySelector('#port')
|
||||||
|
|
||||||
|
const parseIp = ip.split(':')[0]
|
||||||
|
const parsePort = ip.split(':')[1]
|
||||||
|
|
||||||
// Set star
|
// Set star
|
||||||
if (ip) {
|
if (ip) {
|
||||||
document.querySelector('#star').src = 'icons/star_filled.svg'
|
document.querySelector('#star').src = 'icons/star_filled.svg'
|
||||||
}
|
}
|
||||||
|
|
||||||
ipInput.value = ip
|
ipInput.value = parseIp
|
||||||
|
portInput.value = parsePort
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -358,41 +239,36 @@ async function handleFavoriteList() {
|
|||||||
|
|
||||||
const transform = window.getComputedStyle(document.querySelector('#ipList')).transform
|
const transform = window.getComputedStyle(document.querySelector('#ipList')).transform
|
||||||
const xy = [ transform.split(',')[4], transform.split(',')[5] ]
|
const xy = [ transform.split(',')[4], transform.split(',')[5] ]
|
||||||
let newY = parseInt(xy[1].replace(')', '')) - (27 * ipArr.length)
|
let newY = (27 * ipArr.length) * window.devicePixelRatio
|
||||||
|
|
||||||
if (ipArr.length === 0) newY -= 27
|
if (ipArr.length === 0 || ipArr.length === 1) newY = 0
|
||||||
|
|
||||||
ipList.style.transform = `translate(${xy[0]}px, ${newY}px)`
|
ipList.style.transform = `translate(${xy[0]}px, calc(56vh - ${newY}px)`
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
async function openDownloads() {
|
||||||
* Add the current value of the IP input to the favorites list
|
const downloads = document.querySelector('#downloadPanel')
|
||||||
* OR
|
const config = await getCfg()
|
||||||
* Remove the current value of the IP input from the favorites list
|
|
||||||
*/
|
|
||||||
async function setFavorite() {
|
|
||||||
const ip = document.querySelector('#ip').value
|
|
||||||
const ipArr = await getFavIps()
|
|
||||||
|
|
||||||
// Set star icon
|
if (downloads.style.display === 'none') {
|
||||||
const star = document.querySelector('#star')
|
downloads.style.removeProperty('display')
|
||||||
|
|
||||||
if (star.src.includes('filled') && ip) {
|
|
||||||
star.src = 'icons/star_empty.svg'
|
|
||||||
|
|
||||||
// remove from list
|
|
||||||
ipArr.splice(ipArr.indexOf(ip), 1)
|
|
||||||
} else {
|
|
||||||
star.src = 'icons/star_filled.svg'
|
|
||||||
|
|
||||||
// add to list
|
|
||||||
if (ip && !ipArr.includes(ip)) {
|
|
||||||
ipArr.push(ip)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Neutralino.storage.setData('favorites', JSON.stringify(ipArr))
|
// Disable the resource download button if a serverFolder path is not set
|
||||||
|
if (!config.serverFolder) {
|
||||||
|
document.querySelector('#resourceInstall').disabled = true
|
||||||
|
document.querySelector('#resourceInstall').classList.add('disabled')
|
||||||
|
} else {
|
||||||
|
document.querySelector('#resourceInstall').disabled = false
|
||||||
|
document.querySelector('#resourceInstall').classList.remove('disabled')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function closeDownloads() {
|
||||||
|
const downloads = document.querySelector('#downloadPanel')
|
||||||
|
|
||||||
|
downloads.style.display = 'none'
|
||||||
}
|
}
|
||||||
|
|
||||||
async function openSettings() {
|
async function openSettings() {
|
||||||
@ -406,9 +282,11 @@ async function openSettings() {
|
|||||||
// Fill setting options with what is currently set in config
|
// Fill setting options with what is currently set in config
|
||||||
const killSwitch = document.querySelector('#killswitchOption')
|
const killSwitch = document.querySelector('#killswitchOption')
|
||||||
const serverLaunch = document.querySelector('#serverLaunchOption')
|
const serverLaunch = document.querySelector('#serverLaunchOption')
|
||||||
|
const httpsCheckbox = document.querySelector('#httpsOption')
|
||||||
|
|
||||||
killSwitch.checked = config.enableKillswitch
|
killSwitch.checked = config.enableKillswitch
|
||||||
serverLaunch.checked = config.serverLaunchPanel
|
serverLaunch.checked = config.serverLaunchPanel
|
||||||
|
httpsCheckbox.checked = config.useHttps
|
||||||
|
|
||||||
// Load languages
|
// Load languages
|
||||||
getLanguages()
|
getLanguages()
|
||||||
@ -424,7 +302,7 @@ async function closeSettings() {
|
|||||||
settings.style.display = 'none'
|
settings.style.display = 'none'
|
||||||
|
|
||||||
// In case we installed the proxy server
|
// In case we installed the proxy server
|
||||||
if (await proxyIsInstalled() && config.genshinImpactFolder) {
|
if (await proxyIsInstalled() && config.gameexe) {
|
||||||
const playPriv = document.querySelector('#playPrivate')
|
const playPriv = document.querySelector('#playPrivate')
|
||||||
|
|
||||||
playPriv.classList.remove('disabled')
|
playPriv.classList.remove('disabled')
|
||||||
@ -432,13 +310,44 @@ async function closeSettings() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async function toggleKillSwitch() {
|
async function openLogin() {
|
||||||
const killSwitch = document.querySelector('#killswitchOption')
|
const login = document.querySelector('#loginPanel')
|
||||||
|
const ip = document.querySelector('#ip').value
|
||||||
|
const port = document.querySelector('#port').value
|
||||||
|
const loginIpDisplay = document.querySelector('#loginPopupServer')
|
||||||
|
const registerIpDisplay = document.querySelector('#registerPopupServer')
|
||||||
|
|
||||||
const config = await getCfg()
|
const config = await getCfg()
|
||||||
|
const useHttps = config.useHttps
|
||||||
|
const url = `${useHttps ? 'https' : 'http'}://${ip}:${port}`
|
||||||
|
|
||||||
config.enableKillswitch = killSwitch.checked
|
// Check if we even need to authenticate
|
||||||
|
try {
|
||||||
|
const { data } = await axios.get(url + '/authentication/type')
|
||||||
|
|
||||||
Neutralino.storage.setData('config', JSON.stringify(config))
|
if (!data.includes('GCAuthAuthenticationHandler')) {
|
||||||
|
launchPrivate()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
} catch(e) {
|
||||||
|
launchPrivate()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
loginIpDisplay.innerText = ip
|
||||||
|
registerIpDisplay.innerText = ip
|
||||||
|
|
||||||
|
if (login.style.display === 'none') {
|
||||||
|
login.style.removeProperty('display')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function closeLogin() {
|
||||||
|
const login = document.querySelector('#loginPanel')
|
||||||
|
|
||||||
|
login.style.display = 'none'
|
||||||
|
|
||||||
|
setLoginSection()
|
||||||
}
|
}
|
||||||
|
|
||||||
async function closeFirstTimePopup() {
|
async function closeFirstTimePopup() {
|
||||||
@ -447,7 +356,15 @@ async function closeFirstTimePopup() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async function runInstallScript() {
|
async function runInstallScript() {
|
||||||
Neutralino.os.execCommand(`${NL_CWD}/scripts/install.cmd "${NL_CWD}"`)
|
createCmdWindow(`.\\scripts\\install.cmd "${NL_CWD}" true`)
|
||||||
|
|
||||||
|
// Create an interval that will check for the proxy server installation finish
|
||||||
|
const interval = setInterval(async () => {
|
||||||
|
if (await proxyIsInstalled()) {
|
||||||
|
clearInterval(interval)
|
||||||
|
enableButtons()
|
||||||
|
}
|
||||||
|
}, 1000)
|
||||||
|
|
||||||
closeFirstTimePopup()
|
closeFirstTimePopup()
|
||||||
}
|
}
|
||||||
@ -464,10 +381,10 @@ async function checkForUpdatesAndShow() {
|
|||||||
|
|
||||||
// Version mismatch? Update!
|
// Version mismatch? Update!
|
||||||
if (manifest?.version !== NL_APPVERSION) {
|
if (manifest?.version !== NL_APPVERSION) {
|
||||||
subtitle.innerHTML = "New update available!"
|
subtitle.innerHTML = 'New update available!'
|
||||||
updateBtn.classList.remove('disabled')
|
updateBtn.classList.remove('disabled')
|
||||||
} else {
|
} else {
|
||||||
subtitle.innerHTML = "You are on the latest version! :)"
|
subtitle.innerHTML = 'You are on the latest version! :)'
|
||||||
updateBtn.classList.add('disabled')
|
updateBtn.classList.add('disabled')
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -485,157 +402,87 @@ async function displayServerLaunchSection() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async function toggleServerLaunchSection() {
|
|
||||||
const config = await getCfg()
|
|
||||||
|
|
||||||
displayServerLaunchSection()
|
|
||||||
|
|
||||||
// Save setting
|
|
||||||
config.serverLaunchPanel = !config.serverLaunchPanel
|
|
||||||
Neutralino.storage.setData('config', JSON.stringify(config))
|
|
||||||
}
|
|
||||||
|
|
||||||
async function getLanguages() {
|
|
||||||
const languageFiles = (await filesystem.readDirectory(`${NL_CWD}/languages`)).filter(file => file.entry.endsWith('.json'))
|
|
||||||
const config = await getCfg()
|
|
||||||
|
|
||||||
// Load all languages as options
|
|
||||||
for (const file of languageFiles) {
|
|
||||||
const fullLanguageName = JSON.parse(await filesystem.readFile(`${NL_CWD}/languages/${file.entry}`)).fullLangName
|
|
||||||
const lang = file.entry.split('.json')[0]
|
|
||||||
|
|
||||||
const option = document.createElement('option')
|
|
||||||
option.value = lang
|
|
||||||
option.innerHTML = fullLanguageName
|
|
||||||
|
|
||||||
// Set language selected to config language
|
|
||||||
if (lang === config.language) {
|
|
||||||
option.selected = true
|
|
||||||
}
|
|
||||||
|
|
||||||
document.querySelector('#languageSelect').appendChild(option)
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
async function handleLanguageChange(elm) {
|
|
||||||
const list = elm
|
|
||||||
const config = await getCfg()
|
|
||||||
|
|
||||||
// Set language in config
|
|
||||||
config.language = list.value
|
|
||||||
Neutralino.storage.setData('config', JSON.stringify(config))
|
|
||||||
|
|
||||||
// Force refresh of application, no need for restart!
|
|
||||||
window.location.reload()
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Set the game folder by opening a folder picker
|
* Set the game folder by opening a folder picker
|
||||||
*/
|
*/
|
||||||
async function setGenshinImpactFolder() {
|
async function setGameExe() {
|
||||||
const folder = await Neutralino.os.showFolderDialog(localeObj.genshinFolderDialog)
|
const gameExe = await Neutralino.os.showOpenDialog(localeObj.gameFolderDialog, {
|
||||||
|
filters: [
|
||||||
|
{ name: 'Executable files', extensions: ['exe'] }
|
||||||
|
]
|
||||||
|
})
|
||||||
|
|
||||||
|
if (!gameExe[0]) return
|
||||||
|
if (hasChineseChars(gameExe[0])) displayAlert(localeObj.chineseCharacterAlert)
|
||||||
|
|
||||||
// Set the folder in our configuration
|
// Set the folder in our configuration
|
||||||
const config = await getCfg()
|
const config = await getCfg()
|
||||||
|
|
||||||
// See if the actual game folder is inside this one
|
// It's an array of selections, so only get the first one
|
||||||
const folderList = await filesystem.readDirectory(folder)
|
config.gameexe = gameExe[0].replace(/\//g, '\\')
|
||||||
const gameFolder = folderList.filter(file => file.entry.includes('Genshin Impact Game'))
|
|
||||||
|
|
||||||
if (gameFolder.length > 0) {
|
|
||||||
config.genshinImpactFolder = folder + '\\Genshin Impact Game'
|
|
||||||
Neutralino.storage.setData('config', JSON.stringify(config))
|
|
||||||
} else {
|
|
||||||
config.genshinImpactFolder = folder
|
|
||||||
}
|
|
||||||
|
|
||||||
Neutralino.storage.setData('config', JSON.stringify(config))
|
Neutralino.storage.setData('config', JSON.stringify(config))
|
||||||
|
|
||||||
// Refresh background and path
|
// Refresh background and path
|
||||||
setBackgroundImage()
|
setBackgroundImage()
|
||||||
displayGenshinFolder()
|
displayGameFolder()
|
||||||
enableButtons()
|
enableButtons()
|
||||||
}
|
}
|
||||||
|
|
||||||
async function setGrassCutterFolder() {
|
async function setGrasscutterFolder() {
|
||||||
const folder = await Neutralino.os.showOpenDialog(localeObj.grasscutterFileDialog, {
|
const folder = await Neutralino.os.showOpenDialog(localeObj.grasscutterFileDialog, {
|
||||||
filters: [
|
filters: [
|
||||||
{ name: 'Jar files', extensions: ['jar'] }
|
{ name: 'Jar files', extensions: ['jar'] }
|
||||||
]
|
]
|
||||||
})
|
})
|
||||||
|
|
||||||
|
if (!folder[0]) return
|
||||||
|
if (hasChineseChars(folder[0])) displayAlert(localeObj.chineseCharacterAlert)
|
||||||
|
|
||||||
// Set the folder in our configuration
|
// Set the folder in our configuration
|
||||||
const config = await getCfg()
|
const config = await getCfg()
|
||||||
|
|
||||||
config.serverFolder = folder
|
config.serverFolder = folder[0]
|
||||||
Neutralino.storage.setData('config', JSON.stringify(config))
|
Neutralino.storage.setData('config', JSON.stringify(config))
|
||||||
|
|
||||||
displayServerFolder()
|
displayServerFolder()
|
||||||
enableServerButton()
|
enableServerButton()
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Get the name of the game executable
|
|
||||||
*
|
|
||||||
* @returns {Promise<String>}
|
|
||||||
*/
|
|
||||||
async function getGenshinExecName() {
|
|
||||||
// Scan genshin dir
|
|
||||||
const config = await getCfg()
|
|
||||||
const genshinDir = await filesystem.readDirectory(config.genshinImpactFolder)
|
|
||||||
|
|
||||||
// Find the executable
|
|
||||||
const genshinExec = genshinDir.find(file => file.entry.endsWith('.exe'))
|
|
||||||
|
|
||||||
return genshinExec.entry
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Launch the game with no modifications nor proxy
|
* Launch the game with no modifications nor proxy
|
||||||
*/
|
*/
|
||||||
async function launchOfficial() {
|
async function launchOfficial() {
|
||||||
const config = await getCfg()
|
const config = await getCfg()
|
||||||
|
|
||||||
Neutralino.os.execCommand(config.genshinImpactFolder + '/' + await getGenshinExecName())
|
Neutralino.os.execCommand(`"${config.gameexe}"`)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Launch the game with a proxy
|
* Launch the game with a proxy
|
||||||
*/
|
*/
|
||||||
async function launchPrivate() {
|
async function launchPrivate() {
|
||||||
const ip = document.getElementById('ip').value || 'localhost'
|
const ip = document.getElementById('ip').value || '127.0.0.1'
|
||||||
|
const port = document.getElementById('port').value || '443'
|
||||||
|
|
||||||
const config = await getCfg()
|
const config = await getCfg()
|
||||||
|
|
||||||
console.log('connecting to ' + ip)
|
console.log('connecting to ' + ip + ':' + port)
|
||||||
|
|
||||||
// Set the last connect
|
// Set the last connect
|
||||||
config.lastConnect = ip
|
config.lastConnect = ip
|
||||||
Neutralino.storage.setData('config', JSON.stringify(config))
|
Neutralino.storage.setData('config', JSON.stringify(config))
|
||||||
|
|
||||||
// Pass IP and game folder to the private server launcher
|
// Pass IP and game folder to the private server launcher
|
||||||
Neutralino.os.execCommand(`${NL_CWD}/scripts/private_server_launch.cmd ${ip} "${config.genshinImpactFolder}/${await getGenshinExecName()}" "${NL_CWD}" ${config.enableKillswitch}`).catch(e => console.log(e))
|
Neutralino.os.execCommand(
|
||||||
|
`.\\scripts\\private_server_launch.cmd ${ip} ${port} ${config.useHttps} "${config.gameexe}" "${NL_CWD}" ${config.enableKillswitch} true`, {
|
||||||
|
background: true
|
||||||
|
}
|
||||||
|
).catch(e => console.log(e))
|
||||||
}
|
}
|
||||||
|
|
||||||
async function launchLocalServer() {
|
async function launchLocalServer() {
|
||||||
const config = await getCfg()
|
const config = await getCfg()
|
||||||
|
|
||||||
Neutralino.os.execCommand(`${NL_CWD}/scripts/local_server_launch.cmd "${config.serverFolder}"`).catch(e => console.log(e))
|
createCmdWindow(`.\\scripts\\local_server_launch.cmd "${config.serverFolder}"`).catch(e => console.log(e))
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Minimize the window
|
|
||||||
*/
|
|
||||||
function minimizeWin() {
|
|
||||||
console.log('min')
|
|
||||||
Neutralino.window.minimize()
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Close the window
|
|
||||||
*/
|
|
||||||
function closeWin() {
|
|
||||||
console.log('close')
|
|
||||||
Neutralino.app.exit()
|
|
||||||
}
|
|
154
resources/js/login.js
Normal file
154
resources/js/login.js
Normal file
@ -0,0 +1,154 @@
|
|||||||
|
/**
|
||||||
|
* Toggle the login section
|
||||||
|
*/
|
||||||
|
async function setLoginSection() {
|
||||||
|
const title = document.getElementById('loginSectionTitle')
|
||||||
|
const altTitle = document.getElementById('registerSectionTitle')
|
||||||
|
const loginSection = document.getElementById('loginPopupContentBody')
|
||||||
|
const registerSection = document.getElementById('registerPopupContentBody')
|
||||||
|
|
||||||
|
title.classList.add('selectedTitle')
|
||||||
|
altTitle.classList.remove('selectedTitle')
|
||||||
|
|
||||||
|
loginSection.style.removeProperty('display')
|
||||||
|
registerSection.style.display = 'none'
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Toggle the register section
|
||||||
|
*/
|
||||||
|
async function setRegisterSection(fromLogin = false) {
|
||||||
|
const title = document.getElementById('registerSectionTitle')
|
||||||
|
const altTitle = document.getElementById('loginSectionTitle')
|
||||||
|
const loginSection = document.getElementById('loginPopupContentBody')
|
||||||
|
const registerSection = document.getElementById('registerPopupContentBody')
|
||||||
|
|
||||||
|
title.classList.add('selectedTitle')
|
||||||
|
altTitle.classList.remove('selectedTitle')
|
||||||
|
|
||||||
|
loginSection.style.display = 'none'
|
||||||
|
registerSection.style.removeProperty('display')
|
||||||
|
|
||||||
|
if (fromLogin) {
|
||||||
|
// Take the values from the login section and put them in the register section
|
||||||
|
const loginUsername = document.getElementById('loginUsername').value
|
||||||
|
const loginPassword = document.getElementById('loginPassword').value
|
||||||
|
|
||||||
|
document.getElementById('registerUsername').value = loginUsername
|
||||||
|
document.getElementById('registerPassword').value = loginPassword
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function parseJwt(token) {
|
||||||
|
const base64Url = token.split('.')[1]
|
||||||
|
const base64 = base64Url.replace('-', '+').replace('_', '/')
|
||||||
|
return JSON.parse(window.atob(base64))
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Attempt login and launch game
|
||||||
|
*/
|
||||||
|
async function login() {
|
||||||
|
const username = document.getElementById('loginUsername').value
|
||||||
|
const password = document.getElementById('loginPassword').value
|
||||||
|
const ip = document.getElementById('ip').value
|
||||||
|
const port = document.getElementById('port').value || '443'
|
||||||
|
const config = await getCfg()
|
||||||
|
const useHttps = config.useHttps
|
||||||
|
const url = `${useHttps ? 'https' : 'http'}://${ip}:${port}`
|
||||||
|
|
||||||
|
const reqBody = {
|
||||||
|
username,
|
||||||
|
password,
|
||||||
|
}
|
||||||
|
|
||||||
|
const { data } = await axios.post(url + '/authentication/login', reqBody)
|
||||||
|
|
||||||
|
switch(data.message) {
|
||||||
|
case 'INVALID_ACCOUNT':
|
||||||
|
displayLoginAlert(localeObj.alertInvalid || 'Invalid username or password', 'error')
|
||||||
|
break
|
||||||
|
|
||||||
|
case 'NO_PASSWORD':
|
||||||
|
// No account password, create one with change password
|
||||||
|
displayLoginAlert(localeObj.alertNoPass || 'No password set, please change password', 'warn')
|
||||||
|
break
|
||||||
|
|
||||||
|
case 'UNKNOWN':
|
||||||
|
// Unknown error, contact server owner
|
||||||
|
displayLoginAlert(localeObj.alertUnknown || 'Unknown error, contact server owner', 'error')
|
||||||
|
break
|
||||||
|
|
||||||
|
case undefined:
|
||||||
|
case null:
|
||||||
|
case 'AUTH_DISABLED':
|
||||||
|
// Authentication is disabled, we can just connect the user
|
||||||
|
displayLoginAlert(localeObj.alertAuthNoLogin || 'Authentication is disabled, no need to log in!', 'warn')
|
||||||
|
launchPrivate()
|
||||||
|
break
|
||||||
|
|
||||||
|
default:
|
||||||
|
// Success! Copy the JWT token to their clipboard
|
||||||
|
const tkData = parseJwt(data.jwt)
|
||||||
|
await Neutralino.clipboard.writeText(tkData.token)
|
||||||
|
|
||||||
|
displayLoginAlert(localeObj.alertLoginSuccess || 'Login successful! Token copied to clipboard. Paste this token into the username field of the game to log in.', 'success', 8000)
|
||||||
|
launchPrivate()
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Attempt registration, do not launch game
|
||||||
|
*/
|
||||||
|
async function register() {
|
||||||
|
const username = document.getElementById('registerUsername').value
|
||||||
|
const password = document.getElementById('registerPassword').value
|
||||||
|
const password_confirmation = document.getElementById('registerPasswordConfirm').value
|
||||||
|
const ip = document.getElementById('ip').value
|
||||||
|
const port = document.getElementById('port').value || '443'
|
||||||
|
const config = await getCfg()
|
||||||
|
const useHttps = config.useHttps
|
||||||
|
const url = `${useHttps ? 'https' : 'http'}://${ip}:${port}`
|
||||||
|
|
||||||
|
const reqBody = {
|
||||||
|
username,
|
||||||
|
password,
|
||||||
|
password_confirmation
|
||||||
|
}
|
||||||
|
|
||||||
|
const { data } = await axios.post(url + '/authentication/register', reqBody)
|
||||||
|
|
||||||
|
switch(data.message) {
|
||||||
|
case 'USERNAME_TAKEN':
|
||||||
|
// Username is taken
|
||||||
|
displayRegisterAlert(localeObj.alertUserTaken || 'Username is taken', 'error')
|
||||||
|
break
|
||||||
|
|
||||||
|
case 'PASSWORD_MISMATCH':
|
||||||
|
// The password and password confirmation do not match
|
||||||
|
displayRegisterAlert(localStorage.alertPassMismatch || 'Password and password confirmation do not match', 'error')
|
||||||
|
break
|
||||||
|
|
||||||
|
case 'UNKNOWN':
|
||||||
|
// Unknown error, contact server owner
|
||||||
|
displayRegisterAlert(localeObj.alertUnknown || 'Unknown error, contact server owner', 'error')
|
||||||
|
break
|
||||||
|
|
||||||
|
case undefined:
|
||||||
|
case null:
|
||||||
|
case 'AUTH_DISABLED':
|
||||||
|
// Authentication is disabled, we can just connect the user
|
||||||
|
displayRegisterAlert(localeObj.alertAuthNoRegister || 'Authentication is disabled, no need to register!', 'warn')
|
||||||
|
break
|
||||||
|
|
||||||
|
default:
|
||||||
|
// Success!! Bring them to the login screen and auto-input their username
|
||||||
|
const loginUsername = document.getElementById('loginUsername')
|
||||||
|
loginUsername.value = username
|
||||||
|
|
||||||
|
setLoginSection()
|
||||||
|
displayLoginAlert(localeObj.alertRegisterSuccess || 'Registration successful!', 'success', 5000)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
89
resources/js/onLoad.js
Normal file
89
resources/js/onLoad.js
Normal file
@ -0,0 +1,89 @@
|
|||||||
|
|
||||||
|
/**
|
||||||
|
* Every autofill, such as backgrounds and the game folder,
|
||||||
|
* should be done here to ensure DOM contents are loaded.
|
||||||
|
*/
|
||||||
|
document.addEventListener('DOMContentLoaded', async () => {
|
||||||
|
displayUpdate()
|
||||||
|
setBackgroundImage()
|
||||||
|
displayGameFolder()
|
||||||
|
displayServerFolder()
|
||||||
|
|
||||||
|
// Set title version
|
||||||
|
document.querySelector('#version').innerHTML = NL_APPVERSION
|
||||||
|
|
||||||
|
const config = await getCfg()
|
||||||
|
const ipArr = await getFavIps()
|
||||||
|
|
||||||
|
if (config.serverLaunchPanel) {
|
||||||
|
displayServerLaunchSection()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set last connect
|
||||||
|
document.querySelector('#ip').value = config.lastConnect
|
||||||
|
|
||||||
|
if (ipArr.includes(config.lastConnect)) {
|
||||||
|
document.querySelector('#star').src = 'icons/star_filled.svg'
|
||||||
|
}
|
||||||
|
|
||||||
|
// Disable private game launch if proxy IP or proxy server is not found
|
||||||
|
const playPriv = document.querySelector('#playPrivate')
|
||||||
|
|
||||||
|
if (!(await proxyIsInstalled())) {
|
||||||
|
playPriv.classList.add('disabled')
|
||||||
|
playPriv.disabled = true
|
||||||
|
}
|
||||||
|
|
||||||
|
// Exit favorites list and settings panel when clicking outside of it
|
||||||
|
window.addEventListener('click', function(e) {
|
||||||
|
const favList = document.querySelector('#ipList')
|
||||||
|
const settingsPanel = document.querySelector('#settingsPanel')
|
||||||
|
const downloadPanel = document.querySelector('#downloadPanel')
|
||||||
|
|
||||||
|
// This will close the favorites list no matter what is clicked
|
||||||
|
if (favList.style.display !== 'none') {
|
||||||
|
favList.style.display = 'none'
|
||||||
|
favList.style.transform = ''
|
||||||
|
}
|
||||||
|
|
||||||
|
// This will close the settings panel no matter what is clicked
|
||||||
|
let checkElm = e.target
|
||||||
|
|
||||||
|
while(checkElm.tagName !== 'BODY') {
|
||||||
|
if (checkElm.id === 'settingsPanel'
|
||||||
|
|| checkElm.id === 'settingsBtn') {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if (checkElm.id === 'downloadPanel' ||
|
||||||
|
checkElm.id === 'downloadBtn') {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
checkElm = checkElm.parentElement
|
||||||
|
}
|
||||||
|
|
||||||
|
// We travelled through the parents, so if we are at the body, we clicked outside of the settings panel
|
||||||
|
if (checkElm.tagName === 'BODY') {
|
||||||
|
// This will close the settings panel only when something outside of it is clicked
|
||||||
|
if (settingsPanel.style.display !== 'none') {
|
||||||
|
settingsPanel.style.display = 'none'
|
||||||
|
}
|
||||||
|
|
||||||
|
if (downloadPanel.style.display !== 'none') {
|
||||||
|
downloadPanel.style.display = 'none'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
// Ensure we do the translation at the very end, after everything else has loaded
|
||||||
|
await doTranslation()
|
||||||
|
|
||||||
|
if (!config.gameexe) {
|
||||||
|
handleGameNotSet()
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!config.serverFolder) {
|
||||||
|
handleServerNotSet()
|
||||||
|
}
|
||||||
|
})
|
126
resources/js/options.js
Normal file
126
resources/js/options.js
Normal file
@ -0,0 +1,126 @@
|
|||||||
|
/**
|
||||||
|
* Toggle the killswitch script
|
||||||
|
*/
|
||||||
|
async function toggleKillSwitch() {
|
||||||
|
const killSwitch = document.querySelector('#killswitchOption')
|
||||||
|
const config = await getCfg()
|
||||||
|
|
||||||
|
config.enableKillswitch = killSwitch.checked
|
||||||
|
|
||||||
|
Neutralino.storage.setData('config', JSON.stringify(config))
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Toggles the server launching panel
|
||||||
|
*/
|
||||||
|
async function toggleServerLaunchSection() {
|
||||||
|
const config = await getCfg()
|
||||||
|
|
||||||
|
displayServerLaunchSection()
|
||||||
|
|
||||||
|
// Save setting
|
||||||
|
config.serverLaunchPanel = !config.serverLaunchPanel
|
||||||
|
Neutralino.storage.setData('config', JSON.stringify(config))
|
||||||
|
|
||||||
|
// Show a dialog for those who may want to open the downloads section
|
||||||
|
if (config.serverLaunchPanel && !config.serverFolder) {
|
||||||
|
closeSettings()
|
||||||
|
|
||||||
|
openDialog(
|
||||||
|
localeObj.serverEnableDialogTitle || 'You found the Grasscutter server launcher!',
|
||||||
|
localeObj.serverEnableDialogText || 'If you do not have an existing Grasscutter installation to set, would you like to download a build?',
|
||||||
|
true,
|
||||||
|
openDownloads
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get all languages for the language selector
|
||||||
|
*/
|
||||||
|
async function getLanguages() {
|
||||||
|
const languageFiles = (await filesystem.readDirectory(`${NL_CWD}/languages`)).filter(file => file.entry.endsWith('.json'))
|
||||||
|
const config = await getCfg()
|
||||||
|
|
||||||
|
// Clear language options
|
||||||
|
const languageSelect = document.querySelector('#languageSelect')
|
||||||
|
languageSelect.innerHTML = ''
|
||||||
|
|
||||||
|
// Load all languages as options
|
||||||
|
for (const file of languageFiles) {
|
||||||
|
const fullLanguageName = JSON.parse(await filesystem.readFile(`${NL_CWD}/languages/${file.entry}`)).fullLangName
|
||||||
|
const lang = file.entry.split('.json')[0]
|
||||||
|
|
||||||
|
const option = document.createElement('option')
|
||||||
|
option.value = lang
|
||||||
|
option.innerHTML = fullLanguageName
|
||||||
|
|
||||||
|
// Set language selected to config language
|
||||||
|
if (lang === config.language) {
|
||||||
|
option.selected = true
|
||||||
|
}
|
||||||
|
|
||||||
|
document.querySelector('#languageSelect').appendChild(option)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Save lang, refresh to apply
|
||||||
|
*
|
||||||
|
* @param {DOMElement} elm
|
||||||
|
*/
|
||||||
|
async function handleLanguageChange(elm) {
|
||||||
|
const list = elm
|
||||||
|
const config = await getCfg()
|
||||||
|
|
||||||
|
// Set language in config
|
||||||
|
config.language = list.value
|
||||||
|
Neutralino.storage.setData('config', JSON.stringify(config))
|
||||||
|
|
||||||
|
// Force refresh of application, no need for restart!
|
||||||
|
window.location.reload()
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Toggle the use of HTTPS
|
||||||
|
*/
|
||||||
|
async function toggleHttps() {
|
||||||
|
const httpsCheckbox = document.querySelector('#httpsOption')
|
||||||
|
const config = await getCfg()
|
||||||
|
|
||||||
|
config.useHttps = httpsCheckbox.checked
|
||||||
|
|
||||||
|
Neutralino.storage.setData('config', JSON.stringify(config))
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add the current value of the IP input to the favorites list
|
||||||
|
* OR
|
||||||
|
* Remove the current value of the IP input from the favorites list
|
||||||
|
*/
|
||||||
|
async function setFavorite() {
|
||||||
|
const ip = document.querySelector('#ip').value
|
||||||
|
const port = document.querySelector('#port').value || '443'
|
||||||
|
const ipArr = await getFavIps()
|
||||||
|
|
||||||
|
const addr = `${ip}:${port}`
|
||||||
|
|
||||||
|
// Set star icon
|
||||||
|
const star = document.querySelector('#star')
|
||||||
|
|
||||||
|
if (star.src.includes('filled') && ip) {
|
||||||
|
star.src = 'icons/star_empty.svg'
|
||||||
|
|
||||||
|
// remove from list
|
||||||
|
ipArr.splice(ipArr.indexOf(addr), 1)
|
||||||
|
} else {
|
||||||
|
star.src = 'icons/star_filled.svg'
|
||||||
|
|
||||||
|
// add to list
|
||||||
|
if (ip && !ipArr.includes(addr)) {
|
||||||
|
ipArr.push(addr)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Neutralino.storage.setData('favorites', JSON.stringify(ipArr))
|
||||||
|
}
|
@ -13,40 +13,51 @@ async function doTranslation() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const localization = await filesystem.readFile(`${NL_CWD}/languages/${config.language}.json`)
|
const localization = await filesystem.readFile(`${NL_CWD}/languages/${config.language}.json`)
|
||||||
|
const engLocale = await filesystem.readFile(`${NL_CWD}/languages/en.json`)
|
||||||
|
engLocaleObj = JSON.parse(engLocale)
|
||||||
localeObj = JSON.parse(localization)
|
localeObj = JSON.parse(localization)
|
||||||
|
|
||||||
const set = (id, localeString) => document.getElementById(id).innerHTML = localeString || 'UNKNOWN'
|
const set = (id, localeString) => document.getElementById(id).innerText = localeObj[localeString] || engLocaleObj[localeString]
|
||||||
|
|
||||||
// Begin filling in values
|
// Begin filling in values
|
||||||
set('titleSection', localeObj.appName)
|
set('titleSection', 'appName')
|
||||||
|
|
||||||
|
const verSpan = document.createElement('span')
|
||||||
|
verSpan.id = 'version'
|
||||||
|
verSpan.innerHTML = ` v${NL_APPVERSION}`
|
||||||
|
|
||||||
|
document.querySelector('#titleSection').appendChild(verSpan)
|
||||||
|
|
||||||
// Play buttons
|
// Play buttons
|
||||||
set('playOfficial', localeObj.playOfficial)
|
set('playOfficial', 'playOfficial')
|
||||||
set('playPrivate', localeObj.playPrivate)
|
set('playPrivate', 'playPrivate')
|
||||||
set('serverLaunch', localeObj.launchLocalServer)
|
set('serverLaunch', 'launchLocalServer')
|
||||||
|
|
||||||
// File select buttons
|
// File select buttons
|
||||||
set('genshinFolderSet', localeObj.genshinFolderSet)
|
set('gameExeSet', 'gameExeSet')
|
||||||
set('grasscutterFileSet', localeObj.grasscutterFileSet)
|
set('grasscutterFileSet', 'grasscutterFileSet')
|
||||||
|
|
||||||
// Private options
|
// Private options
|
||||||
set('ip', localeObj.ipPlaceholder)
|
document.querySelector('#ip').placeholder = localeObj.ipPlaceholder
|
||||||
|
document.querySelector('#port').placeholder = localeObj.portPlaceholder
|
||||||
|
|
||||||
// Settings
|
// Settings
|
||||||
set('fullSettingsTitle', localeObj.settingsTitle)
|
set('fullSettingsTitle', 'settingsTitle')
|
||||||
set('scriptsTitle', localeObj.scriptsSectionTitle)
|
set('scriptsTitle', 'scriptsSectionTitle')
|
||||||
set('killswitchTitle', localeObj.killswitchOption)
|
set('killswitchTitle', 'killswitchOption')
|
||||||
set('killswitchSubtitle', localeObj.killswitchSubtitle)
|
set('killswitchSubtitle', 'killswitchSubtitle')
|
||||||
set('proxyTitle', localeObj.proxyOption)
|
set('proxyTitle', 'proxyOption')
|
||||||
set('proxyInstall', localeObj.proxyInstallBtn)
|
set('proxyInstall', 'proxyInstallBtn')
|
||||||
set('proxySubtitle', localeObj.proxySubtitle)
|
set('proxySubtitle', 'proxySubtitle')
|
||||||
set('updateBtn', localeObj.updateOption)
|
set('updateBtn', 'updateOption')
|
||||||
set('updateTitle', localeObj.updateOption)
|
set('updateTitle', 'updateOption')
|
||||||
set('updateSubtitle', localeObj.updateSubtitle)
|
set('updateSubtitle', 'updateSubtitle')
|
||||||
set('languageTitle', localeObj.languageOption)
|
set('languageTitle', 'languageOption')
|
||||||
set('languageSubtitle', localeObj.languageSubtitle)
|
set('languageSubtitle', 'languageSubtitle')
|
||||||
set('serverLaunchTitle', localeObj.enableServerLauncherOption)
|
set('serverLaunchTitle', 'enableServerLauncherOption')
|
||||||
set('serverSubtitle', localeObj.enableServerLauncherSubtitle)
|
set('serverSubtitle', 'enableServerLauncherSubtitle')
|
||||||
|
set('httpsTitle', 'httpsOption')
|
||||||
|
set('httpsSubtitle', 'httpsSubtitle')
|
||||||
|
|
||||||
// Intro popup
|
// Intro popup
|
||||||
const popup = document.getElementById('firstTimeNotice')
|
const popup = document.getElementById('firstTimeNotice')
|
||||||
@ -62,6 +73,38 @@ async function doTranslation() {
|
|||||||
introSpan.innerHTML += localeObj.introSen3 + '<br>'
|
introSpan.innerHTML += localeObj.introSen3 + '<br>'
|
||||||
introSpan.innerHTML += localeObj.introSen4 + '<br>'
|
introSpan.innerHTML += localeObj.introSen4 + '<br>'
|
||||||
|
|
||||||
set('firstTimeInstallBtn', localeObj.proxyInstallBtn)
|
set('firstTimeInstallBtn', 'proxyInstallBtn')
|
||||||
set('firstTimeDenyBtn', localeObj.proxyInstallDeny)
|
set('firstTimeDenyBtn', 'proxyInstallDeny')
|
||||||
|
|
||||||
|
// Login section
|
||||||
|
set('loginSectionTitle', 'authLoginTitle')
|
||||||
|
set('registerSectionTitle', 'authRegisterTitle')
|
||||||
|
set('loggingInToIndicator', 'loggingInTo')
|
||||||
|
set('registeringToIndicator', 'registeringFor')
|
||||||
|
set('loginUsernameIndicator', 'authUsername')
|
||||||
|
set('loginPasswordIndicator', 'authPassword')
|
||||||
|
set('registerUsernameIndicator', 'authUsername')
|
||||||
|
set('registerPasswordIndicator', 'authPassword')
|
||||||
|
set('registerConfirmIndicator', 'authConfirmPassword')
|
||||||
|
set('loginPopupContentBodyBtnLogin', 'authLoginBtn')
|
||||||
|
set('loginPopupContentBodyBtnRegister', 'authRegisterBtn')
|
||||||
|
set('noLoginBtn', 'launchWithoutAuth')
|
||||||
|
|
||||||
|
// Downloads section
|
||||||
|
set('downloadTitle', 'downloadTitle')
|
||||||
|
set('grassclipperTitle', 'grassclipperTitle')
|
||||||
|
set('grasscutterTitle', 'grasscutterTitle')
|
||||||
|
set('installerTitle', 'installerTitle')
|
||||||
|
set('installerSubtitle', 'installerSubtitle')
|
||||||
|
set('downloadStable', 'downloadStable')
|
||||||
|
set('stableSubtitle', 'stableSubtitle')
|
||||||
|
set('downloadDev', 'downloadDev')
|
||||||
|
set('devSubtitle', 'devSubtitle')
|
||||||
|
set('downloadResources', 'downloadResources')
|
||||||
|
set('resourceSubtitle', 'resourceSubtitle')
|
||||||
|
set('stableInstall', 'stableInstall')
|
||||||
|
set('devInstall', 'devInstall')
|
||||||
|
|
||||||
|
// update notification
|
||||||
|
set('updateNotifText', 'updateNotifText')
|
||||||
}
|
}
|
@ -1,22 +1,25 @@
|
|||||||
// https://stackoverflow.com/questions/67971689/positioning-the-borderless-window-in-neutralino-js
|
// https://stackoverflow.com/questions/67971689/positioning-the-borderless-window-in-neutralino-js
|
||||||
// had to use this since the in-built function breaks the close and minimize buttons
|
// had to use this since the in-built function breaks the close and minimize buttons
|
||||||
let dragging = false, posX, posY;
|
let dragging = false, ratio = 1, posX, posY
|
||||||
let draggable;
|
let draggable
|
||||||
|
|
||||||
document.addEventListener('DOMContentLoaded', () => {
|
document.addEventListener('DOMContentLoaded', async () => {
|
||||||
draggable = document.getElementById('controlBar');
|
draggable = document.getElementById('controlBar')
|
||||||
|
|
||||||
// Listen to hovers
|
// Listen to hovers
|
||||||
draggable.onmousedown = function (e) {
|
draggable.onmousedown = function (e) {
|
||||||
posX = e.pageX, posY = e.pageY;
|
ratio = window.devicePixelRatio
|
||||||
dragging = true;
|
|
||||||
|
posX = e.pageX * ratio, posY = e.pageY * ratio
|
||||||
|
dragging = true
|
||||||
}
|
}
|
||||||
|
|
||||||
draggable.onmouseup = function (e) {
|
// Patch for monitors with scaling enabled, allows them to detach from the titlebar anywhere
|
||||||
dragging = false;
|
window.onmouseup = function (e) {
|
||||||
|
dragging = false
|
||||||
}
|
}
|
||||||
|
|
||||||
document.onmousemove = function (e) {
|
document.onmousemove = function (e) {
|
||||||
if (dragging) Neutralino.window.move(e.screenX - posX, e.screenY - posY);
|
if (dragging) Neutralino.window.move(e.screenX * ratio - posX, e.screenY * ratio - posY)
|
||||||
}
|
}
|
||||||
})
|
})
|
@ -7,6 +7,14 @@ body {
|
|||||||
font-family: Arial, Helvetica, sans-serif;
|
font-family: Arial, Helvetica, sans-serif;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
a {
|
||||||
|
color: #fff;
|
||||||
|
}
|
||||||
|
|
||||||
|
img {
|
||||||
|
height: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
.darken {
|
.darken {
|
||||||
filter: brightness(0.6);
|
filter: brightness(0.6);
|
||||||
}
|
}
|
||||||
@ -27,13 +35,135 @@ body {
|
|||||||
margin: 10px;
|
margin: 10px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#miscDialog span,
|
||||||
#firstTimeNotice span {
|
#firstTimeNotice span {
|
||||||
display: block;
|
display: block;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
margin-bottom: 6px;
|
margin-bottom: 6px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#loginPanel {
|
||||||
|
height: 50%;
|
||||||
|
width: 32%;
|
||||||
|
}
|
||||||
|
|
||||||
|
#loginPanel img {
|
||||||
|
height: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#loginPopupTitle {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
font-size: 1.2em;
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
|
||||||
|
.authBody {
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
|
|
||||||
|
.authBody div {
|
||||||
|
margin: 6px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.authBody input {
|
||||||
|
height: 20px;
|
||||||
|
background: white;
|
||||||
|
border: none;
|
||||||
|
border-bottom: 2px solid #4d4d4d;
|
||||||
|
|
||||||
|
/* border bottom anim */
|
||||||
|
transition: border-bottom 0.1s ease-in-out;
|
||||||
|
}
|
||||||
|
|
||||||
|
.authBody input:focus {
|
||||||
|
outline: none;
|
||||||
|
border-bottom: 2px solid #ffc61e;
|
||||||
|
}
|
||||||
|
|
||||||
|
#loginPopupTitle img {
|
||||||
|
height: 20px;
|
||||||
|
margin-bottom: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#loginPopupTitle img:hover {
|
||||||
|
filter: invert(85%) sepia(31%) saturate(560%) hue-rotate(329deg) brightness(100%) contrast(92%);
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
#registerPopupServer,
|
||||||
|
#loginPopupServer {
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
|
||||||
|
#loginSectionTitle,
|
||||||
|
#registerSectionTitle {
|
||||||
|
font-weight: normal;
|
||||||
|
font-size: 1em;
|
||||||
|
}
|
||||||
|
|
||||||
|
#loginSectionTitle:hover,
|
||||||
|
#registerSectionTitle:hover {
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
.selectedTitle {
|
||||||
|
font-weight: bold !important;
|
||||||
|
border-bottom: 2px solid #ffc61e;
|
||||||
|
}
|
||||||
|
|
||||||
|
.authInputs div {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.authInputs div span {
|
||||||
|
margin-right: 6px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.error {
|
||||||
|
background: #e90000;
|
||||||
|
}
|
||||||
|
|
||||||
|
.warn {
|
||||||
|
background: #ffc61e;
|
||||||
|
}
|
||||||
|
|
||||||
|
.success {
|
||||||
|
background: #00c200;
|
||||||
|
}
|
||||||
|
|
||||||
|
#registerAlert,
|
||||||
|
#loginAlert {
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
border-radius: 5px;
|
||||||
|
color: #fff;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
#registerAlert img,
|
||||||
|
#loginAlert img {
|
||||||
|
height: 20px;
|
||||||
|
margin: 10px;
|
||||||
|
filter: invert(100%) sepia(0%) saturate(179%) hue-rotate(253deg) brightness(105%) contrast(101%);
|
||||||
|
}
|
||||||
|
|
||||||
|
#registerAlert span,
|
||||||
|
#loginAlert span {
|
||||||
|
margin: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#miscDialog,
|
||||||
#firstTimeNotice,
|
#firstTimeNotice,
|
||||||
|
#loginPanel,
|
||||||
|
#downloadPanel,
|
||||||
#settingsPanel {
|
#settingsPanel {
|
||||||
display: block;
|
display: block;
|
||||||
position: absolute;
|
position: absolute;
|
||||||
@ -49,17 +179,21 @@ body {
|
|||||||
font-family: system-ui;
|
font-family: system-ui;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#downloadPanel,
|
||||||
#settingsPanel {
|
#settingsPanel {
|
||||||
width: 35%;
|
width: 35%;
|
||||||
height: 70%;
|
height: 80%;
|
||||||
|
overflow: auto;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#downloadTitle,
|
||||||
#fullSettingsTitle {
|
#fullSettingsTitle {
|
||||||
font-size: 1.5em;
|
font-size: 1.5em;
|
||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
margin-bottom: 10px;
|
margin-bottom: 10px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#downloadPanelInner,
|
||||||
#settingsPanelInner {
|
#settingsPanelInner {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
@ -68,16 +202,23 @@ body {
|
|||||||
padding: 10px 10%;
|
padding: 10px 10%;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.downloadRow,
|
||||||
.settingsRow {
|
.settingsRow {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.downloadTitle,
|
||||||
.settingTitle {
|
.settingTitle {
|
||||||
font-size: 1.2em;
|
font-size: 1.2em;
|
||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
margin-bottom: 10px;
|
margin-bottom: 10px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.downloadTitle {
|
||||||
|
margin: 6px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.downloadLabel,
|
||||||
.settingLabel {
|
.settingLabel {
|
||||||
display:inline-block;
|
display:inline-block;
|
||||||
font-size: 1em;
|
font-size: 1em;
|
||||||
@ -85,12 +226,14 @@ body {
|
|||||||
margin: 10px 0px;
|
margin: 10px 0px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.downloadSubtitle,
|
||||||
.settingSubtitle {
|
.settingSubtitle {
|
||||||
color: rgb(165, 165, 165);
|
color: rgb(165, 165, 165);
|
||||||
font-size: 0.8em;
|
font-size: 0.8em;
|
||||||
font-weight: normal;
|
font-weight: normal;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.downloadSection,
|
||||||
.settingSection {
|
.settingSection {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: row;
|
flex-direction: row;
|
||||||
@ -98,10 +241,12 @@ body {
|
|||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.downloadSection .smolBtn,
|
||||||
.settingSection .smolBtn {
|
.settingSection .smolBtn {
|
||||||
height: 30px;
|
height: 30px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#downloadTitleBar,
|
||||||
#settingsTitleBar {
|
#settingsTitleBar {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: row;
|
flex-direction: row;
|
||||||
@ -109,15 +254,18 @@ body {
|
|||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#downloadClose,
|
||||||
#settingsClose {
|
#settingsClose {
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
transition: filter 0.1s ease-in-out;
|
transition: filter 0.1s ease-in-out;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#downloadClose img,
|
||||||
#settingsClose img {
|
#settingsClose img {
|
||||||
height: 20px;
|
height: 20px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#downloadClose:hover,
|
||||||
#settingsClose:hover {
|
#settingsClose:hover {
|
||||||
filter: invert(85%) sepia(31%) saturate(560%) hue-rotate(329deg) brightness(100%) contrast(92%);
|
filter: invert(85%) sepia(31%) saturate(560%) hue-rotate(329deg) brightness(100%) contrast(92%);
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
@ -127,7 +275,7 @@ body {
|
|||||||
position: absolute;
|
position: absolute;
|
||||||
z-index: 99;
|
z-index: 99;
|
||||||
padding: 10px;
|
padding: 10px;
|
||||||
transform: translate(150px, 420px);
|
transform: translate(3vw, 420px);
|
||||||
background-color: #fff;
|
background-color: #fff;
|
||||||
border-radius: 5px;
|
border-radius: 5px;
|
||||||
border: 1px solid #ccc;
|
border: 1px solid #ccc;
|
||||||
@ -276,6 +424,18 @@ body {
|
|||||||
background: linear-gradient(#ffc61e, #ffd326);
|
background: linear-gradient(#ffc61e, #ffd326);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.openFolderIcon {
|
||||||
|
display: inline;
|
||||||
|
height: 20px;
|
||||||
|
filter: invert(97%) sepia(85%) saturate(12%) hue-rotate(184deg) brightness(103%) contrast(103%);
|
||||||
|
padding: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.openFolderIcon:hover {
|
||||||
|
cursor: pointer;
|
||||||
|
filter: invert(99%) sepia(0%) saturate(1092%) hue-rotate(172deg) brightness(80%) contrast(103%);
|
||||||
|
}
|
||||||
|
|
||||||
#bottomBar {
|
#bottomBar {
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
@ -304,7 +464,13 @@ body {
|
|||||||
height: 10%;
|
height: 10%;
|
||||||
}
|
}
|
||||||
|
|
||||||
#genshinPath, #serverPath {
|
.bottomSection div div {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
#gamePath, #serverPath {
|
||||||
color: white;
|
color: white;
|
||||||
font-size: 14px;
|
font-size: 14px;
|
||||||
}
|
}
|
||||||
@ -336,7 +502,7 @@ body {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#firstPanel {
|
#firstPanel {
|
||||||
background-position: -340px;
|
background-position: -200px;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Move the first official button to the position on the png */
|
/* Move the first official button to the position on the png */
|
||||||
@ -351,6 +517,10 @@ body {
|
|||||||
display: block;
|
display: block;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#port {
|
||||||
|
width: 12%;
|
||||||
|
}
|
||||||
|
|
||||||
#secondPanel input {
|
#secondPanel input {
|
||||||
margin-bottom: 4px;
|
margin-bottom: 4px;
|
||||||
height: 20px;
|
height: 20px;
|
||||||
@ -393,4 +563,82 @@ body {
|
|||||||
|
|
||||||
#serverInput img:hover {
|
#serverInput img:hover {
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
#downloadNotif {
|
||||||
|
z-index: 99;
|
||||||
|
position: absolute;
|
||||||
|
|
||||||
|
color: #fff;
|
||||||
|
background: #141414;
|
||||||
|
border: 1px solid #ccc;
|
||||||
|
|
||||||
|
padding: 1em;
|
||||||
|
top: 80%;
|
||||||
|
left: 120%;
|
||||||
|
width: 20%;
|
||||||
|
border-radius: 5px;
|
||||||
|
|
||||||
|
text-align: center;
|
||||||
|
|
||||||
|
transition: left 0.5s ease-in-out;
|
||||||
|
}
|
||||||
|
|
||||||
|
#downloadNotif:hover {
|
||||||
|
cursor: pointer;
|
||||||
|
border-color: #fff;
|
||||||
|
}
|
||||||
|
|
||||||
|
#downloadNotif img {
|
||||||
|
height: 20px;
|
||||||
|
vertical-align: middle;
|
||||||
|
margin-right: 4px;
|
||||||
|
filter: invert(100%) sepia(100%) saturate(0%) hue-rotate(16deg) brightness(103%) contrast(101%);
|
||||||
|
}
|
||||||
|
|
||||||
|
#downloadNotif.displayed {
|
||||||
|
left: 70%;
|
||||||
|
}
|
||||||
|
|
||||||
|
#newestVersion {
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
|
||||||
|
#alert {
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
|
||||||
|
font-weight: bold;
|
||||||
|
color: #704a1d;
|
||||||
|
|
||||||
|
position: absolute;
|
||||||
|
top: -100%;
|
||||||
|
left: 15%;
|
||||||
|
background: linear-gradient(#ffc61e, #ffd326);
|
||||||
|
border: 1px solid #704a1d;
|
||||||
|
border-radius: 6px;
|
||||||
|
|
||||||
|
height: 50px;
|
||||||
|
width: 70%;
|
||||||
|
|
||||||
|
z-index: 999;
|
||||||
|
|
||||||
|
transition: top 0.15s ease-in-out;
|
||||||
|
}
|
||||||
|
|
||||||
|
#alert.show {
|
||||||
|
top: 6%;
|
||||||
|
}
|
||||||
|
|
||||||
|
#dialogTitle {
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
|
||||||
|
#dialogBtns {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-around;
|
||||||
|
align-items: center;
|
||||||
|
width: 100%;
|
||||||
|
margin: 0.6em;
|
||||||
}
|
}
|
58
scripts/gc_download.cmd
Normal file
58
scripts/gc_download.cmd
Normal file
@ -0,0 +1,58 @@
|
|||||||
|
@echo off
|
||||||
|
|
||||||
|
set KEYSTORE_URL=%1
|
||||||
|
set ARTIFACT_URL=%2
|
||||||
|
set BRANCH=%3
|
||||||
|
set FOLDER_NAME=".\gc-%BRANCH%"
|
||||||
|
set FOLDER_NAME=%FOLDER_NAME:"=%
|
||||||
|
|
||||||
|
title GC Download Script
|
||||||
|
|
||||||
|
if not exist "%FOLDER_NAME%" mkdir "%FOLDER_NAME%"
|
||||||
|
if not exist ".\temp" mkdir ".\temp"
|
||||||
|
|
||||||
|
echo Downloading Grasscutter prebuilt jar...
|
||||||
|
|
||||||
|
:: Download the jar
|
||||||
|
powershell Invoke-WebRequest -Uri %KEYSTORE_URL% -OutFile ".\temp\gcjar.zip"
|
||||||
|
|
||||||
|
echo Extracting...
|
||||||
|
|
||||||
|
:: Delete old file if there is one there
|
||||||
|
if exist "%FOLDER_NAME%\grasscutter.jar" del "%FOLDER_NAME%\grasscutter.jar"
|
||||||
|
|
||||||
|
powershell Expand-Archive -Path ".\temp\gcjar.zip" -DestinationPath "%FOLDER_NAME%" -Force
|
||||||
|
|
||||||
|
:: Find the jar file name and rename it, just in case
|
||||||
|
for %%i in (%FOLDER_NAME%/*) do (
|
||||||
|
:: If the extension is jar, rename the file
|
||||||
|
if %%~xi equ .jar rename "%FOLDER_NAME%\%%i" grasscutter.jar
|
||||||
|
)
|
||||||
|
|
||||||
|
echo Downloading keystore.p12...
|
||||||
|
|
||||||
|
:: Download the keystore.p12 file
|
||||||
|
powershell Invoke-WebRequest -Uri %ARTIFACT_URL% -OutFile ".\%FOLDER_NAME%\keystore.p12"
|
||||||
|
|
||||||
|
:: Check java version, this will automatically output some tuff
|
||||||
|
call .\scripts\javaver.cmd %BRANCH%
|
||||||
|
|
||||||
|
:: Allow resource downloading to be optional, since it takes a while
|
||||||
|
set REPLY=y
|
||||||
|
set /p "REPLY=Download server resources? (This can take a while) [y|n]:"
|
||||||
|
if /i not "%reply%" == "y" goto :finish
|
||||||
|
|
||||||
|
call .\scripts\resources_download.cmd %FOLDER_NAME%
|
||||||
|
|
||||||
|
goto :finish
|
||||||
|
|
||||||
|
:finish
|
||||||
|
:: Remove temp stuff
|
||||||
|
del /s /q "./temp"
|
||||||
|
|
||||||
|
echo Done, latest Grasscutter %BRANCH% now downloaded in %FOLDER_NAME%
|
||||||
|
|
||||||
|
pause
|
||||||
|
|
||||||
|
taskkill /f /fi "WINDOWTITLE eq GC Download Script"
|
||||||
|
|
@ -3,11 +3,7 @@
|
|||||||
set ORIGIN=%1
|
set ORIGIN=%1
|
||||||
set ORIGIN=%ORIGIN:"=%
|
set ORIGIN=%ORIGIN:"=%
|
||||||
|
|
||||||
:: Ensure admin
|
title Grassclipper Installer
|
||||||
>nul 2>&1 reg query "HKU\S-1-5-19" || (
|
|
||||||
set params = %*:"="""%
|
|
||||||
cd /d "%~dp0" && ( if exist "%temp%\getadmin.vbs" del "%temp%\getadmin.vbs" ) && fsutil dirty query %systemdrive% 1>nul 2>nul || ( echo Set UAC = CreateObject^("Shell.Application"^) : UAC.ShellExecute "cmd.exe", "/k cd ""%~sdp0"" && %~s0 "%1" ", "", "runas", 1 >> "%temp%\getadmin.vbs" && "%temp%\getadmin.vbs" && exit /B )
|
|
||||||
)
|
|
||||||
|
|
||||||
echo Downloading proxy server...
|
echo Downloading proxy server...
|
||||||
|
|
||||||
@ -15,22 +11,22 @@ echo Downloading proxy server...
|
|||||||
cd "%ORIGIN%"
|
cd "%ORIGIN%"
|
||||||
|
|
||||||
if not exist "%ORIGIN%/ext" mkdir "%ORIGIN%/ext"
|
if not exist "%ORIGIN%/ext" mkdir "%ORIGIN%/ext"
|
||||||
if not exist "%ORIGIN%/temp" mkdir "%ORIGIN%/temp"
|
if not exist "%ORIGIN%/temp" mkdir "%ORIGIN%/temp
|
||||||
|
|
||||||
:: Begin by retrieving mitmproxy 8.0.0
|
:: Begin by retrieving mitmproxy 7.0.4
|
||||||
powershell Invoke-WebRequest -Uri https://snapshots.mitmproxy.org/8.0.0/mitmproxy-8.0.0-windows.zip -OutFile "%ORIGIN%/temp/mitmproxy-8.0.0-windows.zip"
|
powershell Invoke-WebRequest -Uri https://snapshots.mitmproxy.org/7.0.4/mitmproxy-7.0.4-windows.zip -OutFile "'%ORIGIN%\temp\mitmproxy-7.0.4-windows.zip'"
|
||||||
|
|
||||||
echo Extracting...
|
echo Extracting...
|
||||||
|
|
||||||
:: Extract from temp/ to ext/ with powershell
|
:: Extract from temp/ to ext/ with powershell
|
||||||
powershell Expand-Archive -Path "%ORIGIN%/temp/mitmproxy-8.0.0-windows.zip" -DestinationPath "%ORIGIN%/ext/" -Force
|
powershell Expand-Archive -Path "'%ORIGIN%\temp\mitmproxy-7.0.4-windows.zip'" -DestinationPath "'%ORIGIN%\ext\'" -Force
|
||||||
|
|
||||||
del /s /q "%ORIGIN%/temp"
|
del /s /q "%ORIGIN%/temp"
|
||||||
|
|
||||||
echo Running proxy server in order to generate certificates...
|
echo Running proxy server in order to generate certificates...
|
||||||
|
|
||||||
:: Start proxy server
|
:: Start proxy server
|
||||||
start "Proxy Server" %ORIGIN%/ext/mitmdump.exe --ssl-insecure --set ip=%ip%
|
start "Proxy Server" "%ORIGIN%\ext\mitmdump.exe" --ssl-insecure --set ip=%ip%
|
||||||
|
|
||||||
:: Allow the proxy server to create the certificates
|
:: Allow the proxy server to create the certificates
|
||||||
ping 127.0.0.1 -n 6 > nul
|
ping 127.0.0.1 -n 6 > nul
|
||||||
@ -41,12 +37,18 @@ taskkill /f /im mitmdump.exe
|
|||||||
echo Adding ceritifcate...
|
echo Adding ceritifcate...
|
||||||
|
|
||||||
:: Ensure we are elevated for certs
|
:: Ensure we are elevated for certs
|
||||||
>nul 2>&1 certutil -addstore root %USERPROFILE%\.mitmproxy\mitmproxy-ca-cert.cer || (
|
>nul 2>&1 certutil -addstore root "%USERPROFILE%\.mitmproxy\mitmproxy-ca-cert.cer" || (
|
||||||
echo Certificate install failed, ensure the script is running as Administrator and that the path "%USERPROFILE%\.mitmproxy" exists,
|
echo ============================================================================================================
|
||||||
|
echo !! Certificate install failed !!
|
||||||
|
echo.
|
||||||
|
echo Please manually run this command as Administrator:
|
||||||
|
echo certutil -addstore root "%USERPROFILE%\.mitmproxy\mitmproxy-ca-cert.cer"
|
||||||
|
echo ============================================================================================================
|
||||||
)
|
)
|
||||||
|
|
||||||
echo Done! You can now open GrassClipper.exe!
|
echo Done! You can now open GrassClipper.exe!
|
||||||
|
|
||||||
pause
|
pause
|
||||||
|
|
||||||
exit /b
|
taskkill /f /fi "WINDOWTITLE eq Grassclipper Installer"
|
||||||
|
taskkill /f /fi "WINDOWTITLE eq Administrator: Grassclipper Installer"
|
70
scripts/javaver.cmd
Normal file
70
scripts/javaver.cmd
Normal file
@ -0,0 +1,70 @@
|
|||||||
|
@echo off
|
||||||
|
|
||||||
|
set BRANCH=%1
|
||||||
|
|
||||||
|
echo Checking java version...
|
||||||
|
|
||||||
|
where java >nul 2>nul
|
||||||
|
if %errorlevel%==1 (
|
||||||
|
echo =======================================================================================
|
||||||
|
echo No version of Java was found!
|
||||||
|
|
||||||
|
if %BRANCH% EQU stable (
|
||||||
|
echo To launch the stable branch server, you must install Java 8
|
||||||
|
)
|
||||||
|
|
||||||
|
if %BRANCH% EQU development (
|
||||||
|
echo To launch the development branch server, you must install Java 17
|
||||||
|
)
|
||||||
|
|
||||||
|
echo =======================================================================================
|
||||||
|
|
||||||
|
exit /b
|
||||||
|
)
|
||||||
|
|
||||||
|
:: https://stackoverflow.com/questions/5675459/how-to-get-java-version-from-batch-script
|
||||||
|
for /f "tokens=3" %%g in ('java -version 2^>^&1 ^| findstr /i "version"') do (
|
||||||
|
@echo Output: %%g
|
||||||
|
set JAVAVER=%%g
|
||||||
|
)
|
||||||
|
set JAVAVER=%JAVAVER:"=%
|
||||||
|
|
||||||
|
for /f "delims=. tokens=1-3" %%v in ("%JAVAVER%") do (
|
||||||
|
set MAJOR=%%v
|
||||||
|
set MINOR=%%w
|
||||||
|
set BUILD=%%x
|
||||||
|
)
|
||||||
|
|
||||||
|
if %BRANCH% EQU stable (
|
||||||
|
:: Ensure java 8
|
||||||
|
if %MAJOR% EQU 1 (
|
||||||
|
if %MINOR% LSS 8 (
|
||||||
|
echo =======================================================================================
|
||||||
|
echo !! Java version is less than 8 !!
|
||||||
|
echo Please download Java 8 to ensure %BRANCH% branch server launches correctly.
|
||||||
|
echo =======================================================================================
|
||||||
|
exit /b
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
if %MAJOR% NEQ 1 (
|
||||||
|
echo =======================================================================================
|
||||||
|
echo !! Java version is not 8 !!
|
||||||
|
echo Please download Java 8 to ensure %BRANCH% branch server launches correctly.
|
||||||
|
echo =======================================================================================
|
||||||
|
exit /b
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
if %BRANCH% EQU development (
|
||||||
|
:: Ensure java 17
|
||||||
|
if %MAJOR% LSS 17 (
|
||||||
|
echo =======================================================================================
|
||||||
|
echo !! Java version is less than 17 !!
|
||||||
|
echo Please download Java 17 to ensure %BRANCH% branch server launches correctly.
|
||||||
|
echo =======================================================================================
|
||||||
|
exit /b
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
echo Java version is compatible
|
@ -5,6 +5,9 @@ set GAME_EXE_NAME=%GAME_EXE_NAME:"=%
|
|||||||
set PROXY_IP=%2
|
set PROXY_IP=%2
|
||||||
set PROXY_IP=%PROXY_IP:"=%
|
set PROXY_IP=%PROXY_IP:"=%
|
||||||
|
|
||||||
|
:: For task killing
|
||||||
|
title PS Killswitch
|
||||||
|
|
||||||
:: Get current wifi SSID to reconnect
|
:: Get current wifi SSID to reconnect
|
||||||
for /f "delims=: tokens=2" %%n in ('netsh wlan show interface name="Wi-Fi" ^| findstr "Profile"') do set "WIFI=%%n"
|
for /f "delims=: tokens=2" %%n in ('netsh wlan show interface name="Wi-Fi" ^| findstr "Profile"') do set "WIFI=%%n"
|
||||||
set WIFI=%WIFI: =%
|
set WIFI=%WIFI: =%
|
||||||
@ -19,16 +22,20 @@ if "%PROXY_IP%" EQU "localhost" (
|
|||||||
)
|
)
|
||||||
|
|
||||||
:loop
|
:loop
|
||||||
|
:: Wait a couple seconds
|
||||||
|
ping 127.0.0.1 -n 2 > nul
|
||||||
|
|
||||||
:: Check if the game is even running
|
:: Check if the game is even running
|
||||||
@rem QPROCESS "%GAME_EXE_NAME%">NUL
|
:: tasklist /fi "ImageName eq %GAME_EXE_NAME%" /fo csv 2>NUL | find /I "%GAME_EXE_NAME%.exe">NUL
|
||||||
@rem IF %ERRORLEVEL% NEQ 0 (
|
:: IF %ERRORLEVEL% NEQ 0 (
|
||||||
@rem exit /b
|
:: exit /b
|
||||||
@rem )
|
:: )
|
||||||
|
|
||||||
:: Check if the proxy server process is running
|
:: Check if the proxy server process is running
|
||||||
:: https://stackoverflow.com/questions/162291/how-to-check-if-a-process-is-running-via-a-batch-script
|
:: https://stackoverflow.com/questions/162291/how-to-check-if-a-process-is-running-via-a-batch-script
|
||||||
tasklist /fi "ImageName eq mitmdump.exe" /fo csv 2>NUL | find /I "mitmdump.exe">NUL
|
tasklist /fi "ImageName eq mitmdump.exe" /fo csv 2>NUL | find /I "mitmdump.exe">NUL
|
||||||
if "%ERRORLEVEL%" NEQ "0" (
|
if "%ERRORLEVEL%" NEQ "0" (
|
||||||
|
echo "mitmdump not running"
|
||||||
goto killgame
|
goto killgame
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -59,8 +66,6 @@ if "%PROXY_IP%" EQU "localhost" (
|
|||||||
goto killgame
|
goto killgame
|
||||||
)
|
)
|
||||||
|
|
||||||
timeout /t 2 /NOBREAK >nul
|
|
||||||
|
|
||||||
goto loop
|
goto loop
|
||||||
|
|
||||||
:killgame
|
:killgame
|
||||||
@ -84,4 +89,6 @@ if "%PROXY_IP%" EQU "localhost" (
|
|||||||
:: Reconnect to the WiFi
|
:: Reconnect to the WiFi
|
||||||
netsh wlan connect name="%WIFI%"
|
netsh wlan connect name="%WIFI%"
|
||||||
|
|
||||||
|
taskkill /f /fi "WINDOWTITLE eq Administrator: PS Killswitch"
|
||||||
|
|
||||||
exit
|
exit
|
@ -3,12 +3,27 @@
|
|||||||
set GRASSCUTTER_JAR=%1
|
set GRASSCUTTER_JAR=%1
|
||||||
set GRASSCUTTER_JAR=%GRASSCUTTER_JAR:"=%
|
set GRASSCUTTER_JAR=%GRASSCUTTER_JAR:"=%
|
||||||
|
|
||||||
:: Ensure admin
|
title Grasscutter
|
||||||
>nul 2>&1 reg query "HKU\S-1-5-19" || (
|
|
||||||
set params = %*:"="""%
|
:: Get folder the jar is in
|
||||||
cd /d "%~dp0" && ( if exist "%temp%\getadmin.vbs" del "%temp%\getadmin.vbs" ) && fsutil dirty query %systemdrive% 1>nul 2>nul || ( echo Set UAC = CreateObject^("Shell.Application"^) : UAC.ShellExecute "cmd.exe", "/k cd ""%~sdp0"" && %~s0 "%1"", "", "runas", 1 >> "%temp%\getadmin.vbs" && "%temp%\getadmin.vbs" && exit /B )
|
set "X=%GRASSCUTTER_JAR%"
|
||||||
)
|
:l
|
||||||
|
set IS_SLASH=false
|
||||||
|
|
||||||
|
if "%X:~-1%"=="\" set IS_SLASH=true
|
||||||
|
if "%X:~-1%"=="/" set IS_SLASH=true
|
||||||
|
|
||||||
|
if %IS_SLASH% equ true goto al
|
||||||
|
|
||||||
|
set "X=%X:~0,-1%"
|
||||||
|
goto l
|
||||||
|
:al
|
||||||
|
set "X=%X:~0,-1%"
|
||||||
|
set "GRASSCUTTER_ROOT=%X%"
|
||||||
|
|
||||||
echo Starting local Grasscutter server...
|
echo Starting local Grasscutter server...
|
||||||
|
|
||||||
start /b java -jar %GRASSCUTTER_JAR%
|
:: Change dir to server directory
|
||||||
|
cd /d "%GRASSCUTTER_ROOT%"
|
||||||
|
|
||||||
|
call java -jar "%GRASSCUTTER_JAR%"
|
@ -1,19 +1,38 @@
|
|||||||
@echo off
|
@echo off
|
||||||
|
|
||||||
:: Ensure admin
|
:: Ensure admin
|
||||||
>nul 2>&1 reg query "HKU\S-1-5-19" || (
|
@REM >nul 2>&1 reg query "HKU\S-1-5-19" || (
|
||||||
set params = %*:"="""%
|
@REM set params = %*:"="""%
|
||||||
cd /d "%~dp0" && ( if exist "%temp%\getadmin.vbs" del "%temp%\getadmin.vbs" ) && fsutil dirty query %systemdrive% 1>nul 2>nul || ( echo Set UAC = CreateObject^("Shell.Application"^) : UAC.ShellExecute "cmd.exe", "/k cd ""%~sdp0"" && %~s0 %1 "%2" ""%cd%"" %4", "", "runas", 1 >> "%temp%\getadmin.vbs" && "%temp%\getadmin.vbs" && exit /B )
|
@REM cd /d "%~dp0" && ( if exist "%temp%\getadmin.vbs" del "%temp%\getadmin.vbs" ) && fsutil dirty query %systemdrive% 1>nul 2>nul || ( echo Set UAC = CreateObject^("Shell.Application"^) : UAC.ShellExecute "cmd.exe", "/k cd ""%~sdp0"" && %~s0 %1 %2 %3 "%4" ""%cd%"" %6", "", "runas", 1 >> "%temp%\getadmin.vbs" && "%temp%\getadmin.vbs" && exit /B )
|
||||||
)
|
@REM )
|
||||||
|
|
||||||
|
:: Use to force task kill
|
||||||
|
title PS Launcher Script
|
||||||
|
|
||||||
|
:: Use a character encoding that allows for use of Chinese characters. This should work but doesn't, but I'm keeping it in here just in case
|
||||||
|
chcp 65001
|
||||||
|
|
||||||
echo Starting Proxy Server
|
echo Starting Proxy Server
|
||||||
|
|
||||||
set IP=%1
|
set IP=%1
|
||||||
set GAME_PATH=%2
|
set PORT=%2
|
||||||
|
set USE_HTTPS=%3
|
||||||
|
set GAME_PATH=%4
|
||||||
set GAME_PATH=%GAME_PATH:"=%
|
set GAME_PATH=%GAME_PATH:"=%
|
||||||
set ORIGIN=%3
|
set ORIGIN=%5
|
||||||
set ORIGIN=%ORIGIN:"=%
|
set ORIGIN=%ORIGIN:"=%
|
||||||
set ENABLE_KILLSWITCH=%4
|
set ENABLE_KILLSWITCH=%6
|
||||||
|
|
||||||
|
:: For registry
|
||||||
|
set GAME_REG="HKEY_CURRENT_USER\Software\miHoYo\Genshin Impact"
|
||||||
|
|
||||||
|
if "%ENABLE_KILLSWITCH%" EQU "true" (
|
||||||
|
:: Restart in elevated if need be
|
||||||
|
>nul 2>&1 reg query "HKU\S-1-5-19" || (
|
||||||
|
set params = %*:"="""%
|
||||||
|
cd /d "%~dp0" && ( if exist "%temp%\getadmin.vbs" del "%temp%\getadmin.vbs" ) && fsutil dirty query %systemdrive% 1>nul 2>nul || ( echo Set UAC = CreateObject^("Shell.Application"^) : UAC.ShellExecute "cmd.exe", "/k cd ""%~sdp0"" && %~s0 %1 %2 %3 "%4" ""%cd%"" %6", "", "runas", 1 >> "%temp%\getadmin.vbs" && "%temp%\getadmin.vbs" && taskkill /f /fi "WINDOWTITLE eq PS Launcher Script" && exit /b )
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
set PROXY=true
|
set PROXY=true
|
||||||
@rem Store original proxy settings
|
@rem Store original proxy settings
|
||||||
@ -25,11 +44,11 @@ reg add "HKCU\Software\Microsoft\Windows\CurrentVersion\Internet Settings" /v Pr
|
|||||||
reg add "HKCU\Software\Microsoft\Windows\CurrentVersion\Internet Settings" /v ProxyServer /d "127.0.0.1:8080" /f >nul 2>nul
|
reg add "HKCU\Software\Microsoft\Windows\CurrentVersion\Internet Settings" /v ProxyServer /d "127.0.0.1:8080" /f >nul 2>nul
|
||||||
|
|
||||||
:: Start proxy server
|
:: Start proxy server
|
||||||
start "Proxy Server" %ORIGIN%/ext/mitmdump.exe -s "%ORIGIN%/proxy/proxy.py" --ssl-insecure --set ip=%IP%
|
start "Proxy Server" "%ORIGIN%\ext\mitmdump.exe" -s "%ORIGIN%/proxy/proxy.py" -k --allow-hosts ".*\.yuanshen\.com|.*\.mihoyo\.com|.*\.hoyoverse\.com" --ssl-insecure --set ip=%IP% --set port=%PORT% --set use_https=%USE_HTTPS%
|
||||||
|
|
||||||
echo Opening %GAME_PATH%
|
echo Opening %GAME_PATH%
|
||||||
|
|
||||||
:: Allow the proxy server to create the certificates
|
:: Allow the proxy server to open fully
|
||||||
ping 127.0.0.1 -n 5 > nul
|
ping 127.0.0.1 -n 5 > nul
|
||||||
|
|
||||||
for %%A in ("%GAME_PATH%") do (
|
for %%A in ("%GAME_PATH%") do (
|
||||||
@ -41,7 +60,7 @@ echo Killswitch: %ENABLE_KILLSWITCH%
|
|||||||
if "%ENABLE_KILLSWITCH%" EQU "true" (
|
if "%ENABLE_KILLSWITCH%" EQU "true" (
|
||||||
echo Killswitch is enabled!
|
echo Killswitch is enabled!
|
||||||
:: Start killswitch
|
:: Start killswitch
|
||||||
start /b %ORIGIN%\scripts\killswitch.cmd "%GAME_EXE%" %IP%"
|
start /b %ORIGIN%\scripts\killswitch.cmd "%GAME_EXE%" 127.0.0.1
|
||||||
)
|
)
|
||||||
|
|
||||||
:: Launch game
|
:: Launch game
|
||||||
@ -49,16 +68,29 @@ if "%ENABLE_KILLSWITCH%" EQU "true" (
|
|||||||
|
|
||||||
:: On exit clean proxy stuff
|
:: On exit clean proxy stuff
|
||||||
:EXIT
|
:EXIT
|
||||||
if "%PROXY%" == "" (
|
echo Exiting...
|
||||||
|
|
||||||
|
if "%PROXY%" EQU "" (
|
||||||
echo Proxy not started, no need to clean up.
|
echo Proxy not started, no need to clean up.
|
||||||
) else (
|
|
||||||
:: Clean proxy settings
|
|
||||||
echo Cleaning up proxy settings
|
|
||||||
reg add "HKCU\Software\Microsoft\Windows\CurrentVersion\Internet Settings" /v ProxyEnable /t REG_DWORD /d "%ORIG_PROXY_ENABLE%" /f >nul 2>nul
|
|
||||||
reg add "HKCU\Software\Microsoft\Windows\CurrentVersion\Internet Settings" /v ProxyServer /d "%ORIG_PROXY_SERVER%" /f >nul 2>nul
|
|
||||||
|
|
||||||
:: Kill proxy server
|
exit /b
|
||||||
taskkill /f /im mitmdump.exe
|
)
|
||||||
|
|
||||||
exit /b
|
:: Clean proxy settings
|
||||||
)
|
echo Cleaning up proxy settings...
|
||||||
|
reg add "HKCU\Software\Microsoft\Windows\CurrentVersion\Internet Settings" /v ProxyEnable /t REG_DWORD /d "%ORIG_PROXY_ENABLE%" /f >nul 2>nul
|
||||||
|
reg add "HKCU\Software\Microsoft\Windows\CurrentVersion\Internet Settings" /v ProxyServer /d "%ORIG_PROXY_SERVER%" /f >nul 2>nul
|
||||||
|
|
||||||
|
:: Kill proxy server
|
||||||
|
taskkill /f /im mitmdump.exe
|
||||||
|
|
||||||
|
echo Done, see you next time
|
||||||
|
|
||||||
|
:: Just in case the user has corutils installed, use this hacky timeout instead of the timeout command
|
||||||
|
ping 127.0.0.1 -n 2 > nul
|
||||||
|
|
||||||
|
:: Attempt to kill either
|
||||||
|
taskkill /f /fi "WINDOWTITLE eq Administrator: PS Launcher Script"
|
||||||
|
taskkill /f /fi "WINDOWTITLE eq PS Launcher Script"
|
||||||
|
|
||||||
|
exit /b
|
29
scripts/resources_download.cmd
Normal file
29
scripts/resources_download.cmd
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
@echo off
|
||||||
|
|
||||||
|
set FOLDER_NAME=%1
|
||||||
|
set FOLDER_NAME=%FOLDER_NAME:"=%
|
||||||
|
|
||||||
|
if not exist ".\temp" mkdir ".\temp"
|
||||||
|
if not exist ".\resources" mkdir ".\resources"
|
||||||
|
|
||||||
|
echo Downloading resources, this can take a while...
|
||||||
|
|
||||||
|
:: Grab the giant ass resource zip
|
||||||
|
powershell Invoke-WebRequest -Uri https://github.com/Koko-boya/Grasscutter_Resources/archive/refs/heads/main.zip -OutFile ".\temp\resources.zip"
|
||||||
|
|
||||||
|
echo Extracting...
|
||||||
|
|
||||||
|
:: Extract resources to the folder
|
||||||
|
powershell Expand-Archive -Path ".\temp\resources.zip" -DestinationPath "%FOLDER_NAME%" -Force
|
||||||
|
|
||||||
|
:: Delete old resources folder if there is one there
|
||||||
|
del /s /q "%FOLDER_NAME%\resources">nul
|
||||||
|
|
||||||
|
echo Moving resources to folder...
|
||||||
|
|
||||||
|
robocopy "%FOLDER_NAME%\Grasscutter_Resources-main\Resources" "%FOLDER_NAME%\resources" /E /MOVE>nul
|
||||||
|
|
||||||
|
:: Delete straggling files
|
||||||
|
del /s /q "%FOLDER_NAME%\Grasscutter_Resources-main"
|
||||||
|
|
||||||
|
echo Done, resources should be properly extracted
|
8
setup_win.cmd
Normal file
8
setup_win.cmd
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
@echo off
|
||||||
|
|
||||||
|
set BINARY_URL="https://github.com/SpikeHD/neutralinojs/releases/download/v1337.0.0/neutralino-win_x64.exe"
|
||||||
|
|
||||||
|
call npm install
|
||||||
|
|
||||||
|
:: Use powershell to download the binary
|
||||||
|
powershell Invoke-WebRequest -Uri %BINARY_URL% -OutFile "./bin/neutralino-win_x64.exe"
|
BIN
tools/mtools.exe
Normal file
BIN
tools/mtools.exe
Normal file
Binary file not shown.
Loading…
Reference in New Issue
Block a user