Compare commits

..

No commits in common. "main" and "v0.9.4" have entirely different histories.
main ... v0.9.4

36 changed files with 155 additions and 1085 deletions

View File

@ -12,7 +12,6 @@
"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,15 +1,7 @@
# 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/) (Supports Windows 8+) [Download Here!](https://github.com/Grasscutters/GrassClipper/releases/)
*\*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!*
@ -19,7 +11,6 @@ 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)
@ -69,15 +60,6 @@ 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)
@ -100,8 +82,6 @@ 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.
@ -135,8 +115,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 & actuallyeunha * PT-BR - na.na
* VIE - labalityowo & lunaticwhat * VIE - labalityowo
* ID - Iqrar99 & nautilust * ID - Iqrar99 & nautilust
* FR - linsorak & memetrollsXD * FR - linsorak & memetrollsXD
* ES - memetrollsXD * ES - memetrollsXD
@ -145,9 +125,6 @@ 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

View File

@ -1,152 +0,0 @@
# 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)

View File

@ -1,152 +0,0 @@
# 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,7 +28,6 @@
"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! :)",
@ -39,7 +38,7 @@
"proxyInstallBtn": "Install", "proxyInstallBtn": "Install",
"proxyInstallDeny": "No thanks", "proxyInstallDeny": "No thanks",
"gameFolderDialog": "Select game exe", "gameFolderDialog": "Select game folder",
"grasscutterFileDialog": "Select Grasscutter server jar file", "grasscutterFileDialog": "Select Grasscutter server jar file",
"loggingInTo": "Logging in to: ", "loggingInTo": "Logging in to: ",
@ -75,8 +74,6 @@
"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",
@ -84,7 +81,7 @@
"updateNotifText": "A new update is available! Newest version: ", "updateNotifText": "A new update is available! Newest version: ",
"foreignCharacterAlert": "The file path set contains foreign characters, this may cause problems!", "chineseCharacterAlert": "The file path set contains Chinese characters, this may cause problems!",
"dialogOk": "OK", "dialogOk": "OK",
"dialogNo": "NO", "dialogNo": "NO",

View File

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

View File

@ -71,20 +71,13 @@
"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",
"devSubtitle": "Pasang versi pengembangan Grasscutter. Versi ini biasanya mempunyai banyak bug dan sering diperbarui. Gunakan dengan resikomu sendiri.", "downloadSubtitle": "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",
"resourceSubtitle": "Unduh sumber daya Grasscutter ke dalam folder Grasscutter yang sudah diatur. Ini harus dilakukan kecuali kamu berencana untuk mendapatkan sumber daya secara eksternal.", "devSubtitle": "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: ",
"foreignCharacterAlert": "ファイルパスに漢字やひらがななどの全角文字が含まれているため、正常に動作しない場合があります。" "chineseCharacterAlert": "ファイルパスに漢字やひらがななどの全角文字が含まれているため、正常に動作しない場合があります。"
} }

View File

@ -1,93 +0,0 @@
{
"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가 없는 경우, 빌드를 다운로드하시겠습니까?"
}

View File

@ -1,91 +0,0 @@
{
"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,53 +35,5 @@
"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?"
} }

View File

@ -1,95 +0,0 @@
{
"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ủ riêng", "playPrivate": "Máy chủ thứ 3",
"launchLocalServer": "Khởi động máy chủ cục bộ", "launchLocalServer": "Khởi động Grasscutter",
"gameExeSet": "Chọn tệp \"GenshinImpact.exe\"", "gameExeSet": "Chọn file \"GenshinImpact.exe\"",
"grasscutterFileSet": "Chọn tệp \"Grasscutter.jar\"", "grasscutterFileSet": "Chọn file \"Grasscutter.jar\"",
"folderNotSet": "Chưa chọn tệp", "folderNotSet": "Chưa chọn file",
"ipPlaceholder": "Địa chỉ máy chủ", "ipPlaceholder": "Địa chỉ",
"portPlaceholder": "Cổng", "portPlaceholder": "Port",
"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": "Thoát khẩn cấp", "killswitchOption": "Công tắc đóng",
"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.", "killswitchSubtitle": "Dành cho những ai sợ bị ban. Tắt game *và mạng* nếu proxy có vấn đề.",
"proxyOption": "Proxy", "proxyOption": "Proxy",
"proxySubtitle": "Dùng để kết nối vào máy chủ riêng", "proxySubtitle": "Dùng để kết nối vào máy chủ thứ 3",
"updateOption": "Cập nhật", "updateOption": "Cập nhật",
"updateSubtitle": "Tính năng tự động cập nhập tạm thời chưa khả dụng. Kiểm tra Github để biết thêm chi tiết", "updateSubtitle": "Tạm thời chưa dùng được. Kiểm tra Github để biết thêm chi tiết",
"languageOption": "Ngôn ngữ", "languageOption": "Ngôn ngữ",
"languageSubtitle": "Thay đổi ngôn ngữ", "languageSubtitle": " ",
"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": "Thay đổi giao thức giữa HTTPS và HTTP", "httpsSubtitle": " ",
"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 tiên xin chào và cảm ơn bạn đã sử dụng! :)", "introSen2": "Trước hết, chào bạn, cảm ơn bạn đã sử dụng! :)",
"introSen3": "Bạn có muốn cài đặt ngay proxy không?", "introSen3": "Bạn có muốn tải proxy không?",
"introSen4": "(cần thiết để vào máy chủ riêng)", "introSen4": "(dùng để vào máy chủ riêng)",
"updateBtn": "Cập nhật", "updateBtn": "Cập nhật",
"proxyInstallBtn": "Cài đặt", "proxyInstallBtn": "Tải",
"proxyInstallDeny": "Huỷ bỏ", "proxyInstallDeny": "Không",
"gameFolderDialog": "Chọn tệp GenshinImpact.exe", "gameFolderDialog": "Chọn GenshinImpact.exe",
"grasscutterFileDialog": "Chọn tệp Grasscutter.jar", "grasscutterFileDialog": "Chọn Grasscutter.jar",
"loggingInTo": "Đang đăng nhập với tên: ", "loggingInTo": "Đang đăng nhập với tên: ",
"registeringFor": ang đăng ký tài khoản: ", "registeringFor": ă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,42 +49,16 @@
"authRegisterBtn": "Đăng ký", "authRegisterBtn": "Đăng ký",
"authLoginTitle": "Đăng nhập", "authLoginTitle": "Đăng nhập",
"authRegisterTitle": "Đăng ký", "authRegisterTitle": "Đăng ký",
"launchWithoutAuth": "Khởi chạy không cần hệ thống xác thực", "launchWithoutAuth": "Sử dụng mà không cần đăng nhập",
"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": "Chưa có mật khẩu. Vui lòng thiết lập mật khẩu", "alertNoPass": "Tài khoản này chưa có mật khẩu.",
"alertUnknown": "Lỗi không xác định, báo cáo với người quản lý máy chủ", "alertUnknown": "Lỗi không xác định, báo cáo với người host",
"alertAuthNoLogin": "Đã vô hiệu hoá hệ thống xác thực, không cần đăng nhập.", "alertAuthNoLogin": "Hệ thống đăng nhập đã bị tắt, không cần đăng nhập",
"alertLoginSuccess": ăng nhập thành công! Token đã được sao chép vào bộ nhớ tạm. Dán token vào phần tài khoản trong game để đăng nhập.", "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.",
"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 trùng khớp", "alertPassMismatch": "Mật khẩu xác thực không đúng với mật khẩu ban đầu",
"alertAuthNoRegister": "Đã vô hiệu hoá hệ thống xác thực, không cần đăng ký.", "alertAuthNoRegister": "Hệ thống đăng nhập đã bị tắt, 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,11 +81,5 @@
"updateNotifText": "有新的GrassClipper更新可用 最新版本: ", "updateNotifText": "有新的GrassClipper更新可用 最新版本: ",
"foreignCharacterAlert": "此路徑含有中文字體,這可能會導致問題的發生。", "chineseCharacterAlert": "此路徑含有中文字體,這可能會導致問題的發生。"
"dialogOk": "好的",
"dialogNo": "不要",
"serverEnableDialogTitle": "您找到了Grasscutter本地伺服器啟動器",
"serverEnableDialogText": "如果你現在還沒有把Grasscutter安裝在電腦上面的話要不要直接從網路上下載"
} }

View File

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

View File

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

View File

@ -1,6 +1,6 @@
{ {
"applicationId": "js.grassclipper.app", "applicationId": "js.grassclipper.app",
"version": "0.9.9", "version": "0.9.4",
"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.9", "version": "0.9.4",
"repository": "https://github.com/Grasscutters/GrassClipper.git", "repository": "https://github.com/Grasscutters/GrassClipper.git",
"author": "SpikeHD <spikegdofficial@gmail.com>", "author": "SpikeHD <spikegdofficial@gmail.com>",
"license": "Apache-2.0", "license": "Apache-2.0",

View File

@ -85,8 +85,7 @@ 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,14 +2,12 @@
<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="js/axios.min.js"></script> <script src="https://unpkg.com/axios/dist/axios.min.js"></script>
<script src="js/neutralino.js"></script> <script src="js/neutralino.js"></script>
<script src="js/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>
@ -177,21 +175,12 @@
<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="downloadResources()" id="resourceInstall">Download</button> <button class="smolBtn" onclick="downloadGC('development')" 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>
@ -263,13 +252,6 @@
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,8 +6,6 @@ function displayAlert(message, clear = 4000) {
alertText.innerText = message alertText.innerText = message
debug.log('Displaying message: ' + message)
setTimeout(() => { setTimeout(() => {
hideAlert() hideAlert()
}, clear) }, clear)
@ -17,7 +15,5 @@ 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,12 +2,10 @@ 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) {
@ -43,7 +41,5 @@ 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)
} }

File diff suppressed because one or more lines are too long

View File

@ -1,17 +0,0 @@
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,8 +1,13 @@
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
@ -11,8 +16,6 @@ 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() {
@ -28,122 +31,75 @@ async function resetDownloadButtons() {
devBtn.disabled = false devBtn.disabled = false
devBtn.classList.remove('disabled') devBtn.classList.remove('disabled')
debug.log('Reset download buttons')
} }
async function downloadDataFiles(branch) { async function downloadGC(branch) {
const config = await getCfg() const config = await getCfg()
if (!branch) { // If we are pulling from a new branch, delete the old installation
debug.warn('Branch not specified') if (config.grasscutterBranch !== branch) await clearGCInstallation()
branch = config.grasscutterBranch || 'development'
}
debug.log('Using branch: ' + branch) // Set current installation in config
config.grasscutterBranch = branch
// Set gc path for people with launcher enabled
config.serverFolder = `${NL_CWD}\\gc-${branch}\\grasscutter.jar`
// Enable server launcher
config.serverLaunchPanel = true
Neutralino.storage.setData('config', JSON.stringify(config))
setDownloadButtonsToLoading() 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, '\\')
debug.log('Server folder fixed: ' + serverFolderFixed) // Ensure data and key folders exist
console.log(config.serverFolder)
console.log(serverFolderFixed)
await Neutralino.os.execCommand(`mkdir ${serverFolderFixed}\\data`) await Neutralino.os.execCommand(`mkdir ${serverFolderFixed}\\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'
const e = await Neutralino.os.execCommand(`powershell Invoke-WebRequest -Uri ${o.url} -OutFile "${serverFolderFixed}\\${folder}\\${o.filename}"`) 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'
const e = await Neutralino.os.execCommand(`powershell Invoke-WebRequest -Uri ${o.url} -OutFile "${serverFolderFixed}\\${folder}\\${o.filename}"`) 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}`)
debug.log('Created installer window') // Fix buttons
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,7 +12,6 @@ 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 => {
@ -21,8 +20,6 @@ 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
@ -39,12 +36,8 @@ 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
@ -57,27 +50,20 @@ 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).catch(e => debug.error('Error getting latest release')) const { data } = await axios.get(url)
const latest = data.tag_name const latest = data.tag_name
debug.log('Latest release:', latest)
return latest return latest
} }
@ -86,12 +72,8 @@ 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')
@ -104,38 +86,34 @@ 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, '')
debug.log('Opening game folder: ', folder) if (folder.length > 0) openInExplorer(folder[0].replace(/\//g, '\\'))
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, '')
debug.log('Opening grasscutter folder: ', folder) if (folder.length > 0) openInExplorer(folder[0].replace(/\//g, '\\'))
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 hasForeignChars(str) { function hasChineseChars(str) {
let re1 = /[^a-z0-9_.,-\\/: ]/g let re1 = new RegExp(/[\u4E00-\uFA29]/) //Chinese character range
let re2 = new RegExp(/[\uE7C7-\uE7F3]/) //non Chinese character range
str = str.replace(/\s/g, '') str = str.replace(/\s/g, '')
debug.log('Checking for foreign chars in path: ', str) if (!re1.test(str) || re2.test(str)) {
debug.log('Path includes foreign chars? ', re1.test(str)) return false
}
return re1.test(str) return true
} }
function openDialog(title, message, negBtn = false, affirmBtn = closeDialog) { function openDialog(title, message, negBtn = false, affirmBtn = closeDialog) {
@ -145,11 +123,8 @@ 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()
@ -159,7 +134,6 @@ 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()
} }
@ -175,8 +149,6 @@ 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'
} }
@ -184,7 +156,6 @@ function closeDialog() {
* Minimize the window * Minimize the window
*/ */
function minimizeWin() { function minimizeWin() {
debug.log('Minimizing window')
Neutralino.window.minimize() Neutralino.window.minimize()
} }
@ -192,7 +163,6 @@ 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,8 +19,6 @@ 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
@ -37,8 +35,6 @@ 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
} }
@ -48,9 +44,7 @@ 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 || 'Not set' document.querySelector('#gamePath').innerHTML = localeObj.folderNotSet
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")'
@ -69,9 +63,7 @@ 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 || 'Not set' document.querySelector('#serverPath').innerHTML = localeObj.folderNotSet
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")`
@ -89,8 +81,6 @@ 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
} }
@ -101,8 +91,6 @@ 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
} }
@ -121,13 +109,9 @@ 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}")`
@ -194,8 +178,6 @@ 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 {
@ -215,8 +197,6 @@ 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'
@ -234,8 +214,6 @@ 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(
@ -243,12 +221,11 @@ 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 || 'No favorites set' listItem.innerHTML = localeObj.noFavorites
} }
for (const ip of ipArr) { for (const ip of ipArr) {
@ -264,8 +241,6 @@ 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)`
@ -276,37 +251,23 @@ 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'
} }
@ -314,8 +275,6 @@ 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')
} }
@ -329,10 +288,6 @@ 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()
@ -344,14 +299,10 @@ 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')
@ -370,28 +321,19 @@ 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
@ -403,8 +345,6 @@ 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()
@ -412,22 +352,16 @@ 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)
@ -447,11 +381,9 @@ 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')
} }
@ -461,14 +393,10 @@ 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'
} }
@ -478,16 +406,14 @@ 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 || 'Select game folder', { const gameExe = await Neutralino.os.showOpenDialog(localeObj.gameFolderDialog, {
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 (hasForeignChars(gameExe[0])) displayAlert(localeObj.foreignCharacterAlert || 'The file path set contains foreign characters, this may cause problems!') if (hasChineseChars(gameExe[0])) displayAlert(localeObj.chineseCharacterAlert)
// Set the folder in our configuration // Set the folder in our configuration
const config = await getCfg() const config = await getCfg()
@ -495,8 +421,6 @@ 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
@ -506,24 +430,19 @@ async function setGameExe() {
} }
async function setGrasscutterFolder() { async function setGrasscutterFolder() {
const folder = await Neutralino.os.showOpenDialog(localeObj?.grasscutterFileDialog || 'Select Grasscutter server jar file', { const folder = await Neutralino.os.showOpenDialog(localeObj.grasscutterFileDialog, {
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 (hasForeignChars(folder[0])) displayAlert(localeObj.foreignCharacterAlert || 'The file path set contains foreign characters, this may cause problems!') if (hasChineseChars(folder[0])) displayAlert(localeObj.chineseCharacterAlert)
// Set the folder in our configuration // Set the folder in our configuration
const config = await getCfg() const config = await getCfg()
config.serverFolder = folder[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()
@ -536,8 +455,6 @@ 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}"`)
} }
@ -550,7 +467,7 @@ async function launchPrivate() {
const config = await getCfg() const config = await getCfg()
debug.log('Connecting to ' + ip + ':' + port) console.log('connecting to ' + ip + ':' + port)
// Set the last connect // Set the last connect
config.lastConnect = ip config.lastConnect = ip
@ -561,13 +478,11 @@ 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 => debug.error(e)) ).catch(e => console.log(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))
} }

View File

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

View File

@ -7,8 +7,6 @@ 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')
@ -25,8 +23,6 @@ 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')
@ -34,8 +30,6 @@ 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
@ -48,9 +42,6 @@ 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))
} }
@ -66,14 +57,12 @@ 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).catch(e => debug.error('Login request failed: ' + e)) const { data } = await axios.post(url + '/authentication/login', reqBody)
switch(data.message) { switch(data.message) {
case 'INVALID_ACCOUNT': case 'INVALID_ACCOUNT':
@ -103,8 +92,6 @@ 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
@ -124,15 +111,13 @@ 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).catch(e => debug.error('Registration request failed: ' + e)) const { data } = await axios.post(url + '/authentication/register', reqBody)
switch(data.message) { switch(data.message) {
case 'USERNAME_TAKEN': case 'USERNAME_TAKEN':
@ -162,8 +147,6 @@ 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,8 +7,6 @@ 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))
} }
@ -20,8 +18,6 @@ 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))
@ -30,8 +26,6 @@ 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?',
@ -48,8 +42,6 @@ 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 = ''
@ -59,15 +51,12 @@ 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
} }
@ -88,8 +77,6 @@ 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()
} }
@ -103,22 +90,6 @@ 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))
} }
@ -134,16 +105,12 @@ 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 {
@ -151,7 +118,6 @@ 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 || 'IP Address' document.querySelector('#ip').placeholder = localeObj.ipPlaceholder
document.querySelector('#port').placeholder = localeObj.portPlaceholder || 'Port' document.querySelector('#port').placeholder = localeObj.portPlaceholder
// Settings // Settings
set('fullSettingsTitle', 'settingsTitle') set('fullSettingsTitle', 'settingsTitle')
@ -58,7 +58,6 @@ 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')
@ -101,8 +100,6 @@ 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,7 +9,14 @@ if %errorlevel%==1 (
echo ======================================================================================= echo =======================================================================================
echo No version of Java was found! echo No version of Java was found!
echo To launch the server, you must install Java 17 if %BRANCH% EQU stable (
echo To launch the stable branch server, you must install Java 8
)
if %BRANCH% EQU development (
echo To launch the development branch server, you must install Java 17
)
echo ======================================================================================= echo =======================================================================================
exit /b exit /b
@ -28,13 +35,36 @@ for /f "delims=. tokens=1-3" %%v in ("%JAVAVER%") do (
set BUILD=%%x set BUILD=%%x
) )
:: Ensure java 17 if %BRANCH% EQU stable (
if "%MAJOR%" NEQ "17" ( :: Ensure java 8
if %MAJOR% EQU 1 (
if %MINOR% LSS 8 (
echo ======================================================================================= echo =======================================================================================
echo !! Java version is not 17 !! echo !! Java version is less than 8 !!
echo Please download Java 17 or later to ensure %BRANCH% branch server launches correctly. echo Please download Java 8 to ensure %BRANCH% branch server launches correctly.
echo ======================================================================================= echo =======================================================================================
exit /b 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 "%FOLDER_NAME%\resources" mkdir "%FOLDER_NAME%\resources" if not exist ".\resources" mkdir ".\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 (this also takes a bit)... echo Moving resources to folder...
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,7 +3,6 @@
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"