Compare commits

..

68 Commits
v0.9.4 ... main

Author SHA1 Message Date
Magix
7368963399
Update README.md 2022-07-04 12:48:18 -04:00
SpikeHD
b2873efcc5
Update README.md 2022-06-02 17:19:56 -07:00
SpikeHD
5e782857b8 Merge branch 'main' of https://github.com/Grasscutters/GrassClipper into main 2022-05-19 00:09:22 -07:00
SpikeHD
224a8b9041 fix missing grasscutter file dialog lang option 2022-05-19 00:09:16 -07:00
SpikeHD
957440ec38
Update README.md 2022-05-16 08:59:00 -07:00
SpikeHD
b0638e7b76 Merge branch 'main' of https://github.com/Grasscutters/GrassClipper into main 2022-05-14 15:12:13 -07:00
SpikeHD
f09eb6ceb1
Update setup_win.cmd 2022-05-14 12:26:51 -07:00
SpikeHD
89dd29a528
Update README.md 2022-05-14 02:46:44 -07:00
SpikeHD
45015ae509
Merge pull request #74 from tsukiroku/main
Added Korean translation.
2022-05-14 02:46:17 -07:00
SpikeHD
2484d0bc6b
Merge pull request #77 from omg-xtao/omg-xtao-patch-1
loginByThirdparty
2022-05-14 02:45:56 -07:00
omg-xtao
c2383577d1
loginByThirdparty 2022-05-14 01:24:32 +08:00
tsukiroku
66b6330c73
Create ko.json
ko-KR
2022-05-14 00:10:23 +09:00
SpikeHD
e6854d454f
Merge pull request #73 from xwwwb/main
replace axios link to local source, some Some countries cannot access unpkg.com
2022-05-12 08:59:59 -07:00
雨奇晴好
9897b3fe49
Update index.html 2022-05-12 22:49:09 +08:00
雨奇晴好
2cc581aff4
use local sourse 2022-05-12 22:48:38 +08:00
SpikeHD
ae0578b8f5
Update README.md 2022-05-11 09:11:35 -07:00
SpikeHD
06d95b3165
Merge pull request #71 from ongsalt/main
Added Thai translation
2022-05-11 09:11:20 -07:00
Oachi
17254dcc5b added Thai translation 2022-05-11 17:05:00 +07:00
SpikeHD
3dce59e869
Update README.md 2022-05-10 22:26:10 -07:00
SpikeHD
797f760094 version bump 2022-05-09 20:22:11 -07:00
SpikeHD
f4f98d1847 fix java version 2022-05-08 12:39:14 -07:00
SpikeHD
ed59684ac9 fix foreign chars 2022-05-08 12:37:08 -07:00
SpikeHD
c72e00f784 version bump 2022-05-07 22:51:40 -07:00
SpikeHD
f2cdab068a Fix resource getting 2022-05-07 22:51:17 -07:00
SpikeHD
60569bf0b1 version bump 2022-05-07 21:55:17 -07:00
SpikeHD
98762a310e get process output during data file install 2022-05-07 21:54:50 -07:00
SpikeHD
2ded1f69ae disable data downloader when gc folder not set 2022-05-07 21:51:49 -07:00
SpikeHD
fecd82fe75 temp foreign char regex 2022-05-07 20:56:44 -07:00
SpikeHD
b9a77ab8f1 allow debugging 2022-05-07 20:49:45 -07:00
SpikeHD
376171b2cf data file options and translations 2022-05-07 20:13:54 -07:00
SpikeHD
5f81ced09f do button stuff 2022-05-07 20:08:55 -07:00
SpikeHD
4a32d1fb41 seperate data files 2022-05-07 20:05:01 -07:00
SpikeHD
71235a59c3 hardcoded language defaults 2022-05-07 19:58:17 -07:00
SpikeHD
a73c3ed5cd foreign char alert 2022-05-07 19:56:02 -07:00
SpikeHD
8951974633 fallback build URL 2022-05-07 19:44:07 -07:00
SpikeHD
7094277199 version bump 2022-05-07 17:32:57 -07:00
SpikeHD
9a09182973 Merge branch 'main' of https://github.com/Grasscutters/GrassClipper into main 2022-05-07 17:32:52 -07:00
SpikeHD
67dc692270 update java version scripts 2022-05-07 17:32:30 -07:00
SpikeHD
055abef5ce add resource download properly 2022-05-07 17:15:54 -07:00
SpikeHD
37d1761813
Merge pull request #63 from nautilust/main
Update language options in README file and add Indonesian README translation
2022-05-07 13:34:39 -07:00
Nautilus
b03de8a055 Add README_id.md 2022-05-07 18:01:30 +07:00
SpikeHD
a9a7cc2e45
Update README.md 2022-05-07 03:29:20 -07:00
SpikeHD
6ccbaa1f24
Merge pull request #62 from actuallyeunha/main
Update pt-br.json
2022-05-07 03:28:55 -07:00
Nautilus
24bc47a72c Update README 2022-05-07 15:25:30 +07:00
Ana Eunha
98657a8270 Update pt-br.json
Updates pt-br to include text from newer versions.
2022-05-06 22:40:23 -03:00
Ana Eunha
5e80d59ed7 Merge branch 'main' of https://github.com/actuallyeunha/GrassClipper 2022-05-06 22:39:26 -03:00
SpikeHD
b6adec4079
Update README.md 2022-05-06 17:29:49 -07:00
SpikeHD
f3534a3fff
Merge pull request #61 from lunaticwhat/main
fixed vn-vi language grammars and added new strings
2022-05-06 17:29:24 -07:00
SpikeHD
4c2e303c6e
Merge pull request #60 from zakhil-dev/main
Added Polish README
2022-05-06 17:29:08 -07:00
SpikeHD
4971d7a4b0
Merge pull request #59 from zakhil-dev/patch-1
Update polish translation
2022-05-06 17:28:42 -07:00
SpikeHD
d8b753f6e8 Merge branch 'main' of https://github.com/Grasscutters/GrassClipper into main 2022-05-06 17:28:27 -07:00
SpikeHD
34ad43cdcb fix launching grasscutter jar 2022-05-06 17:28:22 -07:00
lunaticwhat
a4cf3ee7d4 fixed vn-vi language grammar and added new strings 2022-05-07 06:20:47 +07:00
Piotr Blecharski
1dba3f682c Added Polish README 2022-05-07 00:33:33 +02:00
Zakhil
8c8bd4476c
Update polish translation
Found another string wich I forgot about
2022-05-06 23:41:47 +02:00
SpikeHD
b4760fae9a
Merge pull request #58 from nautilust/main
Update Indonesian Translation
2022-05-06 14:27:53 -07:00
Nautilus
35d2d4de77 small fix and update translations 2022-05-07 03:17:57 +07:00
SpikeHD
90fd5e6204
Update README.md 2022-05-06 12:25:46 -07:00
SpikeHD
5d8f8fe09d
Update README.md 2022-05-06 10:46:18 -07:00
SpikeHD
af225781e3
Merge pull request #57 from zakhil-dev/main
Added Polish translation
2022-05-06 10:45:46 -07:00
Piotr Blecharski
73b1a93039 Merge branch 'main' of github.com:zakhil-dev/GrassClipper 2022-05-06 19:37:12 +02:00
Piotr Blecharski
acb7d9ef91 Forgor to translate one string 2022-05-06 19:37:08 +02:00
Zakhil
19fcfa446c
Merge branch 'Grasscutters:main' into main 2022-05-06 19:33:50 +02:00
Piotr Blecharski
61ebbaf72e Added Polish translation 2022-05-06 19:33:23 +02:00
SpikeHD
5843f0d589
Update README.md 2022-05-06 10:21:28 -07:00
SpikeHD
412be98a05
Merge pull request #56 from Kimi898246/patch-3
Traditional Chinese | Translation Patch
2022-05-06 10:04:28 -07:00
Kimi
72a9d77dd5
Traditional Chinese 2022-05-06 22:42:01 +08:00
Ana Eunha
55e3a8f6f2 Add "Portugues Brasileiro" support 2022-04-23 03:33:09 -03:00
36 changed files with 1085 additions and 155 deletions

View File

@ -12,6 +12,7 @@
"no-undef": 0, "no-undef": 0,
"no-unused-vars": 0, "no-unused-vars": 0,
"no-case-declarations": 0, "no-case-declarations": 0,
"no-control-regex": 0,
"indent": [ "indent": [
"error", "error",

View File

@ -1,7 +1,15 @@
# Please for the love of god do not install GrassClipper into a path in OneDrive
# ! NOTICE !
The previously mentioned **new launcher**: [Cultivation](https://github.com/Grasscutters/Cultivation) has been "released"!\
If you are experiencing problems with GrassClipper, we encourage you to try and checkout the new launcher.
# GrassClipper # GrassClipper
EN | [PL](README_PL.md) | [ID](README_id.md)
Experimental 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!* *\*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!*
@ -11,6 +19,7 @@ Experimental Grasscutter launcher for easily switching between Official and Priv
* [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 502](#error-502)
* [Error 4206](#error-4206) * [Error 4206](#error-4206)
@ -60,6 +69,15 @@ Experimental Grasscutter launcher for easily switching between Official and Priv
Below are some scenarios you may encounter and their solutions. Below are some scenarios you may encounter and their solutions.
# Manual Proxy Installation
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!
## White Screen Fix ## White Screen Fix
Encountering a white screen? [Ensure WebView2 is installed](https://developer.microsoft.com/en-us/microsoft-edge/webview2/#download) Encountering a white screen? [Ensure WebView2 is installed](https://developer.microsoft.com/en-us/microsoft-edge/webview2/#download)
@ -82,6 +100,8 @@ If all else fails, you can run GrassClipper in `chrome` or `browser` mode. To do
## Error 502 ## Error 502
***You may be using the wrong port, ensure your port in GrassClipper is either 443 or nothing (not 0, but literally nothing)***
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. 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. 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.
@ -115,8 +135,8 @@ Thank you to everyone who has provided translations! <3
* ZH - nuoxianCN, Scirese & MrAru * ZH - nuoxianCN, Scirese & MrAru
* ZH-TW - Kimi & KormiMeiko * ZH-TW - Kimi & KormiMeiko
* PT-BR - na.na * PT-BR - na.na & actuallyeunha
* VIE - labalityowo * VIE - labalityowo & lunaticwhat
* ID - Iqrar99 & nautilust * ID - Iqrar99 & nautilust
* FR - linsorak & memetrollsXD * FR - linsorak & memetrollsXD
* ES - memetrollsXD * ES - memetrollsXD
@ -125,6 +145,9 @@ Thank you to everyone who has provided translations! <3
* TR - lilmayofuksu * TR - lilmayofuksu
* JP - conochy * JP - conochy
* HD - Arikatsu * HD - Arikatsu
* PL - zakhil-dev
* TH - ongsalt
* KO - tsukiroku
# Screenshots # Screenshots

152
README_PL.md Normal file
View File

@ -0,0 +1,152 @@
# GrassClipper
[EN](README.md) | PL | [ID](README_id.md)
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)

152
README_id.md Normal file
View File

@ -0,0 +1,152 @@
# GrassClipper
[EN](README.md) | [PL](README_PL.md) | ID
Peluncur eksperimental Grasscutter untuk memudahkan penggantian antara server resmi (Official) dan server pribadi
[Unduh Disini!](https://github.com/Grasscutters/GrassClipper/releases/) (Mendukung Windows 8 keatas)
*\*Catatan: Beberapa terjemahan mungkin ada yang sudah usang, kalau ada bahasa Inggris yang muncul secara acak pada teks atau opsi pilihan mungkin ini penyebabnya. Jika kamu melihat masalah seperti ini, jangan ragu untuk membuat pull request!*
# Daftar Isi
* [Setup (untuk pengguna)](#setup-untuk-pengguna)
* [Setup (untuk pengembang)](#setup-untuk-pengembang)
* [Daftar yang Harus Dilakukan](#daftar-yang-harus-dilakukan)
* [Masalah Umum](#punya-masalah)
* [Pemasangan Proxy Tidak Terbuka/Gagal](#pemasangan-manual-proxy)
* [Perbaikan Layar Putih](#perbaikan-layar-putih)
* [Kesalahan 502](#kesalahan-502)
* [Kesalahan 4206](#kesalahan-4206)
* [Jendela CMD Terbuka Terus-menerus](#jendela-cmd-terbuka-terus-menerus)
* [Discord/Youtube Tidak Berfungsi dengan Baik](#discord-tidak-bisa-mengirim-pesan-atau-memuat-gambar-atau-youtube-tidak-tampil-dengan-baik)
* [Tidak Ada Koneksi Internet](#komputerku-tidak-ada-akses-internet-setelah-menutup-peluncur-atau-restart)
* [Bahasa dan Kredit Terjemahan](#bahasa-yang-tersedia-dan-kredit-penerjemah)
* [Tangkapan Layar](#tangkapan-layar)
# Setup (untuk Pengguna)
1. Unduh file zip nya
2. Ekstrak file zip nya di sembarang tempat atau tempat yang sudah ditentukan
3. Jalankan `GrassClipper.exe`, pasang server proxy nya, dan atur folder game nya!
# Setup (untuk Pengembang)
0. Clone/unduh repositori ini
1. Pastikan kamu punya [NodeJS](https://nodejs.org/en/download/) yang sudah terpasang.
2. Pasang alat CLI `neu`: `npm install -g @neutralinojs/neu`
3. Pasang dependensinya: `setup_win.cmd`
4. Kompilasi dan jalankan:
* Untuk pengujian: `npm run dev`
* Untuk produksi: `npm run build`
# Daftar yang Harus Dilakukan
* Antarmuka/internal
* [x] UI
* [x] Opsi server resmi (Official) dan pribadi
* [x] IP server masukan
* [x] Penataan gaya CSS yang wah! (Seperti gaya menu vertikal ala CoD: MW 2019 untuk memilih antara server resmi (official) dan pribadi? [Lihat disini](https://charlieintel.com/wp-content/uploads/2020/11/MW-new-menu.png))
* [x] Skrip saklar mati (opsional)
* [x] Otomatis menjalankan `install.cmd` ketika membuka GrassClipper untuk pertama kalinya
* [x] Pengunduh otomatis Grasscutter
* [ ] Deteksi saat berada di folder yang tidak dapat diakses oleh program (contohnya `C:/Program Files`) dan ingatkan
* [ ] Gambar kustom untuk bagian server pribadi (siapapun dipersilahkan mengirimkan pull request untuk menambahkan gambar lainnya!)
* [x] Nama pengguna/kata sandi opsional sebelum memasuki server (belum terimplementasi di Grasscutter)
* [ ] Deteksi platform dan skrip bash
* [ ] Pembuat spanduk terintegrasi
* Layanan Proxy
* [x] Server lokal proxy
* [x] Cegat dan ubah permintaan GI seperti dengan Fiddler, izinkan yang lain melewati
* [ ] Perbaiki masalah Discord dan YouTube saat proxy diaktifkan (mungkin diperbaiki)
# Punya masalah?
Dibawah ini mungkin beberapa skenario yang kamu hadapi dan beserta solusinya.
# Pemasangan Manual Proxy
Jika kamu punya masalah saat memasang server proxy, kamu juga dapat memasangnya secara manual. Cara memasangnya:
1. Buat sebuah folder yang bernama `ext` di folder GrassClipper jika folder tersebut tidak ada.
2. Unduh dan ekstrak isi konten dari [file ini](https://snapshots.mitmproxy.org/7.0.4/mitmproxy-7.0.4-windows.zip) ke dalam folder `ext`
3. Klik dua kali `mitmdump.exe` dan izinkan untuk menjalankan beberapa detik agar bisa membuat sertifikat
4. Jalankan perintah ini sebagai Administrator: `certutil -addstore root "%USERPROFILE%\.mitmproxy\mitmproxy-ca-cert.cer"`
5. Gunakan GrassClipper seperti biasa!
## Perbaikan Layar Putih
Mengalami layar putih? [Pastikan WebView2 terpasang](https://developer.microsoft.com/en-us/microsoft-edge/webview2/#download)
Kamu mungkin perlu menjalankan perintah ini sebagai Administrator:
`CheckNetIsolation.exe LoopbackExempt -a -n="Microsoft.Win32WebViewHost_cw5n1h2txyewy"`
Jika kamu mempunyai karakter Cina di jalur file mu, mungkin ini dapat menyebabkan crash! Saya sedang mengerjakan perbaikannya.
Kamu mungkin bisa juga menjalankan ini di mode kompatibilitas Windows 8.
Jika semuanya gagal, kamu bisa menjalankan GrassClipper di mode `chrome` atau `browser`. Cara melakukannya:
* Buat sebuah shortcut ke `GrassClipper.exe`
* Klik kanan shortcut tersebut, lalu klik `Properties`
* Di dalam kotak `Target`, pada bagian akhirnya, tambahkan ` --mode=chrome` atau ` --mode=browser`
* `chrome` hanya bisa bekerja jika kamu punya Chrome yang terpasang dan akan membuat jendela Chrome
* Untuk `browser` seperti yang kamu tebak, akan membuka GrassClipper di default browser mu
* Klik `Ok`
* Jalankan GrassClipper dengan menggunakan shortcut ini mulai dari sekarang!
## Kesalahan 502
1. Jika kamu menjalankan ini di server lokal, pastikan server lokal tersebut berjalan dengan baik. Sebaliknya, pastikan server yang kamu hubungkan juga berjalan dengan baik.
2. Kalau bisa, [gunakan versi pengembangan Grasscutter](https://github.com/Grasscutters/Grasscutter/tree/development). Biasanya versi ini bekerja dengan baik di GrassClipper.
Jika kamu masih mendapatkan kesalahan 502 ketika mencoba masuk ke servermu sendiri, buka file konfigurasi Grasscutter dan tambahkan pada bagian `DispatchServer`:
```json
"PublicPort": PORT_MU
```
dimana `PORT_MU` itu sama dengan port yang kamu gunakan sebagai nilai `Port`. Biasanya diisi dengan nilai 443.
## Kesalahan 4206
Pastikan kamu punya file `keystore.p12` dari versi yang kamu pilih (`stabil` atau `pengembangan`). Dan juga pastikan kata sandinya diatur dengan benar di file `config.json` pada Grasscutters (kosong untuk `stabil`, "123456" untuk `pengembangan`).
## Jendela CMD Terbuka Terus-menerus
Jika kamu mendapatkan jendela CMD yang terbuka secara terus-menerus dari beberapa skrip (seperti pemasangan proxy atau saat menjalankan server pribadi), pastikan kamu punya UAC (User Access Control) yang diatur pada opsi yang memunculkan dialog permintaan izin menjalankan. Pastikan juga akun penggunamu (user account) bisa dibuka dengan perizinan Admin.
## Discord Tidak Bisa Mengirim Pesan atau Memuat Gambar atau Youtube Tidak Tampil dengan Baik
Discord/YouTube (dan beberapa aplikasi lainnya) sepertinya tidak menyukai server proxy mu. Kamu mungkin perlu menonaktifkannya dengan menutup mitmdump atau dengan menonaktifkan proxy mu di pengaturan Windows proxy.
## Komputerku Tidak Ada Akses Internet Setelah Menutup Peluncur atau Restart!
Sepertinya peluncur tidak menutup dengan benar dan tidak bisa membersihkan pengaturan proxy seperti semula. Nonaktifkan proxy mu di pengaturan Windows proxy.
# Bahasa yang Tersedia dan Kredit Penerjemah
Terima kasih kepada semua orang yang telah menyediakan terjemahan! <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
# Tangkapan Layar
![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)

View File

@ -28,6 +28,7 @@
"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", "httpsOption": "Use HTTPS",
"httpsSubtitle": "Choose between using HTTPS or HTTP.", "httpsSubtitle": "Choose between using HTTPS or HTTP.",
"debugOption": "Debugging",
"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! :)",
@ -38,7 +39,7 @@
"proxyInstallBtn": "Install", "proxyInstallBtn": "Install",
"proxyInstallDeny": "No thanks", "proxyInstallDeny": "No thanks",
"gameFolderDialog": "Select game folder", "gameFolderDialog": "Select game exe",
"grasscutterFileDialog": "Select Grasscutter server jar file", "grasscutterFileDialog": "Select Grasscutter server jar file",
"loggingInTo": "Logging in to: ", "loggingInTo": "Logging in to: ",
@ -74,6 +75,8 @@
"devSubtitle": "Install Grasscutter development branch. This build sometimes has bugs, and is frequently updated. Use at your own risk.", "devSubtitle": "Install Grasscutter development branch. This build sometimes has bugs, and is frequently updated. Use at your own risk.",
"downloadResources": "Download Grasscutter Resources", "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.", "resourceSubtitle": "Downloads Grasscutter resources into the currently set Grasscutter folder. This should be done unless you plan on getting resources externally.",
"downloadData": "Download Grasscutter Data",
"dataSubtitle": "Downloads Grasscutter data files, such as keys, spawns, and other vital files.",
"gcScriptRunning": "Running...", "gcScriptRunning": "Running...",
"stableInstall": "Download", "stableInstall": "Download",
@ -81,7 +84,7 @@
"updateNotifText": "A new update is available! Newest version: ", "updateNotifText": "A new update is available! Newest version: ",
"chineseCharacterAlert": "The file path set contains Chinese characters, this may cause problems!", "foreignCharacterAlert": "The file path set contains foreign characters, this may cause problems!",
"dialogOk": "OK", "dialogOk": "OK",
"dialogNo": "NO", "dialogNo": "NO",

View File

@ -81,5 +81,5 @@
"updateNotifText": "नया अपडेट आया है! नया वर्शन: ", "updateNotifText": "नया अपडेट आया है! नया वर्शन: ",
"chineseCharacterAlert": "इस फाइल पथ में चीनी चरक्टेर्स हैं, यह समस्या दे सकता है!" "foreignCharacterAlert": "इस फाइल पथ में चीनी चरक्टेर्स हैं, यह समस्या दे सकता है!"
} }

View File

@ -71,13 +71,20 @@
"downloadStable": "Unduh Versi Stabil Grasscutter", "downloadStable": "Unduh Versi Stabil Grasscutter",
"stableSubtitle": "Pasang versi stabil Grasscutter. Versi ini biasanya tidak punya banyak bug, tetapi juga fiturnya lebih sedikit.", "stableSubtitle": "Pasang versi stabil Grasscutter. Versi ini biasanya tidak punya banyak bug, tetapi juga fiturnya lebih sedikit.",
"downloadDev": "Unduh Versi Pengembangan Grasscutter", "downloadDev": "Unduh Versi Pengembangan Grasscutter",
"downloadSubtitle": "Pasang versi pengembangan Grasscutter. Versi ini biasanya mempunyai banyak bug dan sering diperbarui. Gunakan dengan resikomu sendiri.", "devSubtitle": "Pasang versi pengembangan Grasscutter. Versi ini biasanya mempunyai banyak bug dan sering diperbarui. Gunakan dengan resikomu sendiri.",
"downloadResources": "Unduh Sumber Daya Grasscutter", "downloadResources": "Unduh Sumber Daya Grasscutter",
"devSubtitle": "Unduh sumber daya Grasscutter ke dalam folder Grasscutter yang sudah diatur. Ini harus dilakukan kecuali kamu berencana untuk mendapatkan sumber daya secara eksternal.", "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...", "gcScriptRunning": "Menjalankan...",
"stableInstall": "Unduh", "stableInstall": "Unduh",
"devInstall": "Unduh", "devInstall": "Unduh",
"updateNotifText": "Pembaruan baru tersedia! Versi terbaru: " "updateNotifText": "Pembaruan baru tersedia! Versi terbaru: ",
"foreignCharacterAlert": "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?"
} }

View File

@ -81,5 +81,5 @@
"updateNotifText": "新しいバージョンが利用できます。Version: ", "updateNotifText": "新しいバージョンが利用できます。Version: ",
"chineseCharacterAlert": "ファイルパスに漢字やひらがななどの全角文字が含まれているため、正常に動作しない場合があります。" "foreignCharacterAlert": "ファイルパスに漢字やひらがななどの全角文字が含まれているため、正常に動作しない場合があります。"
} }

93
languages/ko.json Normal file
View File

@ -0,0 +1,93 @@
{
"fullLangName": "한국어",
"appName": "GrassClipper",
"playOfficial": "플레이 (공식)",
"playPrivate": "플레이 (Grasscutter)",
"launchLocalServer": "로컬에서 실행",
"gameExeSet": "게임 실행파일 설정",
"grasscutterFileSet": "\"Grasscutter\" .jar 파일 설정",
"folderNotSet": "폴더가 지정되지 않았습니다.",
"ipPlaceholder": "서버 주소",
"portPlaceholder": "포트",
"noFavorites": "즐겨찾기를 설정하지 않았습니다.",
"settingsTitle": "설정",
"scriptsSectionTitle": "스크립트",
"killswitchOption": "프로세스 중지",
"killswitchSubtitle": "프록시에 문제가 발생할 경우, 게임과 인터넷을 중지합니다.",
"proxyOption": "프록시",
"proxySubtitle": "스크립트를 통해 프록시 서버 설치",
"updateOption": "업데이트",
"updateSubtitle": "자동 업데이트가 일시적으로 비활성화되어 있습니다. Github에서 최신 버전을 확인하세요.",
"languageOption": "언어",
"languageSubtitle": "언어를 선택해주세요.",
"enableServerLauncherOption": "서버 실행 버튼 활성화",
"enableServerLauncherSubtitle": "로컬 Grasscutter 인스턴스를 실행하기 위해, 서버 실행기 타일을 활성화합니다.",
"httpsOption": "HTTPS 사용",
"httpsSubtitle": "HTTPS 또는 HTTP 중 하나를 선택하세요.",
"debugOption": "디버깅",
"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 리소스를 현재 설정된 Grasscutter 폴더에 다운로드합니다. 외부에서 리소스를 가져올 예정이 아니라면, 이 작업을 수행해야 합니다.",
"downloadData": "Grasscutter 데이터 다운로드",
"dataSubtitle": "중요한 Grasscutter 데이터 파일을 다운로드합니다.",
"gcScriptRunning": "실행중...",
"stableInstall": "다운로드 (stable)",
"devInstall": "다운로드 (dev)",
"updateNotifText": "새 업데이트를 사용할 수 있습니다: ",
"foreignCharacterAlert": "파일 경로에 유효하지 않은 문자가 포함되어 있으므로, 문제가 발생할 수 있습니다!",
"dialogOk": "예",
"dialogNo": "아니오",
"serverEnableDialogTitle": "Grasscutter 서버 실행기를 찾았습니다!",
"serverEnableDialogText": "설정할 기존의 Grasscutter가 없는 경우, 빌드를 다운로드하시겠습니까?"
}

91
languages/pl.json Normal file
View 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: ",
"foreignCharacterAlert": "Ś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?"
}

View File

@ -35,5 +35,53 @@
"proxyInstallDeny": "Não obrigado", "proxyInstallDeny": "Não obrigado",
"gameFolderDialog": "Selecione a game pasta", "gameFolderDialog": "Selecione a game pasta",
"grasscutterFileDialog": "Selecione o arquivo jar do Grasscutter" "grasscutterFileDialog": "Selecione o arquivo jar do Grasscutter",
"loggingInTo": "Logando como: ",
"registeringFor": "Registrando como: ",
"authUsername": "Usuário: ",
"authPassword": "Senha: ",
"authConfirmPassword": "Confirme a senha: ",
"authLoginBtn": "Login",
"authRegisterBtn": "Registrar",
"authLoginTitle": "Login",
"authRegisterTitle": "Registrar",
"launchWithoutAuth": "Iniciar sem autenticação",
"alertInvalid": "Usuário ou senha invalidos",
"alertNoPass": "Nenhuma senha cadastrada, por favor troque a senha",
"alertUnknown": "Erro desconhecido, contate o dono do servidor",
"alertAuthNoLogin": "Autenticação desabilitada, não ha necessidade de logar!",
"alertLoginSuccess": "Sucesso no login! Token copiado. Cole esse token no lugar do usuário no jogo para logar.",
"alertUserTaken": "Usuário em uso",
"alertPassMismatch": "Senha e confirmação não estão iguais",
"alertAuthNoRegister": "Autenticação desabilitada, não há necessidade de registrar!",
"alertRegisterSuccess": "Registrado com sucesso!",
"downloadTitle": "Downloads",
"grassclipperTitle": "GrassClipper",
"grasscutterTitle": "Grasscutter",
"installerTitle": "Instalador",
"installerSubtitle": "Instala o proxy e outras ferramentas. Necessário para servidores Grasscutter",
"downloadStable": "Baixar a build Grasscutter Stable",
"stableSubtitle": "Instalar Grasscutter stable. Essa build geralmente tem menos bugs, mas menos recursos",
"downloadDev": "Baixar a build Grasscutter Development",
"devSubtitle": "Instalar Grasscutter Development. Essa build geralmente tem bugs e é frequentemente atualizada. Use por sua conta e risco.",
"downloadResources": "Baixar Grasscutter Resources",
"resourceSubtitle": "Baixar Grasscutter Resources na pasta atual setada do Grasscutter. Isso deve ser feito a não ser que você planeje colocar os resources externamente.",
"gcScriptRunning": "Rodando...",
"stableInstall": "Baixar",
"devInstall": "Baixar",
"updateNotifText": "Uma nova atualização esta disponivel! Nova versão: ",
"foreignCharacterAlert": "O caminho setado contem caracteres chineses, isso pode causar problemas!",
"dialogOk": "OK",
"dialogNo": "Não",
"serverEnableDialogTitle": "Você achou o launcher do server do Grasscutter!",
"serverEnableDialogText": "Caso você ainda não tenha uma instalação do grasscutter para setar, quer baixar uma build?"
} }

95
languages/th.json Normal file
View File

@ -0,0 +1,95 @@
{
"fullLangName": "ภาษาไทย",
"appName": "GrassClipper",
"playOfficial": "เริ่มเกม (Official)",
"playPrivate": "เริ่มเกม (Grasscutter)",
"launchLocalServer": "เริ่ม Local Server",
"gameExeSet": "เลือกไฟล์เกม",
"grasscutterFileSet": "เลือกไฟล์ \"Grasscutter\" .jar ",
"folderNotSet": "ยังไม่ได้ตั้ง",
"ipPlaceholder": "ที่อยู่เซิร์ฟเวอร์...",
"portPlaceholder": "พอร์ต",
"noFavorites": "ยังไม่ได้ตั้ง",
"settingsTitle": "การตั้งค่า",
"scriptsSectionTitle": "สคริปต์",
"killswitchOption": "Kill Switch",
"killswitchSubtitle": "เอาไว้สำหรับพวกที่กลัวโดนแบนมากๆ เมื่อพร็อกซี่มีปัญหาจะบังคับปิดเกมและตัดอินเตอร์เน็ตทันที",
"proxyOption": "พร็อกซี่",
"proxySubtitle": "ติดตั้งพร็อกซี่ด้วยสคริปต์ติดตั้ง",
"updateOption": "อัพเดท",
"updateSubtitle": "การอัพเดทอัตโนมัติไม่พร้อมใช้งานในขณะนี้ ไปที่ Github สำหรับเวอร์ชั่นใหม่",
"languageOption": "ภาษา",
"languageSubtitle": "เลือกภาษาของคุณ",
"enableServerLauncherOption": "เพิ่มปุ่มเริ่มเซิร์ฟเวอร์",
"enableServerLauncherSubtitle": "เพิ่มปุ่มเริ่มเซิร์ฟเวอร์ไปยังหน้าแลนเชอร์ใช้สำหรับเริ่มการทำงานของเซิร์ฟเวอร์ Grasscutter ที่ติดตั้งไว้",
"httpsOption": "ใช้ HTTPS",
"httpsSubtitle": "เลือกระหว่าง HTTPS กับ HTTP.",
"debugOption": "การดีบัก",
"introSen1": "ดูเหมือนว่านี่เป็นครั้งแรกที่คุณเปิด GrassClipper!",
"introSen2": "อย่างแรกเลย ยินดีต้อนรับ เรายินดีที่ได้พบคุณที่นีี่! :)",
"introSen3": "คุณต้องการที่จะรันตัวติดตั้งพร็อกซี่หรือไม่ ",
"introSen4": "(จำเป็นต้องเชื่อมต่อไปยังเซิร์ฟเวอร์)",
"updateBtn": "อัพเดท",
"proxyInstallBtn": "ติดตั้ง",
"proxyInstallDeny": "ไม่ ขอบคุณ",
"gameFolderDialog": "เลือกไฟล์ .exe เกม",
"grasscutterFileDialog": "เลือกไฟล์ .jar Grasscutter ",
"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 ไปยังโฟลเดอร์ของ Grasscutter ที่ตั้งไว้ คุณควรดำเนินการนี้เว้นแต่ว่าตุณวางแผนจะใช้ไฟล์จากภายนอก",
"downloadData": "ดาวน์โหลดไฟล์ข้อมูล Grasscutter",
"dataSubtitle": "ดาวน์โหลดไฟล์ข้อมูลของ Grasscutter อย่างเช่น keys, spawns หรือไฟล์สำคัญอย่างอื่น",
"gcScriptRunning": "กำลังรัน...",
"stableInstall": "ดาวน์โหลด",
"devInstall": "ดาวน์โหลด",
"updateNotifText": "มีการอัพเดทพร้อมใช้งาน เวอร์ชั่นล่าสุด: ",
"foreignCharacterAlert": "เส้นทางของไฟล์มีภาษาอื่นนอกจากภาษาอังกฤษอยู่ นี่อาจก่อให้เกิดปัญหาได้",
"dialogOk": "ตกลง",
"dialogNo": "ไม่",
"serverEnableDialogTitle": "คุณพบแลนเชอร์ของเซิร์ฟเวอร์ Grasscutter",
"serverEnableDialogText": "หากคุณยังไม่ได้ติดตั้งเซิร์ฟเวอร์ Grasscutter คุณต้องการจะดาวน์โหลดหรือไม่"
}

View File

@ -3,45 +3,45 @@
"appName": "GrassClipper", "appName": "GrassClipper",
"playOfficial": "Máy chủ chính thức", "playOfficial": "Máy chủ chính thức",
"playPrivate": "Máy chủ thứ 3", "playPrivate": "Máy chủ riêng",
"launchLocalServer": "Khởi động Grasscutter", "launchLocalServer": "Khởi động máy chủ cục bộ",
"gameExeSet": "Chọn file \"GenshinImpact.exe\"", "gameExeSet": "Chọn tệp \"GenshinImpact.exe\"",
"grasscutterFileSet": "Chọn file \"Grasscutter.jar\"", "grasscutterFileSet": "Chọn tệp \"Grasscutter.jar\"",
"folderNotSet": "Chưa chọn file", "folderNotSet": "Chưa chọn tệp",
"ipPlaceholder": "Địa chỉ", "ipPlaceholder": "Địa chỉ máy chủ",
"portPlaceholder": "Port", "portPlaceholder": "Cổng",
"noFavorites": "Không có máy chủ yêu thích", "noFavorites": "Không có máy chủ yêu thích",
"settingsTitle": "Cài đặt", "settingsTitle": "Cài đặt",
"scriptsSectionTitle": "Scripts", "scriptsSectionTitle": "Scripts",
"killswitchOption": "Công tắc đóng", "killswitchOption": "Thoát khẩn cấp",
"killswitchSubtitle": "Dành cho những ai sợ bị ban. Tắt game *và mạng* nếu proxy có vấn đề.", "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", "proxyOption": "Proxy",
"proxySubtitle": "Dùng để kết nối vào máy chủ thứ 3", "proxySubtitle": "Dùng để kết nối vào máy chủ riêng",
"updateOption": "Cập nhật", "updateOption": "Cập nhật",
"updateSubtitle": "Tạm thời chưa dùng được. Kiểm tra Github để biết thêm chi tiế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ữ", "languageOption": "Ngôn ngữ",
"languageSubtitle": " ", "languageSubtitle": "Thay đổi ngôn ngữ",
"enableServerLauncherOption": "Grasscutter", "enableServerLauncherOption": "Grasscutter",
"enableServerLauncherSubtitle": "Thêm lựa chọn bật máy chủ grasscutter", "enableServerLauncherSubtitle": "Thêm lựa chọn bật máy chủ grasscutter",
"httpsOption": "Dùng HTTPS", "httpsOption": "Dùng HTTPS",
"httpsSubtitle": " ", "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!", "introSen1": "Hình như đây là lần đầu tiên bạn dùng ứng dụng này!",
"introSen2": "Trước hết, chào bạn, cảm ơn bạn đã sử dụng! :)", "introSen2": "Trước tiên xin chào và cảm ơn bạn đã sử dụng! :)",
"introSen3": "Bạn có muốn tải proxy không?", "introSen3": "Bạn có muốn cài đặt ngay proxy không?",
"introSen4": "(dùng để vào máy chủ riêng)", "introSen4": "(cần thiết để vào máy chủ riêng)",
"updateBtn": "Cập nhật", "updateBtn": "Cập nhật",
"proxyInstallBtn": "Tải", "proxyInstallBtn": "Cài đặt",
"proxyInstallDeny": "Không", "proxyInstallDeny": "Huỷ bỏ",
"gameFolderDialog": "Chọn GenshinImpact.exe", "gameFolderDialog": "Chọn tệp GenshinImpact.exe",
"grasscutterFileDialog": "Chọn Grasscutter.jar", "grasscutterFileDialog": "Chọn tệp Grasscutter.jar",
"loggingInTo": "Đang đăng nhập với tên: ", "loggingInTo": "Đang đăng nhập với tên: ",
"registeringFor": ăng ký tài khoản: ", "registeringFor": ang đăng ký tài khoản: ",
"authUsername": "Tài khoản: ", "authUsername": "Tài khoản: ",
"authPassword": "Mật khẩu: ", "authPassword": "Mật khẩu: ",
"authConfirmPassword": "Xác nhận lại mật khẩu: ", "authConfirmPassword": "Xác nhận lại mật khẩu: ",
@ -49,16 +49,42 @@
"authRegisterBtn": "Đăng ký", "authRegisterBtn": "Đăng ký",
"authLoginTitle": "Đăng nhập", "authLoginTitle": "Đăng nhập",
"authRegisterTitle": "Đăng ký", "authRegisterTitle": "Đăng ký",
"launchWithoutAuth": "Sử dụng mà không cần đăng nhập", "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", "alertInvalid": "Tài khoản hoặc mật khẩu không đúng",
"alertNoPass": "Tài khoản này chưa có mật khẩu.", "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 host", "alertUnknown": "Lỗi không xác định, báo cáo với người quản lý máy chủ",
"alertAuthNoLogin": "Hệ thống đăng nhập đã bị tắt, không cần đăng nhập", "alertAuthNoLogin": "Đã vô hiệu hoá hệ thống xác thực, không cần đăng nhập.",
"alertLoginSuccess": ã đăng nhập và tự động sao chép token. Dán token vào phần tài khoản trong game để đă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", "alertUserTaken": "Tên tài khoản đã tồn tại",
"alertPassMismatch": "Mật khẩu xác thực không đúng với mật khẩu ban đầu", "alertPassMismatch": "Mật khẩu xác thực không trùng khớp",
"alertAuthNoRegister": "Hệ thống đăng nhập đã bị tắt, không cần đăng ký", "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!" "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: ",
"foreignCharacterAlert": "Đườ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ề?"
} }

View File

@ -81,5 +81,11 @@
"updateNotifText": "有新的GrassClipper更新可用 最新版本: ", "updateNotifText": "有新的GrassClipper更新可用 最新版本: ",
"chineseCharacterAlert": "此路徑含有中文字體,這可能會導致問題的發生。" "foreignCharacterAlert": "此路徑含有中文字體,這可能會導致問題的發生。",
"dialogOk": "好的",
"dialogNo": "不要",
"serverEnableDialogTitle": "您找到了Grasscutter本地伺服器啟動器",
"serverEnableDialogText": "如果你現在還沒有把Grasscutter安裝在電腦上面的話要不要直接從網路上下載"
} }

View File

@ -80,5 +80,5 @@
"updateNotifText": "有新版本可用!当前最新的版本是: ", "updateNotifText": "有新版本可用!当前最新的版本是: ",
"chineseCharacterAlert": "当前文件路径含有中文字符,可能会导致一些问题。" "foreignCharacterAlert": "当前文件路径含有中文字符,可能会导致一些问题。"
} }

View File

@ -1,5 +1,5 @@
{ {
"applicationId": "js.grassclipper.app", "applicationId": "js.grassclipper.app",
"version": "0.9.4", "version": "0.9.9",
"resourcesURL": "https://github.com/Grasscutters/GrassClipper/releases/latest/download/resources.neu" "resourcesURL": "https://github.com/Grasscutters/GrassClipper/releases/latest/download/resources.neu"
} }

View File

@ -1,6 +1,6 @@
{ {
"applicationId": "js.grassclipper.app", "applicationId": "js.grassclipper.app",
"version": "0.9.4", "version": "0.9.9",
"defaultMode": "window", "defaultMode": "window",
"port": 0, "port": 0,
"documentRoot": "/resources/", "documentRoot": "/resources/",

View File

@ -1,6 +1,6 @@
{ {
"name": "grassclipper", "name": "grassclipper",
"version": "0.9.4", "version": "0.9.9",
"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",

View File

@ -85,7 +85,8 @@ class MlgmXyysd_Anime_Game_Proxy:
"uspider.yuanshen.com", "uspider.yuanshen.com",
"sdk-static.mihoyo.com", "sdk-static.mihoyo.com",
"abtest-api-data-sg.hoyoverse.com", "abtest-api-data-sg.hoyoverse.com",
"log-upload-os.hoyoverse.com" "log-upload-os.hoyoverse.com",
"webapi-os.account.hoyoverse.com"
] ]
if flow.request.host in LIST_DOMAINS: if flow.request.host in LIST_DOMAINS:

View File

@ -2,12 +2,14 @@
<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/axios.min.js"></script>
<script src="js/neutralino.js"></script> <script src="js/neutralino.js"></script>
<script src="js/init.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/helpers.js"></script>
<script src="js/index.js"></script> <script src="js/index.js"></script>
<script src="js/debug.js"></script>
<script src="js/gcdownloader.js"></script> <script src="js/gcdownloader.js"></script>
<script src="js/onLoad.js"></script> <script src="js/onLoad.js"></script>
<script src="js/alerts.js"></script> <script src="js/alerts.js"></script>
@ -175,12 +177,21 @@
<div class="downloadRow"> <div class="downloadRow">
<div class="downloadSection"> <div class="downloadSection">
<span class="downloadLabel" id="downloadResources">Download Grasscutter Resources</span> <span class="downloadLabel" id="downloadResources">Download Grasscutter Resources</span>
<button class="smolBtn" onclick="downloadGC('development')" id="resourceInstall">Download</button> <button class="smolBtn" onclick="downloadResources()" id="resourceInstall">Download</button>
</div> </div>
<span class="downloadSubtitle" id="resourceSubtitle"> <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. Downloads Grasscutter resources into the currently set Grasscutter folder. This should be done unless you plan on getting resources externally.
</span> </span>
</div> </div>
<div class="downloadRow">
<div class="downloadSection">
<span class="downloadLabel" id="downloadData">Download Data Files</span>
<button class="smolBtn" onclick="downloadDataFiles()" id="dataInstall">Download</button>
</div>
<span class="downloadSubtitle" id="dataSubtitle">
Downloads Grasscutter data files, such as keys, spawns, and other vital files.
</span>
</div>
</div> </div>
</div> </div>
@ -252,6 +263,13 @@
Choose between using HTTPS or HTTP. Choose between using HTTPS or HTTP.
</span> </span>
</div> </div>
<div class="settingsRow">
<div class="settingSection">
<span class="settingLabel", id="debugTitle">Debugging</span>
<input type="checkbox" id="debugOption" onchange="toggleDebugging()" />
</select>
</div>
</div>
</div> </div>
</div> </div>

View File

@ -6,6 +6,8 @@ function displayAlert(message, clear = 4000) {
alertText.innerText = message alertText.innerText = message
debug.log('Displaying message: ' + message)
setTimeout(() => { setTimeout(() => {
hideAlert() hideAlert()
}, clear) }, clear)
@ -15,5 +17,7 @@ function hideAlert() {
const alert = document.getElementById('alert') const alert = document.getElementById('alert')
const alertText = document.getElementById('alertText') const alertText = document.getElementById('alertText')
debug.log('Hiding alert')
alert.classList.remove('show') alert.classList.remove('show')
} }

View File

@ -2,10 +2,12 @@ let alertTimeout, alertCooldown = 3000
async function displayLoginAlert(message, type, cooldown = null) { async function displayLoginAlert(message, type, cooldown = null) {
displayAuthAlert(message, type, cooldown, 'login') displayAuthAlert(message, type, cooldown, 'login')
debug.log('Login alert: ' + message)
} }
async function displayRegisterAlert(message, type, cooldown = null) { async function displayRegisterAlert(message, type, cooldown = null) {
displayAuthAlert(message, type, cooldown, 'register') displayAuthAlert(message, type, cooldown, 'register')
debug.log('Register alert: ' + message)
} }
function displayAuthAlert(message, type, cooldown, name) { function displayAuthAlert(message, type, cooldown, name) {
@ -41,5 +43,7 @@ function displayAuthAlert(message, type, cooldown, name) {
// Disappear after cooldown // Disappear after cooldown
alertTimeout = setTimeout(() => { alertTimeout = setTimeout(() => {
elm.style.display = 'none' elm.style.display = 'none'
debug.log('Hiding auth alert')
}, cooldown || alertCooldown) }, cooldown || alertCooldown)
} }

3
resources/js/axios.min.js vendored Normal file

File diff suppressed because one or more lines are too long

17
resources/js/debug.js Normal file
View File

@ -0,0 +1,17 @@
const debug = {
log: async (...args) => {
const cfg = await getCfg()
if (cfg.debug) console.log('[DEBUG] ', ...args)
},
warn: async (...args) => {
const cfg = await getCfg()
if (cfg.debug) console.log('[WARNING] ', ...args)
},
error: async (...args) => {
const cfg = await getCfg()
if (cfg.debug) console.log('[ERROR] ', ...args)
}
}

View File

@ -1,13 +1,8 @@
async function clearGCInstallation() {
Neutralino.os.execCommand('del /s /q "./gc"')
}
async function setDownloadButtonsToLoading() { async function setDownloadButtonsToLoading() {
const stableBtn = document.querySelector('#stableInstall') const stableBtn = document.querySelector('#stableInstall')
const devBtn = document.querySelector('#devInstall') const devBtn = document.querySelector('#devInstall')
stableBtn.innerText = localeObj.gcScriptRunning || 'Running...' stableBtn.innerText = localeObj.gcScriptRunning || 'Running...'
devBtn.innerText = localeObj.gcScriptRunning || 'Running...' devBtn.innerText = localeObj.gcScriptRunning || 'Running...'
// Set btns to disabled // Set btns to disabled
@ -16,6 +11,8 @@ async function setDownloadButtonsToLoading() {
devBtn.disabled = true devBtn.disabled = true
devBtn.classList.add('disabled') devBtn.classList.add('disabled')
debug.log('Set download buttons to loading')
} }
async function resetDownloadButtons() { async function resetDownloadButtons() {
@ -31,75 +28,122 @@ async function resetDownloadButtons() {
devBtn.disabled = false devBtn.disabled = false
devBtn.classList.remove('disabled') devBtn.classList.remove('disabled')
debug.log('Reset download buttons')
} }
async function downloadGC(branch) { async function downloadDataFiles(branch) {
const config = await getCfg() const config = await getCfg()
// If we are pulling from a new branch, delete the old installation if (!branch) {
if (config.grasscutterBranch !== branch) await clearGCInstallation() debug.warn('Branch not specified')
branch = config.grasscutterBranch || 'development'
}
// Set current installation in config debug.log('Using branch: ' + branch)
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() 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 // For data files
const dataFiles = await axios.get(`https://api.github.com/repos/Grasscutters/Grasscutter/contents/data?ref=${branch}`) const dataFiles = await axios.get(`https://api.github.com/repos/Grasscutters/Grasscutter/contents/data?ref=${branch}`)
const dataList = dataFiles.data const dataList = dataFiles.data
.map(file => ({ path: file.path, filename: file.name })) .map(file => ({ path: file.path, filename: file.name }))
.map(o => ({ url: `https://raw.githubusercontent.com/Grasscutters/Grasscutter/${branch}/${o.path}`, filename: o.filename })) .map(o => ({ url: `https://raw.githubusercontent.com/Grasscutters/Grasscutter/${branch}/${o.path}`, filename: o.filename }))
debug.log('Downloaded data files')
// For key files // For key files
const keyFiles = await axios.get(`https://api.github.com/repos/Grasscutters/Grasscutter/contents/keys?ref=${branch}`) const keyFiles = await axios.get(`https://api.github.com/repos/Grasscutters/Grasscutter/contents/keys?ref=${branch}`)
const keyList = keyFiles.data const keyList = keyFiles.data
.map(file => ({ path: file.path, filename: file.name })) .map(file => ({ path: file.path, filename: file.name }))
.map(o => ({ url: `https://raw.githubusercontent.com/Grasscutters/Grasscutter/${branch}/${o.path}`, filename: o.filename })) .map(o => ({ url: `https://raw.githubusercontent.com/Grasscutters/Grasscutter/${branch}/${o.path}`, filename: o.filename }))
debug.log('Downloaded key files')
const serverFolderFixed = config.serverFolder.match(/.*\\|.*\//g, '')[0].replace(/\//g, '\\') const serverFolderFixed = config.serverFolder.match(/.*\\|.*\//g, '')[0].replace(/\//g, '\\')
// Ensure data and key folders exist debug.log('Server folder fixed: ' + serverFolderFixed)
console.log(config.serverFolder)
console.log(serverFolderFixed)
await Neutralino.os.execCommand(`mkdir ${serverFolderFixed}\\data`) await Neutralino.os.execCommand(`mkdir ${serverFolderFixed}\\data`)
await Neutralino.os.execCommand(`mkdir ${serverFolderFixed}\\keys`) await Neutralino.os.execCommand(`mkdir ${serverFolderFixed}\\keys`)
debug.log('Created data and keys folders')
// Download data files // Download data files
for (const o of dataList) { for (const o of dataList) {
const folder = 'data' const folder = 'data'
await Neutralino.os.execCommand(`powershell Invoke-WebRequest -Uri ${o.url} -OutFile "${serverFolderFixed}\\${folder}\\${o.filename}"`) const e = await Neutralino.os.execCommand(`powershell Invoke-WebRequest -Uri ${o.url} -OutFile "${serverFolderFixed}\\${folder}\\${o.filename}"`)
debug.log(e)
} }
// Download key files // Download key files
for (const o of keyList) { for (const o of keyList) {
const folder = 'keys' const folder = 'keys'
await Neutralino.os.execCommand(`powershell Invoke-WebRequest -Uri ${o.url} -OutFile "${serverFolderFixed}\\${folder}\\${o.filename}"`) const e = await Neutralino.os.execCommand(`powershell Invoke-WebRequest -Uri ${o.url} -OutFile "${serverFolderFixed}\\${folder}\\${o.filename}"`)
debug.log(e)
} }
// Fix buttons
resetDownloadButtons()
}
async function downloadGC(branch) {
const config = await getCfg()
// Set current installation in config
config.grasscutterBranch = branch
debug.log('Branch set to: ' + branch)
// Set gc path for people with launcher enabled
config.serverFolder = `${NL_CWD}\\gc-${branch}\\grasscutter.jar`
debug.log('Server folder automatically set to: ' + config.serverFolder)
// Enable server launcher
config.serverLaunchPanel = true
Neutralino.storage.setData('config', JSON.stringify(config))
// Download data files
downloadDataFiles(branch)
// External service that allows un-authed artifact downloading
let artiUrl = `https://nightly.link/Grasscutters/Grasscutter/workflows/build/${branch}/Grasscutter.zip`
await axios.get(artiUrl).catch(e => {
// Fallback link if artifacts are not being uploaded
debug.warn('Artifacts not available for latest, falling back...')
artiUrl = 'https://nightly.link/Grasscutters/Grasscutter/actions/runs/2284467925/Grasscutter.zip'
})
debug.log('Artifact URL: ' + artiUrl)
// Keystore for branch (since they can differ)
const keystoreUrl = `https://github.com/Grasscutters/Grasscutter/raw/${branch}/keystore.p12`
// Run installer // Run installer
createCmdWindow(`.\\scripts\\gc_download.cmd ${artiUrl} ${keystoreUrl} ${branch}`) createCmdWindow(`.\\scripts\\gc_download.cmd ${artiUrl} ${keystoreUrl} ${branch}`)
// Fix buttons debug.log('Created installer window')
resetDownloadButtons()
// Display folder after saving config // Display folder after saving config
displayServerFolder() displayServerFolder()
enableServerButton() enableServerButton()
displayServerLaunchSection() displayServerLaunchSection()
} }
async function downloadResources() {
const config = await getCfg()
let serverFolderFixed = config.serverFolder.match(/.*\\|.*\//g, '')[0].replace(/\//g, '\\')
// Remove trailing slash, it's important here
serverFolderFixed = serverFolderFixed.endsWith('\\') ? serverFolderFixed.slice(0, -1) : serverFolderFixed
debug.log('Server folder fixed: ' + serverFolderFixed)
// Dont bother with data or keys, just want straight resources
createCmdWindow(`.\\scripts\\resources_download.cmd "${serverFolderFixed}"`)
debug.log('Created resources window')
}

View File

@ -12,6 +12,7 @@ async function getCfg() {
serverLaunchPanel: false, serverLaunchPanel: false,
language: 'en', language: 'en',
useHttps: true, useHttps: true,
debug: true,
grasscutterBranch: '', grasscutterBranch: '',
} }
const cfgStr = await Neutralino.storage.getData('config').catch(e => { const cfgStr = await Neutralino.storage.getData('config').catch(e => {
@ -20,6 +21,8 @@ async function getCfg() {
// Show the first time notice if there is no config // Show the first time notice if there is no config
document.querySelector('#firstTimeNotice').style.display = 'block' document.querySelector('#firstTimeNotice').style.display = 'block'
debug.warn('First time opening')
}) })
const config = cfgStr ? JSON.parse(cfgStr) : defaultConf const config = cfgStr ? JSON.parse(cfgStr) : defaultConf
@ -36,8 +39,12 @@ async function getFavIps() {
const ipStr = await Neutralino.storage.getData('favorites').catch(e => { const ipStr = await Neutralino.storage.getData('favorites').catch(e => {
// The data isn't set, so this is our first time opening // The data isn't set, so this is our first time opening
Neutralino.storage.setData('favorites', JSON.stringify([])) Neutralino.storage.setData('favorites', JSON.stringify([]))
debug.warn('No favorites set')
}) })
debug.log('Favorites:', ipStr)
const ipArr = ipStr ? JSON.parse(ipStr) : [] const ipArr = ipStr ? JSON.parse(ipStr) : []
return ipArr return ipArr
@ -50,20 +57,27 @@ async function proxyIsInstalled() {
if (curDirList.find(f => f.entry === 'ext')) { if (curDirList.find(f => f.entry === 'ext')) {
const extFiles = await filesystem.readDirectory(NL_CWD + '/ext') const extFiles = await filesystem.readDirectory(NL_CWD + '/ext')
debug.log('ext/ folder exists')
if (extFiles.find(f => f.entry === 'mitmdump.exe')) { if (extFiles.find(f => f.entry === 'mitmdump.exe')) {
debug.log('mitmdump exists')
return true return true
} }
} }
debug.log('No proxy installed')
return false return false
} }
async function checkForUpdates() { async function checkForUpdates() {
const url = 'https://api.github.com/repos/Grasscutters/GrassClipper/releases/latest' const url = 'https://api.github.com/repos/Grasscutters/GrassClipper/releases/latest'
const { data } = await axios.get(url) const { data } = await axios.get(url).catch(e => debug.error('Error getting latest release'))
const latest = data.tag_name const latest = data.tag_name
debug.log('Latest release:', latest)
return latest return latest
} }
@ -72,8 +86,12 @@ async function displayUpdate() {
const versionDisplay = document.querySelector('#newestVersion') const versionDisplay = document.querySelector('#newestVersion')
const notif = document.querySelector('#downloadNotif') const notif = document.querySelector('#downloadNotif')
debug.log('Comparing versions: ' + latest + ' vs v' + NL_APPVERSION)
if (latest === `v${NL_APPVERSION}`) return if (latest === `v${NL_APPVERSION}`) return
debug.log('New version available')
versionDisplay.innerText = latest versionDisplay.innerText = latest
notif.classList.add('displayed') notif.classList.add('displayed')
@ -86,34 +104,38 @@ async function displayUpdate() {
async function openLatestDownload() { async function openLatestDownload() {
const downloadLink = 'https://github.com/Grasscutters/GrassClipper/releases/latest/' const downloadLink = 'https://github.com/Grasscutters/GrassClipper/releases/latest/'
debug.log('Opening download link: ', downloadLink)
Neutralino.os.open(downloadLink) Neutralino.os.open(downloadLink)
} }
async function openGameFolder() { async function openGameFolder() {
const config = await getCfg() const config = await getCfg()
const folder = config.gameexe.match(/.*\\|.*\//g, '') const folder = config.gameexe?.match(/.*\\|.*\//g, '')
if (folder.length > 0) openInExplorer(folder[0].replace(/\//g, '\\')) debug.log('Opening game folder: ', folder)
if (folder?.length > 0) openInExplorer(folder[0].replace(/\//g, '\\'))
} }
async function openGrasscutterFolder() { async function openGrasscutterFolder() {
const config = await getCfg() const config = await getCfg()
const folder = config.serverFolder.match(/.*\\|.*\//g, '') const folder = config.serverFolder?.match(/.*\\|.*\//g, '')
if (folder.length > 0) openInExplorer(folder[0].replace(/\//g, '\\')) debug.log('Opening grasscutter folder: ', folder)
if (folder?.length > 0) openInExplorer(folder[0].replace(/\//g, '\\'))
} }
// https://www.jimzhao.us/2015/09/javascript-detect-chinese-character.html // https://www.jimzhao.us/2015/09/javascript-detect-chinese-character.html
function hasChineseChars(str) { function hasForeignChars(str) {
let re1 = new RegExp(/[\u4E00-\uFA29]/) //Chinese character range let re1 = /[^a-z0-9_.,-\\/: ]/g
let re2 = new RegExp(/[\uE7C7-\uE7F3]/) //non Chinese character range
str = str.replace(/\s/g, '') str = str.replace(/\s/g, '')
if (!re1.test(str) || re2.test(str)) { debug.log('Checking for foreign chars in path: ', str)
return false debug.log('Path includes foreign chars? ', re1.test(str))
}
return true return re1.test(str)
} }
function openDialog(title, message, negBtn = false, affirmBtn = closeDialog) { function openDialog(title, message, negBtn = false, affirmBtn = closeDialog) {
@ -123,8 +145,11 @@ function openDialog(title, message, negBtn = false, affirmBtn = closeDialog) {
const noBtn = document.getElementById('dialogButtonNeg') const noBtn = document.getElementById('dialogButtonNeg')
const yesBtn = document.getElementById('dialogButtonAffirm') const yesBtn = document.getElementById('dialogButtonAffirm')
debug.log('Opening dialog: ', title, message)
if (!negBtn) { if (!negBtn) {
noBtn.style.display = 'none' noBtn.style.display = 'none'
debug.log('No "no" button')
} else { } else {
noBtn.style.removeProperty('display') noBtn.style.removeProperty('display')
noBtn.onclick = () => closeDialog() noBtn.onclick = () => closeDialog()
@ -134,6 +159,7 @@ function openDialog(title, message, negBtn = false, affirmBtn = closeDialog) {
noBtn.innerText = localeObj.dialogNo || 'NO' noBtn.innerText = localeObj.dialogNo || 'NO'
yesBtn.onclick = () => { yesBtn.onclick = () => {
debug.log('Affirmative button clicked')
affirmBtn() affirmBtn()
closeDialog() closeDialog()
} }
@ -149,6 +175,8 @@ function openDialog(title, message, negBtn = false, affirmBtn = closeDialog) {
function closeDialog() { function closeDialog() {
const dialog = document.getElementById('miscDialog') const dialog = document.getElementById('miscDialog')
debug.log('Closing dialog')
dialog.style.display = 'none' dialog.style.display = 'none'
} }
@ -156,6 +184,7 @@ function closeDialog() {
* Minimize the window * Minimize the window
*/ */
function minimizeWin() { function minimizeWin() {
debug.log('Minimizing window')
Neutralino.window.minimize() Neutralino.window.minimize()
} }
@ -163,6 +192,7 @@ function minimizeWin() {
* Close the window * Close the window
*/ */
function closeWin() { function closeWin() {
debug.log('Closing window')
Neutralino.app.exit() Neutralino.app.exit()
window.close() window.close()

View File

@ -1,14 +1,14 @@
Neutralino.init()
NL_CWD = NL_CWD.replace(/\//g, '\\') NL_CWD = NL_CWD.replace(/\//g, '\\')
let localeObj let localeObj
const filesystem = Neutralino.filesystem const filesystem = Neutralino.filesystem
const createCmdWindow = async (command) => { const createCmdWindow = async (command) => {
debug.log('Running command in new window: ' + command)
Neutralino.os.execCommand(`cmd.exe /c start "" ${command}`, { background: true }) Neutralino.os.execCommand(`cmd.exe /c start "" ${command}`, { background: true })
} }
const openInExplorer = async (path) => { const openInExplorer = async (path) => {
debug.log('Opening path in explorer: ' + path)
createCmdWindow(`explorer.exe "${path}"`) createCmdWindow(`explorer.exe "${path}"`)
} }
@ -19,6 +19,8 @@ async function enableButtons() {
const offBtn = document.querySelector('#playOfficial') const offBtn = document.querySelector('#playOfficial')
const privBtn = document.querySelector('#playPrivate') const privBtn = document.querySelector('#playPrivate')
debug.log('Enabling buttons')
offBtn.classList.remove('disabled') offBtn.classList.remove('disabled')
offBtn.disabled = false offBtn.disabled = false
@ -35,6 +37,8 @@ async function enableButtons() {
async function enableServerButton() { async function enableServerButton() {
const serverBtn = document.querySelector('#serverLaunch') const serverBtn = document.querySelector('#serverLaunch')
debug.log('Enabling server button')
serverBtn.classList.remove('disabled') serverBtn.classList.remove('disabled')
serverBtn.disabled = false serverBtn.disabled = false
} }
@ -44,7 +48,9 @@ async function enableServerButton() {
*/ */
async function handleGameNotSet() { async function handleGameNotSet() {
// Set buttons to greyed out and disable // Set buttons to greyed out and disable
document.querySelector('#gamePath').innerHTML = localeObj.folderNotSet document.querySelector('#gamePath').innerHTML = localeObj.folderNotSet || 'Not set'
debug.log('Handling game not set')
// 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")'
@ -63,7 +69,9 @@ async function handleGameNotSet() {
async function handleServerNotSet() { async function handleServerNotSet() {
// Set buttons to greyed out and disable // Set buttons to greyed out and disable
document.querySelector('#serverPath').innerHTML = localeObj.folderNotSet document.querySelector('#serverPath').innerHTML = localeObj.folderNotSet || 'Not set'
debug.log('Handling server not set')
// 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")`
@ -81,6 +89,8 @@ async function displayGameFolder() {
const elm = document.querySelector('#gamePath') const elm = document.querySelector('#gamePath')
const config = await getCfg() const config = await getCfg()
debug.log('Displaying game exe')
elm.innerHTML = config.gameexe elm.innerHTML = config.gameexe
} }
@ -91,6 +101,8 @@ async function displayServerFolder() {
const elm = document.querySelector('#serverPath') const elm = document.querySelector('#serverPath')
const config = await getCfg() const config = await getCfg()
debug.log('Displaying server folder')
elm.innerHTML = config.serverFolder elm.innerHTML = config.serverFolder
} }
@ -109,9 +121,13 @@ async function setBackgroundImage() {
// 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")'
debug.log('Setting second panel to: ' + privImage)
// 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}")`
debug.log('Setting third panel to: ' + servImage)
// Set the server background image // Set the server background image
document.querySelector('#thirdPanel').style.backgroundImage = `url("../bg/server/${servImage}")` document.querySelector('#thirdPanel').style.backgroundImage = `url("../bg/server/${servImage}")`
@ -178,6 +194,8 @@ async function handleFavoriteInput() {
const addr = `${ip}:${port}` const addr = `${ip}:${port}`
debug.log('Checking if IP is a favorite: ' + addr)
if (!ip || !ipArr.includes(addr)) { if (!ip || !ipArr.includes(addr)) {
document.querySelector('#star').src = 'icons/star_empty.svg' document.querySelector('#star').src = 'icons/star_empty.svg'
} else { } else {
@ -197,6 +215,8 @@ async function setIp(ip) {
const parseIp = ip.split(':')[0] const parseIp = ip.split(':')[0]
const parsePort = ip.split(':')[1] const parsePort = ip.split(':')[1]
debug.log('Setting IP input elm to: ' + parseIp + ' and port to: ' + parsePort)
// Set star // Set star
if (ip) { if (ip) {
document.querySelector('#star').src = 'icons/star_filled.svg' document.querySelector('#star').src = 'icons/star_filled.svg'
@ -214,6 +234,8 @@ async function handleFavoriteList() {
const ipList = document.querySelector('#ipList') const ipList = document.querySelector('#ipList')
if (ipList.style.display === 'none') { if (ipList.style.display === 'none') {
debug.log('IP list was closed, opening it')
ipList.innerHTML = '' ipList.innerHTML = ''
const list = ipList.appendChild( const list = ipList.appendChild(
@ -221,11 +243,12 @@ async function handleFavoriteList() {
) )
if (ipArr.length < 1) { if (ipArr.length < 1) {
console.log('No favorites found')
const listItem = list.appendChild( const listItem = list.appendChild(
document.createElement('li') document.createElement('li')
) )
listItem.innerHTML = localeObj.noFavorites listItem.innerHTML = localeObj.noFavorites || 'No favorites set'
} }
for (const ip of ipArr) { for (const ip of ipArr) {
@ -241,6 +264,8 @@ async function handleFavoriteList() {
const xy = [ transform.split(',')[4], transform.split(',')[5] ] const xy = [ transform.split(',')[4], transform.split(',')[5] ]
let newY = (27 * ipArr.length) * window.devicePixelRatio let newY = (27 * ipArr.length) * window.devicePixelRatio
debug.log('IP list height: 56vh - ' + newY)
if (ipArr.length === 0 || ipArr.length === 1) newY = 0 if (ipArr.length === 0 || ipArr.length === 1) newY = 0
ipList.style.transform = `translate(${xy[0]}px, calc(56vh - ${newY}px)` ipList.style.transform = `translate(${xy[0]}px, calc(56vh - ${newY}px)`
@ -251,23 +276,37 @@ async function openDownloads() {
const downloads = document.querySelector('#downloadPanel') const downloads = document.querySelector('#downloadPanel')
const config = await getCfg() const config = await getCfg()
debug.log('Opening downloads panel')
if (downloads.style.display === 'none') { if (downloads.style.display === 'none') {
downloads.style.removeProperty('display') downloads.style.removeProperty('display')
} }
// Disable the resource download button if a serverFolder path is not set // Disable the resource download button if a serverFolder path is not set
if (!config.serverFolder) { if (!config.serverFolder) {
debug.log('Server folder not set, disabling resource download button and data download button')
document.querySelector('#resourceInstall').disabled = true document.querySelector('#resourceInstall').disabled = true
document.querySelector('#resourceInstall').classList.add('disabled') document.querySelector('#resourceInstall').classList.add('disabled')
// Disable data installer
document.querySelector('#dataInstall').disabled = true
document.querySelector('#dataInstall').classList.add('disabled')
} else { } else {
debug.log('Server folder is set, enabling resource download button and data download button')
document.querySelector('#resourceInstall').disabled = false document.querySelector('#resourceInstall').disabled = false
document.querySelector('#resourceInstall').classList.remove('disabled') document.querySelector('#resourceInstall').classList.remove('disabled')
// Enable data installer
document.querySelector('#dataInstall').disabled = false
document.querySelector('#dataInstall').classList.remove('disabled')
} }
} }
async function closeDownloads() { async function closeDownloads() {
const downloads = document.querySelector('#downloadPanel') const downloads = document.querySelector('#downloadPanel')
debug.log('Closing downloads panel')
downloads.style.display = 'none' downloads.style.display = 'none'
} }
@ -275,6 +314,8 @@ async function openSettings() {
const settings = document.querySelector('#settingsPanel') const settings = document.querySelector('#settingsPanel')
const config = await getCfg() const config = await getCfg()
debug.log('Opening settings panel')
if (settings.style.display === 'none') { if (settings.style.display === 'none') {
settings.style.removeProperty('display') settings.style.removeProperty('display')
} }
@ -288,6 +329,10 @@ async function openSettings() {
serverLaunch.checked = config.serverLaunchPanel serverLaunch.checked = config.serverLaunchPanel
httpsCheckbox.checked = config.useHttps httpsCheckbox.checked = config.useHttps
debug.log('Set killswitch to: ' + config.enableKillswitch)
debug.log('Set server launch to: ' + config.serverLaunchPanel)
debug.log('Set https to: ' + config.useHttps)
// Load languages // Load languages
getLanguages() getLanguages()
@ -299,10 +344,14 @@ async function closeSettings() {
const settings = document.querySelector('#settingsPanel') const settings = document.querySelector('#settingsPanel')
const config = await getCfg() const config = await getCfg()
debug.log('Closing settings panel')
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.gameexe) { if (await proxyIsInstalled() && config.gameexe) {
debug.log('Proxy has been installed and EXE is set, enabling playPrivate')
const playPriv = document.querySelector('#playPrivate') const playPriv = document.querySelector('#playPrivate')
playPriv.classList.remove('disabled') playPriv.classList.remove('disabled')
@ -321,19 +370,28 @@ async function openLogin() {
const useHttps = config.useHttps const useHttps = config.useHttps
const url = `${useHttps ? 'https' : 'http'}://${ip}:${port}` const url = `${useHttps ? 'https' : 'http'}://${ip}:${port}`
debug.log('Opening login panel')
debug.log('Url: ' + url)
// Check if we even need to authenticate // Check if we even need to authenticate
try { try {
const { data } = await axios.get(url + '/authentication/type') const { data } = await axios.get(url + '/authentication/type')
debug.log('Request successful')
if (!data.includes('GCAuthAuthenticationHandler')) { if (!data.includes('GCAuthAuthenticationHandler')) {
debug.log('No authentication required')
launchPrivate() launchPrivate()
return return
} }
} catch(e) { } catch(e) {
debug.warn('Request failed')
launchPrivate() launchPrivate()
return return
} }
debug.log('Login panel opening')
loginIpDisplay.innerText = ip loginIpDisplay.innerText = ip
registerIpDisplay.innerText = ip registerIpDisplay.innerText = ip
@ -345,6 +403,8 @@ async function openLogin() {
async function closeLogin() { async function closeLogin() {
const login = document.querySelector('#loginPanel') const login = document.querySelector('#loginPanel')
debug.log('Closing login panel')
login.style.display = 'none' login.style.display = 'none'
setLoginSection() setLoginSection()
@ -352,16 +412,22 @@ async function closeLogin() {
async function closeFirstTimePopup() { async function closeFirstTimePopup() {
const firstTimePopup = document.querySelector('#firstTimeNotice') const firstTimePopup = document.querySelector('#firstTimeNotice')
debug.log('Closing first time popup')
firstTimePopup.style.display = 'none' firstTimePopup.style.display = 'none'
} }
async function runInstallScript() { async function runInstallScript() {
debug.log('Running install script')
createCmdWindow(`.\\scripts\\install.cmd "${NL_CWD}" true`) createCmdWindow(`.\\scripts\\install.cmd "${NL_CWD}" true`)
// Create an interval that will check for the proxy server installation finish // Create an interval that will check for the proxy server installation finish
const interval = setInterval(async () => { const interval = setInterval(async () => {
debug.log('Checking if proxy server is installed')
if (await proxyIsInstalled()) { if (await proxyIsInstalled()) {
clearInterval(interval) clearInterval(interval)
debug.log('Proxy server installed')
enableButtons() enableButtons()
} }
}, 1000) }, 1000)
@ -381,9 +447,11 @@ async function checkForUpdatesAndShow() {
// Version mismatch? Update! // Version mismatch? Update!
if (manifest?.version !== NL_APPVERSION) { if (manifest?.version !== NL_APPVERSION) {
debug.log('New update available')
subtitle.innerHTML = 'New update available!' subtitle.innerHTML = 'New update available!'
updateBtn.classList.remove('disabled') updateBtn.classList.remove('disabled')
} else { } else {
debug.log('New update not available')
subtitle.innerHTML = 'You are on the latest version! :)' subtitle.innerHTML = 'You are on the latest version! :)'
updateBtn.classList.add('disabled') updateBtn.classList.add('disabled')
} }
@ -393,10 +461,14 @@ async function displayServerLaunchSection() {
const serverPanel = document.querySelector('#thirdPanel') const serverPanel = document.querySelector('#thirdPanel')
const bottomBtnSection = document.querySelector('#serverPath').parentElement const bottomBtnSection = document.querySelector('#serverPath').parentElement
debug.log('Displaying server launch section')
if (serverPanel.style.display === 'none') { if (serverPanel.style.display === 'none') {
debug.log('Showing server launch section')
serverPanel.style.removeProperty('display') serverPanel.style.removeProperty('display')
bottomBtnSection.style.removeProperty('display') bottomBtnSection.style.removeProperty('display')
} else { } else {
debug.log('Hiding server launch section')
serverPanel.style.display = 'none' serverPanel.style.display = 'none'
bottomBtnSection.style.display = 'none' bottomBtnSection.style.display = 'none'
} }
@ -406,14 +478,16 @@ async function displayServerLaunchSection() {
* Set the game folder by opening a folder picker * Set the game folder by opening a folder picker
*/ */
async function setGameExe() { async function setGameExe() {
const gameExe = await Neutralino.os.showOpenDialog(localeObj.gameFolderDialog, { const gameExe = await Neutralino.os.showOpenDialog(localeObj.gameFolderDialog || 'Select game folder', {
filters: [ filters: [
{ name: 'Executable files', extensions: ['exe'] } { name: 'Executable files', extensions: ['exe'] }
] ]
}) })
debug.log('Game exe selected: ' + gameExe[0])
if (!gameExe[0]) return if (!gameExe[0]) return
if (hasChineseChars(gameExe[0])) displayAlert(localeObj.chineseCharacterAlert) if (hasForeignChars(gameExe[0])) displayAlert(localeObj.foreignCharacterAlert || 'The file path set contains foreign characters, this may cause problems!')
// Set the folder in our configuration // Set the folder in our configuration
const config = await getCfg() const config = await getCfg()
@ -421,6 +495,8 @@ async function setGameExe() {
// It's an array of selections, so only get the first one // It's an array of selections, so only get the first one
config.gameexe = gameExe[0].replace(/\//g, '\\') config.gameexe = gameExe[0].replace(/\//g, '\\')
debug.log('Setting game exe to: ' + config.gameexe)
Neutralino.storage.setData('config', JSON.stringify(config)) Neutralino.storage.setData('config', JSON.stringify(config))
// Refresh background and path // Refresh background and path
@ -430,19 +506,24 @@ async function setGameExe() {
} }
async function setGrasscutterFolder() { async function setGrasscutterFolder() {
const folder = await Neutralino.os.showOpenDialog(localeObj.grasscutterFileDialog, { const folder = await Neutralino.os.showOpenDialog(localeObj?.grasscutterFileDialog || 'Select Grasscutter server jar file', {
filters: [ filters: [
{ name: 'Jar files', extensions: ['jar'] } { name: 'Jar files', extensions: ['jar'] }
] ]
}) })
debug.log('Grasscutter folder selected: ' + folder[0])
if (!folder[0]) return if (!folder[0]) return
if (hasChineseChars(folder[0])) displayAlert(localeObj.chineseCharacterAlert) if (hasForeignChars(folder[0])) displayAlert(localeObj.foreignCharacterAlert || 'The file path set contains foreign characters, this may cause problems!')
// Set the folder in our configuration // Set the folder in our configuration
const config = await getCfg() const config = await getCfg()
config.serverFolder = folder[0] config.serverFolder = folder[0]
debug.log('Setting grasscutter folder to: ' + config.serverFolder)
Neutralino.storage.setData('config', JSON.stringify(config)) Neutralino.storage.setData('config', JSON.stringify(config))
displayServerFolder() displayServerFolder()
@ -455,6 +536,8 @@ async function setGrasscutterFolder() {
async function launchOfficial() { async function launchOfficial() {
const config = await getCfg() const config = await getCfg()
debug.log('Launching game at ' + config.gameexe)
Neutralino.os.execCommand(`"${config.gameexe}"`) Neutralino.os.execCommand(`"${config.gameexe}"`)
} }
@ -467,7 +550,7 @@ async function launchPrivate() {
const config = await getCfg() const config = await getCfg()
console.log('connecting to ' + ip + ':' + port) debug.log('Connecting to ' + ip + ':' + port)
// Set the last connect // Set the last connect
config.lastConnect = ip config.lastConnect = ip
@ -478,11 +561,13 @@ async function launchPrivate() {
`.\\scripts\\private_server_launch.cmd ${ip} ${port} ${config.useHttps} "${config.gameexe}" "${NL_CWD}" ${config.enableKillswitch} true`, { `.\\scripts\\private_server_launch.cmd ${ip} ${port} ${config.useHttps} "${config.gameexe}" "${NL_CWD}" ${config.enableKillswitch} true`, {
background: true background: true
} }
).catch(e => console.log(e)) ).catch(e => debug.error(e))
} }
async function launchLocalServer() { async function launchLocalServer() {
const config = await getCfg() const config = await getCfg()
debug.log('Launching local server')
createCmdWindow(`.\\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))
} }

2
resources/js/init.js Normal file
View File

@ -0,0 +1,2 @@
// This file is the very first loaded when the app is started.
Neutralino.init()

View File

@ -7,6 +7,8 @@ async function setLoginSection() {
const loginSection = document.getElementById('loginPopupContentBody') const loginSection = document.getElementById('loginPopupContentBody')
const registerSection = document.getElementById('registerPopupContentBody') const registerSection = document.getElementById('registerPopupContentBody')
debug.log('Setting to login section')
title.classList.add('selectedTitle') title.classList.add('selectedTitle')
altTitle.classList.remove('selectedTitle') altTitle.classList.remove('selectedTitle')
@ -23,6 +25,8 @@ async function setRegisterSection(fromLogin = false) {
const loginSection = document.getElementById('loginPopupContentBody') const loginSection = document.getElementById('loginPopupContentBody')
const registerSection = document.getElementById('registerPopupContentBody') const registerSection = document.getElementById('registerPopupContentBody')
debug.log('Setting to register section')
title.classList.add('selectedTitle') title.classList.add('selectedTitle')
altTitle.classList.remove('selectedTitle') altTitle.classList.remove('selectedTitle')
@ -30,6 +34,8 @@ async function setRegisterSection(fromLogin = false) {
registerSection.style.removeProperty('display') registerSection.style.removeProperty('display')
if (fromLogin) { if (fromLogin) {
debug.log('Just registered, setting to login page')
// Take the values from the login section and put them in the register section // Take the values from the login section and put them in the register section
const loginUsername = document.getElementById('loginUsername').value const loginUsername = document.getElementById('loginUsername').value
const loginPassword = document.getElementById('loginPassword').value const loginPassword = document.getElementById('loginPassword').value
@ -42,6 +48,9 @@ async function setRegisterSection(fromLogin = false) {
function parseJwt(token) { function parseJwt(token) {
const base64Url = token.split('.')[1] const base64Url = token.split('.')[1]
const base64 = base64Url.replace('-', '+').replace('_', '/') const base64 = base64Url.replace('-', '+').replace('_', '/')
debug.log('Parsed JWT: ' + base64)
return JSON.parse(window.atob(base64)) return JSON.parse(window.atob(base64))
} }
@ -57,12 +66,14 @@ async function login() {
const useHttps = config.useHttps const useHttps = config.useHttps
const url = `${useHttps ? 'https' : 'http'}://${ip}:${port}` const url = `${useHttps ? 'https' : 'http'}://${ip}:${port}`
debug.log('Attempting login to ' + url)
const reqBody = { const reqBody = {
username, username,
password, password,
} }
const { data } = await axios.post(url + '/authentication/login', reqBody) const { data } = await axios.post(url + '/authentication/login', reqBody).catch(e => debug.error('Login request failed: ' + e))
switch(data.message) { switch(data.message) {
case 'INVALID_ACCOUNT': case 'INVALID_ACCOUNT':
@ -92,6 +103,8 @@ async function login() {
const tkData = parseJwt(data.jwt) const tkData = parseJwt(data.jwt)
await Neutralino.clipboard.writeText(tkData.token) await Neutralino.clipboard.writeText(tkData.token)
debug.log('Login success')
displayLoginAlert(localeObj.alertLoginSuccess || 'Login successful! Token copied to clipboard. Paste this token into the username field of the game to log in.', 'success', 8000) 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() launchPrivate()
break break
@ -111,13 +124,15 @@ async function register() {
const useHttps = config.useHttps const useHttps = config.useHttps
const url = `${useHttps ? 'https' : 'http'}://${ip}:${port}` const url = `${useHttps ? 'https' : 'http'}://${ip}:${port}`
debug.log('Attempting registration to ' + url)
const reqBody = { const reqBody = {
username, username,
password, password,
password_confirmation password_confirmation
} }
const { data } = await axios.post(url + '/authentication/register', reqBody) const { data } = await axios.post(url + '/authentication/register', reqBody).catch(e => debug.error('Registration request failed: ' + e))
switch(data.message) { switch(data.message) {
case 'USERNAME_TAKEN': case 'USERNAME_TAKEN':
@ -147,6 +162,8 @@ async function register() {
const loginUsername = document.getElementById('loginUsername') const loginUsername = document.getElementById('loginUsername')
loginUsername.value = username loginUsername.value = username
debug.log('Registered with username ' + username)
setLoginSection() setLoginSection()
displayLoginAlert(localeObj.alertRegisterSuccess || 'Registration successful!', 'success', 5000) displayLoginAlert(localeObj.alertRegisterSuccess || 'Registration successful!', 'success', 5000)
break break

View File

@ -7,6 +7,8 @@ async function toggleKillSwitch() {
config.enableKillswitch = killSwitch.checked config.enableKillswitch = killSwitch.checked
debug.log('Killswitch is now ', config.enableKillswitch)
Neutralino.storage.setData('config', JSON.stringify(config)) Neutralino.storage.setData('config', JSON.stringify(config))
} }
@ -18,6 +20,8 @@ async function toggleServerLaunchSection() {
displayServerLaunchSection() displayServerLaunchSection()
debug.log('Toggling server panel')
// Save setting // Save setting
config.serverLaunchPanel = !config.serverLaunchPanel config.serverLaunchPanel = !config.serverLaunchPanel
Neutralino.storage.setData('config', JSON.stringify(config)) Neutralino.storage.setData('config', JSON.stringify(config))
@ -26,6 +30,8 @@ async function toggleServerLaunchSection() {
if (config.serverLaunchPanel && !config.serverFolder) { if (config.serverLaunchPanel && !config.serverFolder) {
closeSettings() closeSettings()
debug.log('First time server launcher')
openDialog( openDialog(
localeObj.serverEnableDialogTitle || 'You found the Grasscutter server launcher!', 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?', localeObj.serverEnableDialogText || 'If you do not have an existing Grasscutter installation to set, would you like to download a build?',
@ -42,6 +48,8 @@ async function getLanguages() {
const languageFiles = (await filesystem.readDirectory(`${NL_CWD}/languages`)).filter(file => file.entry.endsWith('.json')) const languageFiles = (await filesystem.readDirectory(`${NL_CWD}/languages`)).filter(file => file.entry.endsWith('.json'))
const config = await getCfg() const config = await getCfg()
debug.log('Grabbing languages')
// Clear language options // Clear language options
const languageSelect = document.querySelector('#languageSelect') const languageSelect = document.querySelector('#languageSelect')
languageSelect.innerHTML = '' languageSelect.innerHTML = ''
@ -51,12 +59,15 @@ async function getLanguages() {
const fullLanguageName = JSON.parse(await filesystem.readFile(`${NL_CWD}/languages/${file.entry}`)).fullLangName const fullLanguageName = JSON.parse(await filesystem.readFile(`${NL_CWD}/languages/${file.entry}`)).fullLangName
const lang = file.entry.split('.json')[0] const lang = file.entry.split('.json')[0]
debug.log('Loading language: ', lang)
const option = document.createElement('option') const option = document.createElement('option')
option.value = lang option.value = lang
option.innerHTML = fullLanguageName option.innerHTML = fullLanguageName
// Set language selected to config language // Set language selected to config language
if (lang === config.language) { if (lang === config.language) {
debug.log('Selected language: ', lang)
option.selected = true option.selected = true
} }
@ -77,6 +88,8 @@ async function handleLanguageChange(elm) {
config.language = list.value config.language = list.value
Neutralino.storage.setData('config', JSON.stringify(config)) Neutralino.storage.setData('config', JSON.stringify(config))
debug.log('Language changed to: ', list.value)
// Force refresh of application, no need for restart! // Force refresh of application, no need for restart!
window.location.reload() window.location.reload()
} }
@ -90,6 +103,22 @@ async function toggleHttps() {
config.useHttps = httpsCheckbox.checked config.useHttps = httpsCheckbox.checked
debug.log('HTTPS set to: ', config.useHttps)
Neutralino.storage.setData('config', JSON.stringify(config))
}
/**
* Toggle debugging
*/
async function toggleDebugging() {
const debugCheckbox = document.querySelector('#debugOption')
const config = await getCfg()
config.debug = debugCheckbox.checked
debug.log('Debugging set to: ', config.debug)
Neutralino.storage.setData('config', JSON.stringify(config)) Neutralino.storage.setData('config', JSON.stringify(config))
} }
@ -105,12 +134,16 @@ async function setFavorite() {
const addr = `${ip}:${port}` const addr = `${ip}:${port}`
debug.log('Handling favorite: ', addr)
// Set star icon // Set star icon
const star = document.querySelector('#star') const star = document.querySelector('#star')
if (star.src.includes('filled') && ip) { if (star.src.includes('filled') && ip) {
star.src = 'icons/star_empty.svg' star.src = 'icons/star_empty.svg'
debug.log('Removing from list: ', addr)
// remove from list // remove from list
ipArr.splice(ipArr.indexOf(addr), 1) ipArr.splice(ipArr.indexOf(addr), 1)
} else { } else {
@ -118,6 +151,7 @@ async function setFavorite() {
// add to list // add to list
if (ip && !ipArr.includes(addr)) { if (ip && !ipArr.includes(addr)) {
debug.log('Adding to list: ', addr)
ipArr.push(addr) ipArr.push(addr)
} }
} }

View File

@ -38,8 +38,8 @@ async function doTranslation() {
set('grasscutterFileSet', 'grasscutterFileSet') set('grasscutterFileSet', 'grasscutterFileSet')
// Private options // Private options
document.querySelector('#ip').placeholder = localeObj.ipPlaceholder document.querySelector('#ip').placeholder = localeObj.ipPlaceholder || 'IP Address'
document.querySelector('#port').placeholder = localeObj.portPlaceholder document.querySelector('#port').placeholder = localeObj.portPlaceholder || 'Port'
// Settings // Settings
set('fullSettingsTitle', 'settingsTitle') set('fullSettingsTitle', 'settingsTitle')
@ -58,6 +58,7 @@ async function doTranslation() {
set('serverSubtitle', 'enableServerLauncherSubtitle') set('serverSubtitle', 'enableServerLauncherSubtitle')
set('httpsTitle', 'httpsOption') set('httpsTitle', 'httpsOption')
set('httpsSubtitle', 'httpsSubtitle') set('httpsSubtitle', 'httpsSubtitle')
set('debugTitle', 'debugOption')
// Intro popup // Intro popup
const popup = document.getElementById('firstTimeNotice') const popup = document.getElementById('firstTimeNotice')
@ -100,6 +101,8 @@ async function doTranslation() {
set('stableSubtitle', 'stableSubtitle') set('stableSubtitle', 'stableSubtitle')
set('downloadDev', 'downloadDev') set('downloadDev', 'downloadDev')
set('devSubtitle', 'devSubtitle') set('devSubtitle', 'devSubtitle')
set('dataSubtitle', 'dataSubtitle')
set('downloadData', 'downloadData')
set('downloadResources', 'downloadResources') set('downloadResources', 'downloadResources')
set('resourceSubtitle', 'resourceSubtitle') set('resourceSubtitle', 'resourceSubtitle')
set('stableInstall', 'stableInstall') set('stableInstall', 'stableInstall')

View File

@ -9,14 +9,7 @@ if %errorlevel%==1 (
echo ======================================================================================= echo =======================================================================================
echo No version of Java was found! echo No version of Java was found!
if %BRANCH% EQU stable ( echo To launch the server, you must install Java 17
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 ======================================================================================= echo =======================================================================================
exit /b exit /b
@ -35,36 +28,13 @@ for /f "delims=. tokens=1-3" %%v in ("%JAVAVER%") do (
set BUILD=%%x set BUILD=%%x
) )
if %BRANCH% EQU stable ( :: Ensure java 17
:: Ensure java 8 if "%MAJOR%" NEQ "17" (
if %MAJOR% EQU 1 ( echo =======================================================================================
if %MINOR% LSS 8 ( echo !! Java version is not 17 !!
echo ======================================================================================= echo Please download Java 17 or later to ensure %BRANCH% branch server launches correctly.
echo !! Java version is less than 8 !! echo =======================================================================================
echo Please download Java 8 to ensure %BRANCH% branch server launches correctly. exit /b
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 echo Java version is compatible

View File

@ -26,4 +26,4 @@ echo Starting local Grasscutter server...
:: Change dir to server directory :: Change dir to server directory
cd /d "%GRASSCUTTER_ROOT%" cd /d "%GRASSCUTTER_ROOT%"
call java -jar %GRASSCUTTER_JAR% call java -jar "%GRASSCUTTER_JAR%"

View File

@ -44,7 +44,7 @@ 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" -k --allow-hosts ".*\.yuanshen\.com|.*\.mihoyo\.com|.*\.hoyoverse\.com" --ssl-insecure --set ip=%IP% --set port=%PORT% --set use_https=%USE_HTTPS% 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%

View File

@ -4,7 +4,7 @@ set FOLDER_NAME=%1
set FOLDER_NAME=%FOLDER_NAME:"=% set FOLDER_NAME=%FOLDER_NAME:"=%
if not exist ".\temp" mkdir ".\temp" if not exist ".\temp" mkdir ".\temp"
if not exist ".\resources" mkdir ".\resources" if not exist "%FOLDER_NAME%\resources" mkdir "%FOLDER_NAME%\resources"
echo Downloading resources, this can take a while... echo Downloading resources, this can take a while...
@ -19,7 +19,7 @@ powershell Expand-Archive -Path ".\temp\resources.zip" -DestinationPath "%FOLDER
:: Delete old resources folder if there is one there :: Delete old resources folder if there is one there
del /s /q "%FOLDER_NAME%\resources">nul del /s /q "%FOLDER_NAME%\resources">nul
echo Moving resources to folder... echo Moving resources to folder (this also takes a bit)...
robocopy "%FOLDER_NAME%\Grasscutter_Resources-main\Resources" "%FOLDER_NAME%\resources" /E /MOVE>nul robocopy "%FOLDER_NAME%\Grasscutter_Resources-main\Resources" "%FOLDER_NAME%\resources" /E /MOVE>nul

View File

@ -3,6 +3,7 @@
set BINARY_URL="https://github.com/SpikeHD/neutralinojs/releases/download/v1337.0.0/neutralino-win_x64.exe" set BINARY_URL="https://github.com/SpikeHD/neutralinojs/releases/download/v1337.0.0/neutralino-win_x64.exe"
call npm install call npm install
call neu update
:: Use powershell to download the binary :: Use powershell to download the binary
powershell Invoke-WebRequest -Uri %BINARY_URL% -OutFile "./bin/neutralino-win_x64.exe" powershell Invoke-WebRequest -Uri %BINARY_URL% -OutFile "./bin/neutralino-win_x64.exe"