39 Commits

Author SHA1 Message Date
175e7f5f09 Format code [skip actions] 2023-08-01 04:23:23 +00:00
665263d9ba Fix account list subcommand 2023-07-31 17:26:30 -04:00
a4ad781d61 Format code [skip actions] 2023-07-31 16:54:59 +00:00
465582b8da Handle Dungeons with null config (#2279) 2023-07-31 12:52:42 -04:00
4320bc7d29 Format code [skip actions] 2023-07-29 15:40:10 +00:00
98fbc4e512 Improve Platforms (#2275)
* Improve Platforms

* add this.
2023-07-29 11:38:34 -04:00
f3277dee9f Fix Forging (#2278) 2023-07-29 11:37:32 -04:00
0005fcf96f Format code [skip actions] 2023-07-23 03:19:10 +00:00
f065b2b16f Format code [skip actions] 2023-07-23 03:18:53 +00:00
e9d68936f6 Format code [skip actions] 2023-07-23 03:18:29 +00:00
47971dc931 various small bug fixes (#2270)
* various small bug fixes

* Upgrade warning to error

* A few more state changes to match the others in this pull
2023-07-22 23:17:59 -04:00
fe169398ec Gadgets can be picked up too (#2272)
* Gadgets can be picked up too

* put in a warning
2023-07-22 23:17:39 -04:00
ddc082fc97 Everything triggers regions (#2271)
* Everything triggers regions

* hotfix
2023-07-22 23:17:33 -04:00
0b806e21cf Fix talks that have a quest id of 4 digits or more (#2268) 2023-07-22 23:17:18 -04:00
49ba0e55e8 make xxGroupVariableValueByGroup the same as xxGroupVariableValue (#2266) 2023-07-22 23:17:11 -04:00
ae2ab20b5b add support for 3.8 teapot binout files (scene 2005, 2204) (#2265) 2023-07-22 23:17:02 -04:00
27d495742d Format code [skip actions] 2023-07-15 03:05:16 +00:00
f17339f1b6 Send global value packet after the scene host has finished initializing the scene 2023-07-14 23:03:49 -04:00
f0775f70f3 Send packet after scene load & Directly send back entity ID to clients 2023-07-14 21:22:45 -04:00
fecf83cfa4 Revert AvatarUpgradeRsp 2023-07-14 21:09:57 -04:00
dfbe4022f3 Merge remote-tracking branch 'origin/development' into development 2023-07-14 19:47:56 -04:00
e859d87ae3 Implement SetEntityServerGlobalValueByEntityId 2023-07-14 19:46:30 -04:00
acb3575c77 update zh-TW.json (#2260) 2023-07-14 19:37:54 -04:00
f725d9203d Fix QUEST_CONTENT_LEAVE_SCENE (#2262) 2023-07-14 19:37:29 -04:00
d62394e35e Remove obsolete code in ScriptLib.java (#2263) 2023-07-14 19:37:08 -04:00
a4d5de06d1 Fix starting wrong quest for story quests (#2264) 2023-07-14 19:36:50 -04:00
73acfa1178 Update README_zh-TW (#2261)
* Update README_zh-TW.md

添加繁體版快速開始

* Update README_zh-TW.md
2023-07-12 22:01:20 -04:00
0d71d29932 fix first entering teapot failure (#2259) 2023-07-12 22:00:55 -04:00
d0a4da8a1f Update languages [skip actions] 2023-07-09 06:04:19 +00:00
d332861ed8 add italian support (italian translation fix) (#2249)
* Updated IT Readme

* Update it-IT.json

* Update it-IT.json (2)

* fixed a couple errors while reviewing
2023-07-09 02:03:54 -04:00
47186a47b2 It now can bind on correct addr (#2254)
Now Grasscutter will bind on address specified in config.json (bindAddress), instead of 0.0.0.0
2023-07-08 12:27:12 -04:00
5090b17b80 Update ru-RU.json (#2247)
ty Scaldy for help♥♥♥
2023-07-03 23:32:15 -04:00
5b9508d7a4 Update French translations (#2246) 2023-07-03 23:30:22 -04:00
df7941bc1b Format code [skip actions] 2023-07-03 11:03:03 +00:00
9cac0953fa Add alternate val name (#2245) 2023-07-03 07:01:50 -04:00
7cdb774b1f Merge remote-tracking branch 'origin/development' into development 2023-07-01 01:29:40 -04:00
f40cdfd23d Update protocol definition for AvatarUpgradeRsp 2023-07-01 01:29:30 -04:00
4e71a15479 Move unknown condition handler message to debug 2023-07-01 01:29:14 -04:00
6afa1bf276 Bump to 1.6.2 & Fix handbook generation error 2023-07-01 01:28:35 -04:00
42 changed files with 2350 additions and 2141 deletions

View File

@ -57,7 +57,7 @@ sourceCompatibility = JavaVersion.VERSION_17
targetCompatibility = JavaVersion.VERSION_17 targetCompatibility = JavaVersion.VERSION_17
group = 'io.grasscutter' group = 'io.grasscutter'
version = '1.6.1' version = '1.6.2'
java { java {
withJavadocJar() withJavadocJar()
@ -335,10 +335,16 @@ tasks.register('generateHandbook') {
return return
} }
// Resolve the NPM command.
var npm = 'npm'
if (Os.isFamily(Os.FAMILY_WINDOWS))
npm = 'npm.cmd'
def npmVersion = { def npmVersion = {
try { try {
return 'npm --version'.execute().text.trim() return "${npm} --version".execute()
} catch (ignored) { } catch (ignored) {
ignored.printStackTrace()
return 'NPM_NOT_FOUND' return 'NPM_NOT_FOUND'
} }
} }
@ -378,10 +384,6 @@ tasks.register('generateHandbook') {
} }
// Build the handbook. // Build the handbook.
var npm = 'npm'
if (Os.isFamily(Os.FAMILY_WINDOWS))
npm = 'npm.cmd'
exec { exec {
workingDir 'src/handbook' workingDir 'src/handbook'
commandLine npm, 'run', 'build' commandLine npm, 'run', 'build'

View File

@ -18,90 +18,57 @@
* Apparition de monstres via la console * Apparition de monstres via la console
* Inventaire (obtention d'objets/de personnages, amélioration d'objets/personnages, etc) * Inventaire (obtention d'objets/de personnages, amélioration d'objets/personnages, etc)
## Guide de démarrage rapide ## Guide d'installation rapide
**Note:** Pour obtenir un support, rejoignez notre serveur [Discord](https://discord.gg/T5vZU6UyeG) (en anglais). **Note:** Pour obtenir un support, rejoignez notre serveur [Discord](https://discord.gg/T5vZU6UyeG) (en anglais).
### Logiciels requis ### Démarage rapide (Automatique)
* Java SE - 17 ([link](https://www.oracle.com/java/technologies/javase/jdk17-archive-downloads.html)) - Téléchargez Java 17: https://www.oracle.com/java/technologies/javase/jdk17-archive-downloads.html
- Téléchargez [MongoDB Community Server](https://www.mongodb.com/try/download/community)
- Téléchargez la version du jeu REL3.7 (Le client de jeut peut être obtenu ici si vous ne l'avez pas): https://github.com/MAnggiarMustofa/GI-Download-Library/blob/main/GenshinImpact/Client/3.7.0.md
**Note:** Si vous voulez juste **l'exécuter**, Alors vous pouvez télécharger seulement le **jre** - Téléchargez la [dernière version de Cultivation](https://github.com/Grasscutters/Cultivation/releases/latest). Ulilisez l'installateur en `.msi`.
- Après avoir ouvert Cultivation (en administrateur), appuyez sur le bouton de téléchargement en haut a droite.
- Cliquez sur le bouton `Téléchargez tout-en-un`
- Cliquez sur l'engrenage dans le coin en haut a droite.
- Définisez l'emplacement d'installation du jeu.
- Définisez le chemin Java personnalisé à `C:\Program Files\Java\jdk-17\bin\java.exe`
- Laissez tous les autres paramètes par défauts
* MongoDB (4.0+ recommandé) - Appuyez sur le bouton a coté de Lancer.
- Appuyez sur le bouton Lancer.
- Connectez vous avec le nom d'utilisateur que vous voulez. Le mot de passe n'a pas d'importance.
* Proxy daemon: mitmproxy (mitmdump, recommended), Fiddler Classic, etc. ### Compilation
### Lancement Grasscutter utilise Gradle pour la gestion des dépendances et la compilation.
**Note:** Si vous avez mis à jour depuis une ancienne version, supprimez `config.json` pour le regénérer. **Prérequis**
1. Obtenez `grasscutter.jar` - [Java SE Development Kits - 17](https://www.oracle.com/java/technologies/javase/jdk17-archive-downloads.html) ou plus récent
- Téléchargez le depuis les [actions](https://github.com/Grasscutters/Grasscutter/suites/6895963598/artifacts/267483297)
- [Buildez le par vous-même](#Building)
2. Créez un dossier `resources` dans le dossier où grasscutter.jar est situé et déplacez vos dossiers `BinOutput` et `ExcelBinOutput` ici *(Vérifiez le [wiki](https://github.com/Grasscutters/Grasscutter/wiki) pour plus de détails sur comment les obtenir.)*
3. Exécutez Grasscutter avec `java -jar grasscutter.jar`. **Soyez sûr que le service MongoDB est en cours d'exécution.**
### Connection avec le client
½. Créez un compte avec la [console de commande du serveur](https://github.com/Grasscutters/Grasscutter/wiki/Commands#targeting).
1. Redirection du traffic: (Choisissez-en un)
- mitmdump: `mitmdump -s proxy.py -k`
Approuvez le certificat CA:
**Note:**Le certificat CA est généralement stocké sous `%USERPROFILE%\ .mitmproxy`, ou vous pouvez le télécharger depuis `http://mitm.it`
Double-cliquez pour [installer](https://docs.microsoft.com/en-us/skype-sdk/sdn/articles/installing-the-trusted-root-certificate#installing-a-trusted-root-certificate) oo ...
- Via la ligne de commande
```shell
certutil -addstore root %USERPROFILE%\.mitmproxy\mitmproxy-ca-cert.cer
```
- Fiddler Classic: Exécutez Fiddler Classic, Activez `Decrypt https traffic` dans les paramètres et changez le port par défaut ici (Tools -> Options -> Connections) à autre chose que `8888`, et chargez [ce script](https://github.lunatic.moe/fiddlerscript).
- [Fichier hosts](https://github.com/Grasscutters/Grasscutter/wiki/Running#traffic-route-map)
2. Définissez le proxy du réseau comme `127.0.0.1:8080` ou le port du proxy que vous avez spécifié.
**Vous pouvez aussi utiliser `start.cmd` to démarrer les serveurs et le proxy automatiquement, mais vous devez mettre en place la variable d'environnement JAVA_HOME**
### Building
Grasscutter utilise Gradle pour gérer les dépendances et la construction.
**Logiciels requis:**
- [Java SE Development Kits - 17](https://www.oracle.com/java/technologies/javase/jdk17-archive-downloads.html)
- [Git](https://git-scm.com/downloads) - [Git](https://git-scm.com/downloads)
##### Windows ##### Windows
```shell ```shell
git clone https://github.com/Grasscutters/Grasscutter.git git clone --recurse-submodules https://github.com/Grasscutters/Grasscutter.git
cd Grasscutter cd Grasscutter
.\gradlew.bat # Mettre en place l'environnement .\gradlew.bat # Setting up environments
.\gradlew jar # Compiler .\gradlew jar # Compile
``` ```
##### Linux ##### Linux (GNU)
```bash ```bash
git clone https://github.com/Grasscutters/Grasscutter.git git clone --recurse-submodules https://github.com/Grasscutters/Grasscutter.git
cd Grasscutter cd Grasscutter
chmod +x gradlew chmod +x gradlew
./gradlew jar # Compiler ./gradlew jar # Compile
``` ```
Vous trouverez le fichier jar compilé à la racine du dossier du projet. Vous pouvez trouver le jar de sortie dans la racine du dossier du projet.
### Les commandes ont été déplacé vers le [wiki](https://github.com/Grasscutters/Grasscutter/wiki/Commands)! (en anglais) ### Dépanage
# Dépannage rapide Pour une liste des problèmes communs et leur solution et pour demander de l'aide, veuillez rejoindre [notre serveur Discord](https://discord.gg/T5vZU6UyeG) (en anglais) et dirigez vous vers le salon de support.
* Si la compilation a échoué, veuillez vérifier votre installation de votre JDK (JDK 17 et le bon dossier bin du JDK dans la variable PATH)
* Mon client ne se connecte pas au serveur, impossible de se connecter a mon compte, 4206, etc... - La plupart du temps, *le problème* vient de la configuration de votre proxy. Si vous utilisez Fiddler, vérifiez s'il est exécuté sur un port autre que 8888
* Séquence de démarrage : MongoDB > Grasscutter > Proxy (mitmdump, fiddler, etc...) > Jeu

View File

@ -5,7 +5,7 @@
[EN](../README.md) | [简中](README_zh-CN.md) | [繁中](README_zh-TW.md) | [FR](README_fr-FR.md) | [ES](README_es-ES.md) | [HE](README_HE.md) | [RU](README_ru-RU.md) | [PL](README_pl-PL.md) | [ID](README_id-ID.md) | [KR](README_ko-KR.md) | [FIL/PH](README_fil-PH.md) | [NL](README_NL.md) | [JP](README_ja-JP.md) | [IT](README_it-IT.md) | [VI](README_vi-VN.md) [EN](../README.md) | [简中](README_zh-CN.md) | [繁中](README_zh-TW.md) | [FR](README_fr-FR.md) | [ES](README_es-ES.md) | [HE](README_HE.md) | [RU](README_ru-RU.md) | [PL](README_pl-PL.md) | [ID](README_id-ID.md) | [KR](README_ko-KR.md) | [FIL/PH](README_fil-PH.md) | [NL](README_NL.md) | [JP](README_ja-JP.md) | [IT](README_it-IT.md) | [VI](README_vi-VN.md)
**Attenzione:** Diamo sempre il benvenuto ai contributori del progetto. Prima di aggiungere il tuo contributo, leggi attentamente il nostro [Codice di condotta](https://github.com/Grasscutters/Grasscutter/blob/stable/CONTRIBUTING.md). **Attenzione:** Diamo sempre il benvenuto ai contributori del progetto. Prima di contribuire, leggi attentamente il nostro [Codice di condotta](https://github.com/Grasscutters/Grasscutter/blob/stable/CONTRIBUTING.md).
## Funzionalità attuali ## Funzionalità attuali
@ -15,12 +15,12 @@
* Teletrasporto * Teletrasporto
* Sistema Gacha * Sistema Gacha
* Cooperativa *parzialmente* funzionale * Cooperativa *parzialmente* funzionale
* Evoca mostri dalla console * Evocazione nemici usando la console
* Funzionalità dell'inventario (ricevi oggetti/personaggi, aggiorna oggetti/personaggi, ecc.) * Inventario (ricevi e aggiorna oggetti/personaggi, ecc.)
## Guida rapida all'installazione ## Guida rapida all'installazione
**Nota:** Per il supporto, unisciti al nostro [Discord](https://discord.gg/T5vZU6UyeG). **Nota:** Se hai bisogno di aiuto, unisciti al nostro [Discord](https://discord.gg/T5vZU6UyeG).
### Requisiti ### Requisiti
@ -30,30 +30,30 @@
* [MongoDB](https://www.mongodb.com/try/download/community) (consigliato 4.0+) * [MongoDB](https://www.mongodb.com/try/download/community) (consigliato 4.0+)
* Servizio proxy: mitmproxy (mitmdump, consigliato), Fiddler Classic, ecc. * Servizi proxy: mitmproxy (mitmdump, consigliato), Fiddler Classic, ecc.
### Esecuzione ### Esecuzione
**Nota:** Se hai eseguito l'aggiornamento da una versione precedente, rimuovi `config.json` in modo che venga generato di nuovo. **Nota:** Se hai aggiornato da una versione precedente, elimina `config.json` in modo che venga generato di nuovo.
1. Ottieni "grasscutter.jar". 1. Ottieni "grasscutter.jar".
- Scarica da [azioni](https://github.com/Grasscutters/Grasscutter/suites/6895963598/artifacts/267483297) - Scarica da [azioni](https://github.com/Grasscutters/Grasscutter/suites/6895963598/artifacts/267483297)
- [Compilalo tu stesso](#Compilazione) - [Compilalo tu stesso](#Compilazione)
2. Crea una cartella `resources` nella directory in cui si trova grasscutter.jar e sposta lì le cartelle `BinOutput` ed `ExcelBinOutput` *(Vedi il [wiki](https://github.com/Grasscutters/Grasscutter/wiki ) per maggiori dettagli su come ottenerli.)* 2. Crea una cartella chiamata `resources` nella directory in cui si trova grasscutter.jar e sposta lì le cartelle `BinOutput` ed `ExcelBinOutput` *(Vedi il [wiki](https://github.com/Grasscutters/Grasscutter/wiki ) per maggiori dettagli su come ottenerli.)*
3. Eseguire Grasscutter con `java -jar grasscutter.jar`. **Assicurati che il servizio mongodb sia attivo.** 3. Esegui Grasscutter con `java -jar grasscutter.jar`. **Assicurati che il servizio mongodb sia attivo.**
### Connessione client ### Connessione al client
½. Crea un account usando [il comando corrispondente nella console del server](https://github.com/Grasscutters/Grasscutter/wiki/Commands#targeting). ½. Crea un account usando [il comando corrispondente nella console del server](https://github.com/Grasscutters/Grasscutter/wiki/Commands#targeting).
1. Reindirizza il traffico: (scegli uno) 1. Reindirizza il traffico: (scegli uno)
- mitmdump: `mitmdump -s proxy.py -k` - Con mitmdump: `mitmdump -s proxy.py -k`
Autorizza il certificato CA: Autorizza il certificato CA:
**Nota:**Il certificato CA si trova solitamente in `%USERPROFILE%\ .mitmproxy`, oppure puoi scaricarlo da `http://mitm.it` **Nota:**Il certificato CA si trova solitamente in `%USERPROFILE%\ .mitmproxy`, oppure puoi scaricarlo da `http://mitm.it`
Fare doppio clic su [installa](https://docs.microsoft.com/en-us/skype-sdk/sdn/articles/installing-the-trusted-root-certificate#installing-a-trusted-root-certificate) o ... Fai doppio clic su [installa](https://docs.microsoft.com/en-us/skype-sdk/sdn/articles/installing-the-trusted-root-certificate#installing-a-trusted-root-certificate) o ...
- Con riga di comando - Con riga di comando
@ -61,11 +61,11 @@
certutil -addstore root %USERPROFILE%\.mitmproxy\mitmproxy-ca-cert.cer certutil -addstore root %USERPROFILE%\.mitmproxy\mitmproxy-ca-cert.cer
``` ```
- Fiddler Classic: esegui Fiddler Classic, abilita `Decrypt https traffic` nelle opzioni e cambia la porta predefinita in (Strumenti -> Opzioni -> Connessioni) in qualcosa di diverso da `8888`, e carica [questo script](https :/ /github.lunatic.moe/fiddlerscript). - Con Fiddler Classic: esegui Fiddler Classic, abilita `Decrypt https traffic` nelle opzioni e cambia la porta predefinita in (Strumenti -> Opzioni -> Connessioni) in qualcosa di diverso da `8888`, e carica [questo script](https://github.lunatic.moe/fiddlerscript).
- [File host](https://github.com/Grasscutters/Grasscutter/wiki/Running#traffic-route-map) - [File host](https://github.com/Grasscutters/Grasscutter/wiki/Running#traffic-route-map)
2. Impostare il proxy di rete su `127.0.0.1:8080` o la porta proxy impostata. 2. Imposta il proxy di rete a `127.0.0.1:8080` o la porta proxy impostata.
**Puoi anche usare `start.cmd` per avviare automaticamente il server e il servizio proxy, ma devi impostare l'ambiente JAVA_HOME** **Puoi anche usare `start.cmd` per avviare automaticamente il server e il servizio proxy, ma devi impostare l'ambiente JAVA_HOME**
@ -98,11 +98,11 @@ chmod +x gradlew
Puoi trovare il jar generato nella cartella principale del progetto. Puoi trovare il jar generato nella cartella principale del progetto.
### I comandi sono stati spostati nel [wiki](https://github.com/Grasscutters/Grasscutter/wiki/Commands)! ### I comandi sono stati spostati al [wiki](https://github.com/Grasscutters/Grasscutter/wiki/Commands)!
# Soluzioni agli errori comuni # Soluzioni agli errori comuni
* Se la compilazione non riesce, controlla l'installazione di JDK (JDK 17 e convalida la variabile JDK bin PATH) * Se la compilazione non riesce, controlla l'installazione di JDK (JDK 17 e convalida la variabile JDK bin PATH)
* Il mio client non si connette, non accede, 4206, ecc... - Probabilmente le tue impostazioni proxy sono *il problema*, se usi * Se il tuo client non si connette, non accede, da errore 4206, ecc... - Probabilmente *il problema* sono le tue impostazioni proxy, se usi
Fiddler assicurati di utilizzare una porta diversa da 8888 Fiddler assicurati di utilizzare una porta diversa da 8888
* Sequenza di avvio: MongoDB > Grasscutter > Servizio proxy (mitmdump, fiddler, ecc.) > Gioco * Sequenza di avvio: MongoDB > Grasscutter > Servizio proxy (mitmdump, fiddler, ecc.) > Gioco

View File

@ -22,52 +22,23 @@
**注意:** 如需幫助請加入 [Discord](https://discord.gg/T5vZU6UyeG) **注意:** 如需幫助請加入 [Discord](https://discord.gg/T5vZU6UyeG)
### 環境需求 ### 快速開始(全自動)
* Java SE - 17 ([連結](https://www.oracle.com/java/technologies/javase/jdk17-archive-downloads.html)) - 下載 Java 17https://www.oracle.com/java/technologies/javase/jdk17-archive-downloads.html
- 下載 [MongoDB 社區伺服器](https://www.mongodb.com/try/download/community)
- 下載遊戲版本 REL3.7(如果你沒有的話,可以在[這裡](https://github.com/MAnggiarMustofa/GI-Download-Library/blob/main/GenshinImpact/Client/3.7.0.md)找到 3.7 客戶端)
**注意:** 如果僅想**執行服務端**, 使用 **jre** 即可 - 下載 [最新的 Cultivation 版本](https://github.com/Grasscutters/Cultivation/releases/latest)。使用 `.msi` 安裝程式。
- 以管理員身分打開 Culivation按右上角的下載按鈕。
- 點擊 `Download All-in-One`
- 點擊右上角的齒輪
- 將遊戲安裝路徑設置為你的遊戲所在的位置。
- 將自定義 Java 路徑設置為 `C:\Program Files\Java\jdk-17\bin\java.exe`
- 其他設置保持預設
* [MongoDB](https://www.mongodb.com/try/download/community) (推薦 4.0+) - 點擊啟動旁邊的小按鈕。
- 點擊啟動按鈕。
* 代理程式: mitmproxy (推薦 mitmdump), Fiddler Classic 等 - 用你想要的用戶名登錄,密碼無所謂。
### 執行
**注意:** 從舊版本升級到新版本, 需要刪除 `config.json`
1. 獲取 `grasscutter.jar`
- 從 [actions](https://github.com/Grasscutters/Grasscutter/suites/6895963598/artifacts/267483297) 下載
- [自行編譯](#編譯)
2. 在 JAR 檔案根目錄中建立 `resources` 資料夾並複製 `BinOutput``ExcelBinOutput` *(查看 [wiki](https://github.com/Grasscutters/Grasscutter/wiki) 瞭解更多)*
3. 命令列 `java -jar grasscutter.jar` 執行 Grasscutter。**在此之前請確認 MongoDB 服務執行正常**
### 客戶端連線
½. 在伺服器控制台[建立賬戶](https://github.com/Grasscutters/Grasscutter/wiki/Commands#targeting)
1. 重定向流量: (選擇其中一個)
- mitmdump: `mitmdump -s proxy.py -k`
信任 CA 證書:
**注意:** mitmproxy 的 CA 證書通常存放在 `%USERPROFILE%\ .mitmproxy`, 或者在 `http://mitm.it` 下載證書
雙擊[安裝根證書](https://docs.microsoft.com/en-us/skype-sdk/sdn/articles/installing-the-trusted-root-certificate#installing-a-trusted-root-certificate)或者...
- 使用命令列
```shell
certutil -addstore root %USERPROFILE%\.mitmproxy\mitmproxy-ca-cert.cer
```
- Fiddler Classic: 執行 Fiddler Classic, 在設定中開啟 `解密 https 通訊` 並將通訊埠設為除 `8888` 以外的任意通訊埠 (工具 -> 選項 -> 連線) 並載入[此指令碼](https://github.lunatic.moe/fiddlerscript)
- [Hosts 檔案](https://github.com/Grasscutters/Grasscutter/wiki/Running#traffic-route-map)
2. 設定代理為 `127.0.0.1:8080` 或你設定的通訊埠
**也可直接執行 `start.cmd` 一鍵啟動服務端並設定代理, 但必須設定 `JAVA_HOME` 環境變數**
### 編譯 ### 編譯

View File

@ -25,23 +25,23 @@ public final class ForgeQueueDataOuterClass {
int getAvatarId(); int getAvatarId();
/** /**
* <code>uint32 unfinish_count = 15;</code> * <code>uint32 finish_count = 15;</code>
* @return The unfinishCount.
*/
int getUnfinishCount();
/**
* <code>uint32 next_finish_timestamp = 13;</code>
* @return The nextFinishTimestamp.
*/
int getNextFinishTimestamp();
/**
* <code>uint32 finish_count = 4;</code>
* @return The finishCount. * @return The finishCount.
*/ */
int getFinishCount(); int getFinishCount();
/**
* <code>uint32 total_finish_timestamp = 13;</code>
* @return The totalFinishTimestamp.
*/
int getTotalFinishTimestamp();
/**
* <code>uint32 queue_id = 4;</code>
* @return The queueId.
*/
int getQueueId();
/** /**
* <code>uint32 forge_id = 1;</code> * <code>uint32 forge_id = 1;</code>
* @return The forgeId. * @return The forgeId.
@ -49,16 +49,16 @@ public final class ForgeQueueDataOuterClass {
int getForgeId(); int getForgeId();
/** /**
* <code>uint32 total_finish_timestamp = 8;</code> * <code>uint32 next_finish_timestamp = 8;</code>
* @return The totalFinishTimestamp. * @return The nextFinishTimestamp.
*/ */
int getTotalFinishTimestamp(); int getNextFinishTimestamp();
/** /**
* <code>uint32 queue_id = 9;</code> * <code>uint32 unfinish_count = 9;</code>
* @return The queueId. * @return The unfinishCount.
*/ */
int getQueueId(); int getUnfinishCount();
} }
/** /**
* <pre> * <pre>
@ -116,22 +116,22 @@ public final class ForgeQueueDataOuterClass {
} }
case 32: { case 32: {
finishCount_ = input.readUInt32(); queueId_ = input.readUInt32();
break; break;
} }
case 64: { case 64: {
totalFinishTimestamp_ = input.readUInt32(); nextFinishTimestamp_ = input.readUInt32();
break; break;
} }
case 72: { case 72: {
queueId_ = input.readUInt32(); unfinishCount_ = input.readUInt32();
break; break;
} }
case 104: { case 104: {
nextFinishTimestamp_ = input.readUInt32(); totalFinishTimestamp_ = input.readUInt32();
break; break;
} }
case 112: { case 112: {
@ -141,7 +141,7 @@ public final class ForgeQueueDataOuterClass {
} }
case 120: { case 120: {
unfinishCount_ = input.readUInt32(); finishCount_ = input.readUInt32();
break; break;
} }
default: { default: {
@ -187,32 +187,10 @@ public final class ForgeQueueDataOuterClass {
return avatarId_; return avatarId_;
} }
public static final int UNFINISH_COUNT_FIELD_NUMBER = 15; public static final int FINISH_COUNT_FIELD_NUMBER = 15;
private int unfinishCount_;
/**
* <code>uint32 unfinish_count = 15;</code>
* @return The unfinishCount.
*/
@java.lang.Override
public int getUnfinishCount() {
return unfinishCount_;
}
public static final int NEXT_FINISH_TIMESTAMP_FIELD_NUMBER = 13;
private int nextFinishTimestamp_;
/**
* <code>uint32 next_finish_timestamp = 13;</code>
* @return The nextFinishTimestamp.
*/
@java.lang.Override
public int getNextFinishTimestamp() {
return nextFinishTimestamp_;
}
public static final int FINISH_COUNT_FIELD_NUMBER = 4;
private int finishCount_; private int finishCount_;
/** /**
* <code>uint32 finish_count = 4;</code> * <code>uint32 finish_count = 15;</code>
* @return The finishCount. * @return The finishCount.
*/ */
@java.lang.Override @java.lang.Override
@ -220,6 +198,28 @@ public final class ForgeQueueDataOuterClass {
return finishCount_; return finishCount_;
} }
public static final int TOTAL_FINISH_TIMESTAMP_FIELD_NUMBER = 13;
private int totalFinishTimestamp_;
/**
* <code>uint32 total_finish_timestamp = 13;</code>
* @return The totalFinishTimestamp.
*/
@java.lang.Override
public int getTotalFinishTimestamp() {
return totalFinishTimestamp_;
}
public static final int QUEUE_ID_FIELD_NUMBER = 4;
private int queueId_;
/**
* <code>uint32 queue_id = 4;</code>
* @return The queueId.
*/
@java.lang.Override
public int getQueueId() {
return queueId_;
}
public static final int FORGE_ID_FIELD_NUMBER = 1; public static final int FORGE_ID_FIELD_NUMBER = 1;
private int forgeId_; private int forgeId_;
/** /**
@ -231,26 +231,26 @@ public final class ForgeQueueDataOuterClass {
return forgeId_; return forgeId_;
} }
public static final int TOTAL_FINISH_TIMESTAMP_FIELD_NUMBER = 8; public static final int NEXT_FINISH_TIMESTAMP_FIELD_NUMBER = 8;
private int totalFinishTimestamp_; private int nextFinishTimestamp_;
/** /**
* <code>uint32 total_finish_timestamp = 8;</code> * <code>uint32 next_finish_timestamp = 8;</code>
* @return The totalFinishTimestamp. * @return The nextFinishTimestamp.
*/ */
@java.lang.Override @java.lang.Override
public int getTotalFinishTimestamp() { public int getNextFinishTimestamp() {
return totalFinishTimestamp_; return nextFinishTimestamp_;
} }
public static final int QUEUE_ID_FIELD_NUMBER = 9; public static final int UNFINISH_COUNT_FIELD_NUMBER = 9;
private int queueId_; private int unfinishCount_;
/** /**
* <code>uint32 queue_id = 9;</code> * <code>uint32 unfinish_count = 9;</code>
* @return The queueId. * @return The unfinishCount.
*/ */
@java.lang.Override @java.lang.Override
public int getQueueId() { public int getUnfinishCount() {
return queueId_; return unfinishCount_;
} }
private byte memoizedIsInitialized = -1; private byte memoizedIsInitialized = -1;
@ -270,23 +270,23 @@ public final class ForgeQueueDataOuterClass {
if (forgeId_ != 0) { if (forgeId_ != 0) {
output.writeUInt32(1, forgeId_); output.writeUInt32(1, forgeId_);
} }
if (finishCount_ != 0) {
output.writeUInt32(4, finishCount_);
}
if (totalFinishTimestamp_ != 0) {
output.writeUInt32(8, totalFinishTimestamp_);
}
if (queueId_ != 0) { if (queueId_ != 0) {
output.writeUInt32(9, queueId_); output.writeUInt32(4, queueId_);
} }
if (nextFinishTimestamp_ != 0) { if (nextFinishTimestamp_ != 0) {
output.writeUInt32(13, nextFinishTimestamp_); output.writeUInt32(8, nextFinishTimestamp_);
}
if (unfinishCount_ != 0) {
output.writeUInt32(9, unfinishCount_);
}
if (totalFinishTimestamp_ != 0) {
output.writeUInt32(13, totalFinishTimestamp_);
} }
if (avatarId_ != 0) { if (avatarId_ != 0) {
output.writeUInt32(14, avatarId_); output.writeUInt32(14, avatarId_);
} }
if (unfinishCount_ != 0) { if (finishCount_ != 0) {
output.writeUInt32(15, unfinishCount_); output.writeUInt32(15, finishCount_);
} }
unknownFields.writeTo(output); unknownFields.writeTo(output);
} }
@ -301,29 +301,29 @@ public final class ForgeQueueDataOuterClass {
size += com.google.protobuf.CodedOutputStream size += com.google.protobuf.CodedOutputStream
.computeUInt32Size(1, forgeId_); .computeUInt32Size(1, forgeId_);
} }
if (finishCount_ != 0) {
size += com.google.protobuf.CodedOutputStream
.computeUInt32Size(4, finishCount_);
}
if (totalFinishTimestamp_ != 0) {
size += com.google.protobuf.CodedOutputStream
.computeUInt32Size(8, totalFinishTimestamp_);
}
if (queueId_ != 0) { if (queueId_ != 0) {
size += com.google.protobuf.CodedOutputStream size += com.google.protobuf.CodedOutputStream
.computeUInt32Size(9, queueId_); .computeUInt32Size(4, queueId_);
} }
if (nextFinishTimestamp_ != 0) { if (nextFinishTimestamp_ != 0) {
size += com.google.protobuf.CodedOutputStream size += com.google.protobuf.CodedOutputStream
.computeUInt32Size(13, nextFinishTimestamp_); .computeUInt32Size(8, nextFinishTimestamp_);
}
if (unfinishCount_ != 0) {
size += com.google.protobuf.CodedOutputStream
.computeUInt32Size(9, unfinishCount_);
}
if (totalFinishTimestamp_ != 0) {
size += com.google.protobuf.CodedOutputStream
.computeUInt32Size(13, totalFinishTimestamp_);
} }
if (avatarId_ != 0) { if (avatarId_ != 0) {
size += com.google.protobuf.CodedOutputStream size += com.google.protobuf.CodedOutputStream
.computeUInt32Size(14, avatarId_); .computeUInt32Size(14, avatarId_);
} }
if (unfinishCount_ != 0) { if (finishCount_ != 0) {
size += com.google.protobuf.CodedOutputStream size += com.google.protobuf.CodedOutputStream
.computeUInt32Size(15, unfinishCount_); .computeUInt32Size(15, finishCount_);
} }
size += unknownFields.getSerializedSize(); size += unknownFields.getSerializedSize();
memoizedSize = size; memoizedSize = size;
@ -342,18 +342,18 @@ public final class ForgeQueueDataOuterClass {
if (getAvatarId() if (getAvatarId()
!= other.getAvatarId()) return false; != other.getAvatarId()) return false;
if (getUnfinishCount()
!= other.getUnfinishCount()) return false;
if (getNextFinishTimestamp()
!= other.getNextFinishTimestamp()) return false;
if (getFinishCount() if (getFinishCount()
!= other.getFinishCount()) return false; != other.getFinishCount()) return false;
if (getForgeId()
!= other.getForgeId()) return false;
if (getTotalFinishTimestamp() if (getTotalFinishTimestamp()
!= other.getTotalFinishTimestamp()) return false; != other.getTotalFinishTimestamp()) return false;
if (getQueueId() if (getQueueId()
!= other.getQueueId()) return false; != other.getQueueId()) return false;
if (getForgeId()
!= other.getForgeId()) return false;
if (getNextFinishTimestamp()
!= other.getNextFinishTimestamp()) return false;
if (getUnfinishCount()
!= other.getUnfinishCount()) return false;
if (!unknownFields.equals(other.unknownFields)) return false; if (!unknownFields.equals(other.unknownFields)) return false;
return true; return true;
} }
@ -367,18 +367,18 @@ public final class ForgeQueueDataOuterClass {
hash = (19 * hash) + getDescriptor().hashCode(); hash = (19 * hash) + getDescriptor().hashCode();
hash = (37 * hash) + AVATAR_ID_FIELD_NUMBER; hash = (37 * hash) + AVATAR_ID_FIELD_NUMBER;
hash = (53 * hash) + getAvatarId(); hash = (53 * hash) + getAvatarId();
hash = (37 * hash) + UNFINISH_COUNT_FIELD_NUMBER;
hash = (53 * hash) + getUnfinishCount();
hash = (37 * hash) + NEXT_FINISH_TIMESTAMP_FIELD_NUMBER;
hash = (53 * hash) + getNextFinishTimestamp();
hash = (37 * hash) + FINISH_COUNT_FIELD_NUMBER; hash = (37 * hash) + FINISH_COUNT_FIELD_NUMBER;
hash = (53 * hash) + getFinishCount(); hash = (53 * hash) + getFinishCount();
hash = (37 * hash) + FORGE_ID_FIELD_NUMBER;
hash = (53 * hash) + getForgeId();
hash = (37 * hash) + TOTAL_FINISH_TIMESTAMP_FIELD_NUMBER; hash = (37 * hash) + TOTAL_FINISH_TIMESTAMP_FIELD_NUMBER;
hash = (53 * hash) + getTotalFinishTimestamp(); hash = (53 * hash) + getTotalFinishTimestamp();
hash = (37 * hash) + QUEUE_ID_FIELD_NUMBER; hash = (37 * hash) + QUEUE_ID_FIELD_NUMBER;
hash = (53 * hash) + getQueueId(); hash = (53 * hash) + getQueueId();
hash = (37 * hash) + FORGE_ID_FIELD_NUMBER;
hash = (53 * hash) + getForgeId();
hash = (37 * hash) + NEXT_FINISH_TIMESTAMP_FIELD_NUMBER;
hash = (53 * hash) + getNextFinishTimestamp();
hash = (37 * hash) + UNFINISH_COUNT_FIELD_NUMBER;
hash = (53 * hash) + getUnfinishCount();
hash = (29 * hash) + unknownFields.hashCode(); hash = (29 * hash) + unknownFields.hashCode();
memoizedHashCode = hash; memoizedHashCode = hash;
return hash; return hash;
@ -518,18 +518,18 @@ public final class ForgeQueueDataOuterClass {
super.clear(); super.clear();
avatarId_ = 0; avatarId_ = 0;
unfinishCount_ = 0;
nextFinishTimestamp_ = 0;
finishCount_ = 0; finishCount_ = 0;
forgeId_ = 0;
totalFinishTimestamp_ = 0; totalFinishTimestamp_ = 0;
queueId_ = 0; queueId_ = 0;
forgeId_ = 0;
nextFinishTimestamp_ = 0;
unfinishCount_ = 0;
return this; return this;
} }
@ -557,12 +557,12 @@ public final class ForgeQueueDataOuterClass {
public emu.grasscutter.net.proto.ForgeQueueDataOuterClass.ForgeQueueData buildPartial() { public emu.grasscutter.net.proto.ForgeQueueDataOuterClass.ForgeQueueData buildPartial() {
emu.grasscutter.net.proto.ForgeQueueDataOuterClass.ForgeQueueData result = new emu.grasscutter.net.proto.ForgeQueueDataOuterClass.ForgeQueueData(this); emu.grasscutter.net.proto.ForgeQueueDataOuterClass.ForgeQueueData result = new emu.grasscutter.net.proto.ForgeQueueDataOuterClass.ForgeQueueData(this);
result.avatarId_ = avatarId_; result.avatarId_ = avatarId_;
result.unfinishCount_ = unfinishCount_;
result.nextFinishTimestamp_ = nextFinishTimestamp_;
result.finishCount_ = finishCount_; result.finishCount_ = finishCount_;
result.forgeId_ = forgeId_;
result.totalFinishTimestamp_ = totalFinishTimestamp_; result.totalFinishTimestamp_ = totalFinishTimestamp_;
result.queueId_ = queueId_; result.queueId_ = queueId_;
result.forgeId_ = forgeId_;
result.nextFinishTimestamp_ = nextFinishTimestamp_;
result.unfinishCount_ = unfinishCount_;
onBuilt(); onBuilt();
return result; return result;
} }
@ -614,24 +614,24 @@ public final class ForgeQueueDataOuterClass {
if (other.getAvatarId() != 0) { if (other.getAvatarId() != 0) {
setAvatarId(other.getAvatarId()); setAvatarId(other.getAvatarId());
} }
if (other.getUnfinishCount() != 0) {
setUnfinishCount(other.getUnfinishCount());
}
if (other.getNextFinishTimestamp() != 0) {
setNextFinishTimestamp(other.getNextFinishTimestamp());
}
if (other.getFinishCount() != 0) { if (other.getFinishCount() != 0) {
setFinishCount(other.getFinishCount()); setFinishCount(other.getFinishCount());
} }
if (other.getForgeId() != 0) {
setForgeId(other.getForgeId());
}
if (other.getTotalFinishTimestamp() != 0) { if (other.getTotalFinishTimestamp() != 0) {
setTotalFinishTimestamp(other.getTotalFinishTimestamp()); setTotalFinishTimestamp(other.getTotalFinishTimestamp());
} }
if (other.getQueueId() != 0) { if (other.getQueueId() != 0) {
setQueueId(other.getQueueId()); setQueueId(other.getQueueId());
} }
if (other.getForgeId() != 0) {
setForgeId(other.getForgeId());
}
if (other.getNextFinishTimestamp() != 0) {
setNextFinishTimestamp(other.getNextFinishTimestamp());
}
if (other.getUnfinishCount() != 0) {
setUnfinishCount(other.getUnfinishCount());
}
this.mergeUnknownFields(other.unknownFields); this.mergeUnknownFields(other.unknownFields);
onChanged(); onChanged();
return this; return this;
@ -692,71 +692,9 @@ public final class ForgeQueueDataOuterClass {
return this; return this;
} }
private int unfinishCount_ ;
/**
* <code>uint32 unfinish_count = 15;</code>
* @return The unfinishCount.
*/
@java.lang.Override
public int getUnfinishCount() {
return unfinishCount_;
}
/**
* <code>uint32 unfinish_count = 15;</code>
* @param value The unfinishCount to set.
* @return This builder for chaining.
*/
public Builder setUnfinishCount(int value) {
unfinishCount_ = value;
onChanged();
return this;
}
/**
* <code>uint32 unfinish_count = 15;</code>
* @return This builder for chaining.
*/
public Builder clearUnfinishCount() {
unfinishCount_ = 0;
onChanged();
return this;
}
private int nextFinishTimestamp_ ;
/**
* <code>uint32 next_finish_timestamp = 13;</code>
* @return The nextFinishTimestamp.
*/
@java.lang.Override
public int getNextFinishTimestamp() {
return nextFinishTimestamp_;
}
/**
* <code>uint32 next_finish_timestamp = 13;</code>
* @param value The nextFinishTimestamp to set.
* @return This builder for chaining.
*/
public Builder setNextFinishTimestamp(int value) {
nextFinishTimestamp_ = value;
onChanged();
return this;
}
/**
* <code>uint32 next_finish_timestamp = 13;</code>
* @return This builder for chaining.
*/
public Builder clearNextFinishTimestamp() {
nextFinishTimestamp_ = 0;
onChanged();
return this;
}
private int finishCount_ ; private int finishCount_ ;
/** /**
* <code>uint32 finish_count = 4;</code> * <code>uint32 finish_count = 15;</code>
* @return The finishCount. * @return The finishCount.
*/ */
@java.lang.Override @java.lang.Override
@ -764,7 +702,7 @@ public final class ForgeQueueDataOuterClass {
return finishCount_; return finishCount_;
} }
/** /**
* <code>uint32 finish_count = 4;</code> * <code>uint32 finish_count = 15;</code>
* @param value The finishCount to set. * @param value The finishCount to set.
* @return This builder for chaining. * @return This builder for chaining.
*/ */
@ -775,7 +713,7 @@ public final class ForgeQueueDataOuterClass {
return this; return this;
} }
/** /**
* <code>uint32 finish_count = 4;</code> * <code>uint32 finish_count = 15;</code>
* @return This builder for chaining. * @return This builder for chaining.
*/ */
public Builder clearFinishCount() { public Builder clearFinishCount() {
@ -785,6 +723,68 @@ public final class ForgeQueueDataOuterClass {
return this; return this;
} }
private int totalFinishTimestamp_ ;
/**
* <code>uint32 total_finish_timestamp = 13;</code>
* @return The totalFinishTimestamp.
*/
@java.lang.Override
public int getTotalFinishTimestamp() {
return totalFinishTimestamp_;
}
/**
* <code>uint32 total_finish_timestamp = 13;</code>
* @param value The totalFinishTimestamp to set.
* @return This builder for chaining.
*/
public Builder setTotalFinishTimestamp(int value) {
totalFinishTimestamp_ = value;
onChanged();
return this;
}
/**
* <code>uint32 total_finish_timestamp = 13;</code>
* @return This builder for chaining.
*/
public Builder clearTotalFinishTimestamp() {
totalFinishTimestamp_ = 0;
onChanged();
return this;
}
private int queueId_ ;
/**
* <code>uint32 queue_id = 4;</code>
* @return The queueId.
*/
@java.lang.Override
public int getQueueId() {
return queueId_;
}
/**
* <code>uint32 queue_id = 4;</code>
* @param value The queueId to set.
* @return This builder for chaining.
*/
public Builder setQueueId(int value) {
queueId_ = value;
onChanged();
return this;
}
/**
* <code>uint32 queue_id = 4;</code>
* @return This builder for chaining.
*/
public Builder clearQueueId() {
queueId_ = 0;
onChanged();
return this;
}
private int forgeId_ ; private int forgeId_ ;
/** /**
* <code>uint32 forge_id = 1;</code> * <code>uint32 forge_id = 1;</code>
@ -816,64 +816,64 @@ public final class ForgeQueueDataOuterClass {
return this; return this;
} }
private int totalFinishTimestamp_ ; private int nextFinishTimestamp_ ;
/** /**
* <code>uint32 total_finish_timestamp = 8;</code> * <code>uint32 next_finish_timestamp = 8;</code>
* @return The totalFinishTimestamp. * @return The nextFinishTimestamp.
*/ */
@java.lang.Override @java.lang.Override
public int getTotalFinishTimestamp() { public int getNextFinishTimestamp() {
return totalFinishTimestamp_; return nextFinishTimestamp_;
} }
/** /**
* <code>uint32 total_finish_timestamp = 8;</code> * <code>uint32 next_finish_timestamp = 8;</code>
* @param value The totalFinishTimestamp to set. * @param value The nextFinishTimestamp to set.
* @return This builder for chaining. * @return This builder for chaining.
*/ */
public Builder setTotalFinishTimestamp(int value) { public Builder setNextFinishTimestamp(int value) {
totalFinishTimestamp_ = value; nextFinishTimestamp_ = value;
onChanged(); onChanged();
return this; return this;
} }
/** /**
* <code>uint32 total_finish_timestamp = 8;</code> * <code>uint32 next_finish_timestamp = 8;</code>
* @return This builder for chaining. * @return This builder for chaining.
*/ */
public Builder clearTotalFinishTimestamp() { public Builder clearNextFinishTimestamp() {
totalFinishTimestamp_ = 0; nextFinishTimestamp_ = 0;
onChanged(); onChanged();
return this; return this;
} }
private int queueId_ ; private int unfinishCount_ ;
/** /**
* <code>uint32 queue_id = 9;</code> * <code>uint32 unfinish_count = 9;</code>
* @return The queueId. * @return The unfinishCount.
*/ */
@java.lang.Override @java.lang.Override
public int getQueueId() { public int getUnfinishCount() {
return queueId_; return unfinishCount_;
} }
/** /**
* <code>uint32 queue_id = 9;</code> * <code>uint32 unfinish_count = 9;</code>
* @param value The queueId to set. * @param value The unfinishCount to set.
* @return This builder for chaining. * @return This builder for chaining.
*/ */
public Builder setQueueId(int value) { public Builder setUnfinishCount(int value) {
queueId_ = value; unfinishCount_ = value;
onChanged(); onChanged();
return this; return this;
} }
/** /**
* <code>uint32 queue_id = 9;</code> * <code>uint32 unfinish_count = 9;</code>
* @return This builder for chaining. * @return This builder for chaining.
*/ */
public Builder clearQueueId() { public Builder clearUnfinishCount() {
queueId_ = 0; unfinishCount_ = 0;
onChanged(); onChanged();
return this; return this;
} }
@ -945,10 +945,10 @@ public final class ForgeQueueDataOuterClass {
static { static {
java.lang.String[] descriptorData = { java.lang.String[] descriptorData = {
"\n\024ForgeQueueData.proto\"\264\001\n\016ForgeQueueDat" + "\n\024ForgeQueueData.proto\"\264\001\n\016ForgeQueueDat" +
"a\022\021\n\tavatar_id\030\016 \001(\r\022\026\n\016unfinish_count\030\017" + "a\022\021\n\tavatar_id\030\016 \001(\r\022\024\n\014finish_count\030\017 \001" +
" \001(\r\022\035\n\025next_finish_timestamp\030\r \001(\r\022\024\n\014f" + "(\r\022\036\n\026total_finish_timestamp\030\r \001(\r\022\020\n\010qu" +
"inish_count\030\004 \001(\r\022\020\n\010forge_id\030\001 \001(\r\022\036\n\026t" + "eue_id\030\004 \001(\r\022\020\n\010forge_id\030\001 \001(\r\022\035\n\025next_f" +
"otal_finish_timestamp\030\010 \001(\r\022\020\n\010queue_id\030" + "inish_timestamp\030\010 \001(\r\022\026\n\016unfinish_count\030" +
"\t \001(\rB\033\n\031emu.grasscutter.net.protob\006prot" + "\t \001(\rB\033\n\031emu.grasscutter.net.protob\006prot" +
"o3" "o3"
}; };
@ -961,7 +961,7 @@ public final class ForgeQueueDataOuterClass {
internal_static_ForgeQueueData_fieldAccessorTable = new internal_static_ForgeQueueData_fieldAccessorTable = new
com.google.protobuf.GeneratedMessageV3.FieldAccessorTable( com.google.protobuf.GeneratedMessageV3.FieldAccessorTable(
internal_static_ForgeQueueData_descriptor, internal_static_ForgeQueueData_descriptor,
new java.lang.String[] { "AvatarId", "UnfinishCount", "NextFinishTimestamp", "FinishCount", "ForgeId", "TotalFinishTimestamp", "QueueId", }); new java.lang.String[] { "AvatarId", "FinishCount", "TotalFinishTimestamp", "QueueId", "ForgeId", "NextFinishTimestamp", "UnfinishCount", });
} }
// @@protoc_insertion_point(outer_class_scope) // @@protoc_insertion_point(outer_class_scope)

View File

@ -4,11 +4,9 @@ import static emu.grasscutter.utils.lang.Language.translate;
import at.favre.lib.crypto.bcrypt.BCrypt; import at.favre.lib.crypto.bcrypt.BCrypt;
import emu.grasscutter.Grasscutter; import emu.grasscutter.Grasscutter;
import emu.grasscutter.command.Command; import emu.grasscutter.command.*;
import emu.grasscutter.command.CommandHandler;
import emu.grasscutter.config.Configuration; import emu.grasscutter.config.Configuration;
import emu.grasscutter.database.DatabaseHelper; import emu.grasscutter.database.*;
import emu.grasscutter.database.DatabaseManager;
import emu.grasscutter.game.Account; import emu.grasscutter.game.Account;
import emu.grasscutter.game.player.Player; import emu.grasscutter.game.player.Player;
import java.util.List; import java.util.List;
@ -31,17 +29,17 @@ public final class AccountCommand implements CommandHandler {
return; return;
} }
if (args.size() < 2) {
sendUsageMessage(sender);
return;
}
String action = args.get(0); String action = args.get(0);
String username = args.get(1);
switch (action) { switch (action) {
default -> this.sendUsageMessage(sender); default -> this.sendUsageMessage(sender);
case "create" -> { case "create" -> {
if (args.size() < 2) {
this.sendUsageMessage(sender);
return;
}
var username = args.get(1);
int uid = 0; int uid = 0;
String password = ""; String password = "";
if (Configuration.ACCOUNT.EXPERIMENTAL_RealPassword) { if (Configuration.ACCOUNT.EXPERIMENTAL_RealPassword) {
@ -94,6 +92,12 @@ public final class AccountCommand implements CommandHandler {
} }
} }
case "delete" -> { case "delete" -> {
if (args.size() < 2) {
this.sendUsageMessage(sender);
return;
}
var username = args.get(1);
// Get the account we want to delete. // Get the account we want to delete.
Account toDelete = DatabaseHelper.getAccountByName(username); Account toDelete = DatabaseHelper.getAccountByName(username);
if (toDelete == null) { if (toDelete == null) {
@ -104,6 +108,12 @@ public final class AccountCommand implements CommandHandler {
CommandHandler.sendMessage(sender, translate(sender, "commands.account.delete")); CommandHandler.sendMessage(sender, translate(sender, "commands.account.delete"));
} }
case "resetpass" -> { case "resetpass" -> {
if (args.size() < 2) {
this.sendUsageMessage(sender);
return;
}
var username = args.get(1);
if (!Configuration.ACCOUNT.EXPERIMENTAL_RealPassword) { if (!Configuration.ACCOUNT.EXPERIMENTAL_RealPassword) {
CommandHandler.sendMessage( CommandHandler.sendMessage(
sender, "resetpass requires EXPERIMENTAL_RealPassword to be true."); sender, "resetpass requires EXPERIMENTAL_RealPassword to be true.");

View File

@ -9,6 +9,7 @@ import emu.grasscutter.Grasscutter;
import emu.grasscutter.data.binout.*; import emu.grasscutter.data.binout.*;
import emu.grasscutter.data.binout.AbilityModifier.AbilityModifierAction; import emu.grasscutter.data.binout.AbilityModifier.AbilityModifierAction;
import emu.grasscutter.data.binout.config.*; import emu.grasscutter.data.binout.config.*;
import emu.grasscutter.data.binout.routes.*;
import emu.grasscutter.data.common.PointData; import emu.grasscutter.data.common.PointData;
import emu.grasscutter.data.custom.*; import emu.grasscutter.data.custom.*;
import emu.grasscutter.data.excels.trial.TrialAvatarActivityDataData; import emu.grasscutter.data.excels.trial.TrialAvatarActivityDataData;
@ -107,6 +108,7 @@ public final class ResourceLoader {
// Load default home layout // Load default home layout
loadHomeworldDefaultSaveData(); loadHomeworldDefaultSaveData();
loadNpcBornData(); loadNpcBornData();
loadRoutes();
loadBlossomResources(); loadBlossomResources();
cacheTalentLevelSets(); cacheTalentLevelSets();
// Load activities. // Load activities.
@ -264,6 +266,32 @@ public final class ResourceLoader {
} }
} }
private static void loadRoutes() {
try {
Files.newDirectoryStream(getResourcePath("BinOutput/LevelDesign/Routes/"), "*.json")
.forEach(
path -> {
try {
val data = JsonUtils.loadToClass(path, SceneRoutes.class);
val routesArray = data.getRoutes();
if (routesArray == null) return;
val routesMap =
GameData.getSceneRouteData()
.getOrDefault(data.getSceneId(), new Int2ObjectOpenHashMap<>());
for (Route route : routesArray) {
routesMap.put(route.getLocalId(), route);
}
GameData.getSceneRouteData().put(data.getSceneId(), routesMap);
} catch (IOException ignored) {
}
});
Grasscutter.getLogger()
.debug("Loaded " + GameData.getSceneNpcBornData().size() + " SceneRouteDatas.");
} catch (IOException e) {
Grasscutter.getLogger().error("Failed to load SceneRouteData folder.");
}
}
private static void cacheTalentLevelSets() { private static void cacheTalentLevelSets() {
// All known levels, keyed by proudSkillGroupId // All known levels, keyed by proudSkillGroupId
GameData.getProudSkillDataMap() GameData.getProudSkillDataMap()

View File

@ -11,38 +11,58 @@ import lombok.experimental.FieldDefaults;
@FieldDefaults(level = AccessLevel.PRIVATE) @FieldDefaults(level = AccessLevel.PRIVATE)
public class HomeworldDefaultSaveData { public class HomeworldDefaultSaveData {
@SerializedName(value = "KFHBFNPDJBE", alternate = "PKACPHDGGEI") @SerializedName(
value = "KFHBFNPDJBE",
alternate = {"PKACPHDGGEI", "AKOLOBLHDFK"})
List<HomeBlock> homeBlockLists; List<HomeBlock> homeBlockLists;
@SerializedName(value = "IJNPADKGNKE", alternate = "MINCKHBNING") @SerializedName(
value = "IJNPADKGNKE",
alternate = {"MINCKHBNING", "MBICDPDEKDM"})
Position bornPos; Position bornPos;
@SerializedName("IPIIGEMFLHK") @SerializedName(
value = "IPIIGEMFLHK",
alternate = {"EJJIOJKFKCO"})
Position bornRot; Position bornRot;
@SerializedName("HHOLBNPIHEM") @SerializedName(
value = "HHOLBNPIHEM",
alternate = {"CJAKHCIFHNP"})
Position djinPos; Position djinPos;
@SerializedName("KNHCJKHCOAN") @SerializedName(
value = "KNHCJKHCOAN",
alternate = {"AMDNOHPGKMI"})
HomeFurniture mainhouse; HomeFurniture mainhouse;
@SerializedName("NIHOJFEKFPG") @SerializedName(
value = "NIHOJFEKFPG",
alternate = {"BHCPEAOPIDC"})
List<HomeFurniture> doorLists; List<HomeFurniture> doorLists;
@SerializedName("EPGELGEFJFK") @SerializedName(
value = "EPGELGEFJFK",
alternate = {"AABEPENIFLN"})
List<HomeFurniture> stairLists; List<HomeFurniture> stairLists;
@Data @Data
@FieldDefaults(level = AccessLevel.PRIVATE) @FieldDefaults(level = AccessLevel.PRIVATE)
public static class HomeBlock { public static class HomeBlock {
@SerializedName(value = "FGIJCELCGFI", alternate = "PGDPDIDJEEL") @SerializedName(
value = "FGIJCELCGFI",
alternate = {"PGDPDIDJEEL", "ANICBLBOBKD"})
int blockId; int blockId;
@SerializedName("BEAPOFELABD") @SerializedName(
value = "BEAPOFELABD",
alternate = {"NCIMIKKFLOH"})
List<HomeFurniture> furnitures; List<HomeFurniture> furnitures;
@SerializedName("MLIODLGDFHJ") @SerializedName(
value = "MLIODLGDFHJ",
alternate = {"GJGNLIINBGB"})
List<HomeFurniture> persistentFurnitures; List<HomeFurniture> persistentFurnitures;
} }
@ -50,10 +70,14 @@ public class HomeworldDefaultSaveData {
@FieldDefaults(level = AccessLevel.PRIVATE) @FieldDefaults(level = AccessLevel.PRIVATE)
public static class HomeFurniture { public static class HomeFurniture {
@SerializedName(value = "ENHNGKJBJAB", alternate = "KMAAJJHPNBA") @SerializedName(
value = "ENHNGKJBJAB",
alternate = {"KMAAJJHPNBA", "FFLCGFGGGND"})
int id; int id;
@SerializedName(value = "NGIEEIOLPPO", alternate = "JFKAHNCPDME") @SerializedName(
value = "NGIEEIOLPPO",
alternate = {"JFKAHNCPDME", "BPCGGBKIAMG"})
Position pos; Position pos;
// @SerializedName(value = "HEOCEHKEBFM", alternate = "LKCKOOGFDBM") // @SerializedName(value = "HEOCEHKEBFM", alternate = "LKCKOOGFDBM")
Position rot; Position rot;

View File

@ -89,7 +89,14 @@ public class ItemData extends GameResource {
@SerializedName( @SerializedName(
value = "roomSceneId", value = "roomSceneId",
alternate = {"BMEPAMCNABE", "DANFGGLKLNO", "JFDLJGDFIGL", "OHIANNAEEAK", "MFGACDIOHGF"}) alternate = {
"BMEPAMCNABE",
"DANFGGLKLNO",
"JFDLJGDFIGL",
"OHIANNAEEAK",
"MFGACDIOHGF",
"roomSceneID"
})
private int roomSceneId; private int roomSceneId;
// Custom // Custom

View File

@ -39,7 +39,7 @@ public final class TalkConfigData extends GameResource {
if (this.questId <= 0) { if (this.questId <= 0) {
var id = String.valueOf(this.getId()); var id = String.valueOf(this.getId());
this.questId = Integer.parseInt(id.length() < 5 ? "0" : id.substring(0, 3)); this.questId = Integer.parseInt(id.length() < 5 ? "0" : id.substring(0, id.length() - 2));
} }
} }

View File

@ -6,12 +6,13 @@ import emu.grasscutter.game.dungeons.enums.DungeonPassConditionType;
import emu.grasscutter.game.quest.enums.LogicType; import emu.grasscutter.game.quest.enums.LogicType;
import java.util.List; import java.util.List;
import lombok.Getter; import lombok.Getter;
import lombok.Setter;
@ResourceType(name = "DungeonPassExcelConfigData.json") @ResourceType(name = "DungeonPassExcelConfigData.json")
public class DungeonPassConfigData extends GameResource { public class DungeonPassConfigData extends GameResource {
@Getter private int id; @Getter private int id;
@Getter private LogicType logicType; @Getter private LogicType logicType;
@Getter private List<DungeonPassCondition> conds; @Getter @Setter private List<DungeonPassCondition> conds;
public static class DungeonPassCondition { public static class DungeonPassCondition {
@Getter private DungeonPassConditionType condType; @Getter private DungeonPassConditionType condType;

View File

@ -15,7 +15,7 @@ public class UnknownActivityConditionHandler extends ActivityConditionBaseHandle
@Override @Override
public boolean execute( public boolean execute(
PlayerActivityData activityData, ActivityConfigItem activityConfig, int... params) { PlayerActivityData activityData, ActivityConfigItem activityConfig, int... params) {
Grasscutter.getLogger().error("Called unknown condition handler {}.", conditions.name()); Grasscutter.getLogger().debug("Called unknown condition handler {}.", conditions.name());
return false; return false;
} }
} }

View File

@ -53,8 +53,13 @@ public final class DungeonManager {
public DungeonManager(@NonNull Scene scene, @NonNull DungeonData dungeonData) { public DungeonManager(@NonNull Scene scene, @NonNull DungeonData dungeonData) {
this.scene = scene; this.scene = scene;
this.dungeonData = dungeonData; this.dungeonData = dungeonData;
if (dungeonData.getPassCond() == 0) {
this.passConfigData = new DungeonPassConfigData();
this.passConfigData.setConds(new ArrayList<>());
} else {
this.passConfigData = GameData.getDungeonPassConfigDataMap().get(dungeonData.getPassCond()); this.passConfigData = GameData.getDungeonPassConfigDataMap().get(dungeonData.getPassCond());
this.finishedConditions = new int[passConfigData.getConds().size()]; }
this.finishedConditions = new int[this.passConfigData.getConds().size()];
} }
public void triggerEvent(DungeonPassConditionType conditionType, int... params) { public void triggerEvent(DungeonPassConditionType conditionType, int... params) {
@ -76,6 +81,7 @@ public final class DungeonManager {
} }
public boolean isFinishedSuccessfully() { public boolean isFinishedSuccessfully() {
if (passConfigData.getLogicType() == null) return false;
return LogicType.calculate(passConfigData.getLogicType(), finishedConditions); return LogicType.calculate(passConfigData.getLogicType(), finishedConditions);
} }

View File

@ -70,7 +70,7 @@ public abstract class EntityBaseGadget extends GameEntity {
getGadgetId()) getGadgetId())
.setSourceEntityId(getId()) .setSourceEntityId(getId())
.setParam3((int) this.getFightProperty(FightProperty.FIGHT_PROP_CUR_HP)) .setParam3((int) this.getFightProperty(FightProperty.FIGHT_PROP_CUR_HP))
.setEventSource(Integer.toString(getConfigId()))); .setEventSource(getConfigId()));
} }
protected void fillFightProps(ConfigEntityGadget configGadget) { protected void fillFightProps(ConfigEntityGadget configGadget) {

View File

@ -7,6 +7,7 @@ import emu.grasscutter.data.binout.config.fields.ConfigAbilityData;
import emu.grasscutter.data.excels.GadgetData; import emu.grasscutter.data.excels.GadgetData;
import emu.grasscutter.game.entity.gadget.*; import emu.grasscutter.game.entity.gadget.*;
import emu.grasscutter.game.entity.gadget.platform.BaseRoute; import emu.grasscutter.game.entity.gadget.platform.BaseRoute;
import emu.grasscutter.game.entity.gadget.platform.ConfigRoute;
import emu.grasscutter.game.player.Player; import emu.grasscutter.game.player.Player;
import emu.grasscutter.game.props.*; import emu.grasscutter.game.props.*;
import emu.grasscutter.game.world.*; import emu.grasscutter.game.world.*;
@ -246,6 +247,62 @@ public class EntityGadget extends EntityBaseGadget {
if (routeConfig.isStarted()) { if (routeConfig.isStarted()) {
return true; return true;
} }
if (routeConfig instanceof ConfigRoute configRoute) {
var route = this.getScene().getSceneRouteById(configRoute.getRouteId());
if (route != null) {
var points = route.getPoints();
val currIndex = configRoute.getStartIndex();
Position prevpos;
if (currIndex == 0) {
prevpos = getPosition();
this.getScene()
.getScriptManager()
.callEvent(
new ScriptArgs(
this.getGroupId(),
EventType.EVENT_PLATFORM_REACH_POINT,
this.getConfigId(),
configRoute.getRouteId())
.setParam3(0)
.setEventSource(this.getConfigId()));
} else {
prevpos = points[currIndex].getPos();
}
double time = 0;
for (var i = currIndex; i < points.length; ++i) {
time += points[i].getPos().computeDistance(prevpos) / points[i].getTargetVelocity();
prevpos = points[i].getPos();
val I = i;
configRoute
.getScheduledIndexes()
.add(
this.getScene()
.getScheduler()
.scheduleDelayedTask(
() -> {
if (points[I].isHasReachEvent() && I > currIndex) {
this.getScene()
.getScriptManager()
.callEvent(
new ScriptArgs(
this.getGroupId(),
EventType.EVENT_PLATFORM_REACH_POINT,
this.getConfigId(),
configRoute.getRouteId())
.setParam3(I)
.setEventSource(this.getConfigId()));
}
configRoute.setStartIndex(I);
this.position.set(points[I].getPos());
},
(int) time));
}
}
}
getScene().broadcastPacket(new PacketSceneTimeNotify(getScene())); getScene().broadcastPacket(new PacketSceneTimeNotify(getScene()));
routeConfig.startRoute(getScene()); routeConfig.startRoute(getScene());
getScene().broadcastPacket(new PacketPlatformStartRouteNotify(this)); getScene().broadcastPacket(new PacketPlatformStartRouteNotify(this));
@ -261,6 +318,14 @@ public class EntityGadget extends EntityBaseGadget {
if (!routeConfig.isStarted()) { if (!routeConfig.isStarted()) {
return true; return true;
} }
if (routeConfig instanceof ConfigRoute configRoute) {
for (var task : configRoute.getScheduledIndexes()) {
this.getScene().getScheduler().cancelTask(task);
}
configRoute.getScheduledIndexes().clear();
}
routeConfig.stopRoute(getScene()); routeConfig.stopRoute(getScene());
getScene().broadcastPacket(new PacketPlatformStopRouteNotify(this)); getScene().broadcastPacket(new PacketPlatformStopRouteNotify(this));

View File

@ -235,7 +235,7 @@ public class EntityMonster extends GameEntity {
getScene().getScriptManager().callEvent(new ScriptArgs(this.getGroupId(), EVENT_SPECIFIC_MONSTER_HP_CHANGE, getConfigId(), monsterData.getId()) getScene().getScriptManager().callEvent(new ScriptArgs(this.getGroupId(), EVENT_SPECIFIC_MONSTER_HP_CHANGE, getConfigId(), monsterData.getId())
.setSourceEntityId(getId()) .setSourceEntityId(getId())
.setParam3((int) this.getFightProperty(FightProperty.FIGHT_PROP_CUR_HP)) .setParam3((int) this.getFightProperty(FightProperty.FIGHT_PROP_CUR_HP))
.setEventSource(Integer.toString(getConfigId()))); .setEventSource(getConfigId()));
} }
@Override @Override

View File

@ -59,12 +59,12 @@ public final class GadgetGatherObject extends GadgetContent {
GameItem item = new GameItem(itemData, 1); GameItem item = new GameItem(itemData, 1);
player.getInventory().addItem(item, ActionReason.Gather); player.getInventory().addItem(item, ActionReason.Gather);
getGadget() var ScriptArgs =
.getScene() new ScriptArgs(getGadget().getGroupId(), EventType.EVENT_GATHER, getGadget().getConfigId());
.getScriptManager() if (getGadget().getMetaGadget() != null) {
.callEvent( ScriptArgs.setEventSource(getGadget().getMetaGadget().config_id);
new ScriptArgs( }
getGadget().getGroupId(), EventType.EVENT_GATHER, getGadget().getConfigId())); getGadget().getScene().getScriptManager().callEvent(ScriptArgs);
getGadget() getGadget()
.getScene() .getScene()

View File

@ -1,20 +1,56 @@
package emu.grasscutter.game.entity.gadget; package emu.grasscutter.game.entity.gadget;
import emu.grasscutter.data.GameData;
import emu.grasscutter.data.excels.GatherData;
import emu.grasscutter.data.excels.ItemData;
import emu.grasscutter.game.entity.EntityGadget; import emu.grasscutter.game.entity.EntityGadget;
import emu.grasscutter.game.inventory.GameItem;
import emu.grasscutter.game.player.Player; import emu.grasscutter.game.player.Player;
import emu.grasscutter.game.props.ActionReason;
import emu.grasscutter.net.proto.GadgetInteractReqOuterClass; import emu.grasscutter.net.proto.GadgetInteractReqOuterClass;
import emu.grasscutter.net.proto.InteractTypeOuterClass;
import emu.grasscutter.net.proto.SceneGadgetInfoOuterClass; import emu.grasscutter.net.proto.SceneGadgetInfoOuterClass;
import emu.grasscutter.scripts.constants.EventType;
import emu.grasscutter.scripts.data.ScriptArgs;
import emu.grasscutter.server.packet.send.PacketGadgetInteractRsp;
public class GadgetObject extends GadgetContent { public class GadgetObject extends GadgetContent {
private int itemId;
public GadgetObject(EntityGadget gadget) { public GadgetObject(EntityGadget gadget) {
super(gadget); super(gadget);
GatherData gatherData = GameData.getGatherDataMap().get(gadget.getPointType());
if (gatherData != null) {
this.itemId = gatherData.getItemId();
}
} }
@Override @Override
public boolean onInteract(Player player, GadgetInteractReqOuterClass.GadgetInteractReq req) { public boolean onInteract(Player player, GadgetInteractReqOuterClass.GadgetInteractReq req) {
// This is a workaround until a proper gadget interaction system can be put in place.
ItemData itemData = GameData.getItemDataMap().get(this.itemId);
if (itemData == null) {
return false; return false;
} }
GameItem item = new GameItem(itemData, 1);
player.getInventory().addItem(item, ActionReason.Gather);
var ScriptArgs =
new ScriptArgs(getGadget().getGroupId(), EventType.EVENT_GATHER, getGadget().getConfigId());
if (getGadget().getMetaGadget() != null) {
ScriptArgs.setEventSource(getGadget().getMetaGadget().config_id);
}
getGadget().getScene().getScriptManager().callEvent(ScriptArgs);
getGadget()
.getScene()
.broadcastPacket(
new PacketGadgetInteractRsp(
getGadget(), InteractTypeOuterClass.InteractType.INTERACT_TYPE_GATHER));
return true;
}
@Override @Override
public void onBuildProto(SceneGadgetInfoOuterClass.SceneGadgetInfo.Builder gadgetInfo) {} public void onBuildProto(SceneGadgetInfoOuterClass.SceneGadgetInfo.Builder gadgetInfo) {}
} }

View File

@ -4,27 +4,36 @@ import emu.grasscutter.game.world.Position;
import emu.grasscutter.net.proto.MovingPlatformTypeOuterClass; import emu.grasscutter.net.proto.MovingPlatformTypeOuterClass;
import emu.grasscutter.net.proto.PlatformInfoOuterClass; import emu.grasscutter.net.proto.PlatformInfoOuterClass;
import emu.grasscutter.scripts.data.SceneGadget; import emu.grasscutter.scripts.data.SceneGadget;
import java.util.ArrayList;
import java.util.List;
import lombok.Getter; import lombok.Getter;
import lombok.Setter; import lombok.Setter;
public class ConfigRoute extends BaseRoute { public class ConfigRoute extends BaseRoute {
@Getter @Setter private int routeId; @Getter @Setter private int routeId;
@Getter @Setter private int startIndex;
@Getter @Setter private List<Integer> scheduledIndexes;
public ConfigRoute(SceneGadget gadget) { public ConfigRoute(SceneGadget gadget) {
super(gadget); super(gadget);
this.routeId = gadget.route_id; this.routeId = gadget.route_id;
this.startIndex = 0;
this.scheduledIndexes = new ArrayList<>();
} }
public ConfigRoute(Position startRot, boolean startRoute, boolean isActive, int routeId) { public ConfigRoute(Position startRot, boolean startRoute, boolean isActive, int routeId) {
super(startRot, startRoute, isActive); super(startRot, startRoute, isActive);
this.routeId = routeId; this.routeId = routeId;
this.startIndex = 0;
this.scheduledIndexes = new ArrayList<>();
} }
@Override @Override
public PlatformInfoOuterClass.PlatformInfo.Builder toProto() { public PlatformInfoOuterClass.PlatformInfo.Builder toProto() {
return super.toProto() return super.toProto()
.setRouteId(routeId) .setRouteId(this.routeId)
.setStartIndex(this.startIndex)
.setMovingPlatformType( .setMovingPlatformType(
MovingPlatformTypeOuterClass.MovingPlatformType.MOVING_PLATFORM_TYPE_USE_CONFIG); MovingPlatformTypeOuterClass.MovingPlatformType.MOVING_PLATFORM_TYPE_USE_CONFIG);
} }

View File

@ -1536,6 +1536,12 @@ public class Player implements PlayerHook, FieldFetch {
} }
} }
@Override
public boolean equals(Object obj) {
return obj instanceof Player otherPlayer &&
this.id == otherPlayer.getUid();
}
public enum SceneLoadState { public enum SceneLoadState {
NONE(0), LOADING(1), INIT(2), LOADED(3); NONE(0), LOADING(1), INIT(2), LOADED(3);
@ -1546,5 +1552,4 @@ public class Player implements PlayerHook, FieldFetch {
this.value = value; this.value = value;
} }
} }
} }

View File

@ -12,6 +12,6 @@ public class ContentLeaveScene extends BaseContent {
@Override @Override
public boolean execute( public boolean execute(
GameQuest quest, QuestData.QuestContentCondition condition, String paramStr, int... params) { GameQuest quest, QuestData.QuestContentCondition condition, String paramStr, int... params) {
return quest.getOwner().getScene().getPrevScene() == params[0]; return condition.getParam()[0] == params[0];
} }
} }

View File

@ -13,9 +13,11 @@ public class ContentQuestStateEqual extends BaseContent {
public boolean execute( public boolean execute(
GameQuest quest, QuestData.QuestContentCondition condition, String paramStr, int... params) { GameQuest quest, QuestData.QuestContentCondition condition, String paramStr, int... params) {
GameQuest checkQuest = quest.getOwner().getQuestManager().getQuestById(condition.getParam()[0]); GameQuest checkQuest = quest.getOwner().getQuestManager().getQuestById(condition.getParam()[0]);
if (checkQuest == null) { if (checkQuest == null) {
return false; return false;
} }
return checkQuest.getState().getValue() == params[1];
return checkQuest.getState().getValue() == condition.getParam()[1];
} }
} }

View File

@ -12,12 +12,12 @@ public class ContentQuestStateNotEqual extends BaseContent {
@Override @Override
public boolean execute( public boolean execute(
GameQuest quest, QuestData.QuestContentCondition condition, String paramStr, int... params) { GameQuest quest, QuestData.QuestContentCondition condition, String paramStr, int... params) {
GameQuest checkQuest = quest.getOwner().getQuestManager().getQuestById(params[0]); GameQuest checkQuest = quest.getOwner().getQuestManager().getQuestById(condition.getParam()[0]);
if (checkQuest != null) {
return checkQuest.getState().getValue() != params[1];
}
if (checkQuest == null) {
return false; return false;
} }
return checkQuest.getState().getValue() != condition.getParam()[1];
}
} }

View File

@ -13,8 +13,8 @@ public class ContentQuestVarEqual extends BaseContent {
@Override @Override
public boolean execute( public boolean execute(
GameQuest quest, QuestData.QuestContentCondition condition, String paramStr, int... params) { GameQuest quest, QuestData.QuestContentCondition condition, String paramStr, int... params) {
int questVarValue = quest.getMainQuest().getQuestVars()[params[0]]; int questVarValue = quest.getMainQuest().getQuestVars()[condition.getParam()[0]];
Grasscutter.getLogger().debug("questVar {} : {}", params[0], questVarValue); Grasscutter.getLogger().debug("questVar {} : {}", condition.getParam()[0], questVarValue);
return questVarValue == params[1]; return questVarValue == condition.getParam()[1];
} }
} }

View File

@ -13,8 +13,8 @@ public class ContentQuestVarGreater extends BaseContent {
@Override @Override
public boolean execute( public boolean execute(
GameQuest quest, QuestData.QuestContentCondition condition, String paramStr, int... params) { GameQuest quest, QuestData.QuestContentCondition condition, String paramStr, int... params) {
int questVarValue = quest.getMainQuest().getQuestVars()[params[0]]; int questVarValue = quest.getMainQuest().getQuestVars()[condition.getParam()[0]];
Grasscutter.getLogger().debug("questVar {} : {}", params[0], questVarValue); Grasscutter.getLogger().debug("questVar {} : {}", condition.getParam()[0], questVarValue);
return questVarValue > params[1]; return questVarValue > condition.getParam()[1];
} }
} }

View File

@ -13,8 +13,8 @@ public class ContentQuestVarLess extends BaseContent {
@Override @Override
public boolean execute( public boolean execute(
GameQuest quest, QuestData.QuestContentCondition condition, String paramStr, int... params) { GameQuest quest, QuestData.QuestContentCondition condition, String paramStr, int... params) {
int questVarValue = quest.getMainQuest().getQuestVars()[params[0]]; int questVarValue = quest.getMainQuest().getQuestVars()[condition.getParam()[0]];
Grasscutter.getLogger().debug("questVar {} : {}", params[0], questVarValue); Grasscutter.getLogger().debug("questVar {} : {}", condition.getParam()[0], questVarValue);
return questVarValue < params[1]; return questVarValue < condition.getParam()[1];
} }
} }

View File

@ -31,6 +31,7 @@ import emu.grasscutter.scripts.data.*;
import emu.grasscutter.server.event.entity.EntityCreationEvent; import emu.grasscutter.server.event.entity.EntityCreationEvent;
import emu.grasscutter.server.event.player.PlayerTeleportEvent; import emu.grasscutter.server.event.player.PlayerTeleportEvent;
import emu.grasscutter.server.packet.send.*; import emu.grasscutter.server.packet.send.*;
import emu.grasscutter.server.scheduler.ServerTaskScheduler;
import emu.grasscutter.utils.objects.KahnsSort; import emu.grasscutter.utils.objects.KahnsSort;
import it.unimi.dsi.fastutil.ints.Int2ObjectMap; import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
import java.util.*; import java.util.*;
@ -51,7 +52,6 @@ public final class Scene {
@Getter private final Set<SceneGroup> loadedGroups; @Getter private final Set<SceneGroup> loadedGroups;
@Getter private final BlossomManager blossomManager; @Getter private final BlossomManager blossomManager;
private final HashSet<Integer> unlockedForces; private final HashSet<Integer> unlockedForces;
private final List<Runnable> afterLoadedCallbacks = new ArrayList<>();
private final long startWorldTime; private final long startWorldTime;
@Getter @Setter DungeonManager dungeonManager; @Getter @Setter DungeonManager dungeonManager;
@Getter Int2ObjectMap<Route> sceneRoutes; @Getter Int2ObjectMap<Route> sceneRoutes;
@ -68,7 +68,11 @@ public final class Scene {
@Getter private int tickCount = 0; @Getter private int tickCount = 0;
@Getter private boolean isPaused = false; @Getter private boolean isPaused = false;
private final List<Runnable> afterLoadedCallbacks = new ArrayList<>();
private final List<Runnable> afterHostInitCallbacks = new ArrayList<>();
@Getter private GameEntity sceneEntity; @Getter private GameEntity sceneEntity;
@Getter private final ServerTaskScheduler scheduler;
public Scene(World world, SceneData sceneData) { public Scene(World world, SceneData sceneData) {
this.world = world; this.world = world;
@ -92,6 +96,7 @@ public final class Scene {
this.blossomManager = new BlossomManager(this); this.blossomManager = new BlossomManager(this);
this.unlockedForces = new HashSet<>(); this.unlockedForces = new HashSet<>();
this.sceneEntity = new EntityScene(this); this.sceneEntity = new EntityScene(this);
this.scheduler = new ServerTaskScheduler();
} }
public int getId() { public int getId() {
@ -106,6 +111,13 @@ public final class Scene {
return this.getPlayers().size(); return this.getPlayers().size();
} }
/**
* @return The scene's world's host.
*/
public Player getHost() {
return this.getWorld().getHost();
}
public GameEntity getEntityById(int id) { public GameEntity getEntityById(int id) {
// Check if the scene's entity ID is referenced. // Check if the scene's entity ID is referenced.
if (id == 0x13800001) return this.sceneEntity; if (id == 0x13800001) return this.sceneEntity;
@ -524,6 +536,10 @@ public final class Scene {
return; return;
} }
if (!isPaused) {
this.getScheduler().runTasks();
}
if (this.getScriptManager().isInit()) { if (this.getScriptManager().isInit()) {
// this.checkBlocks(); // this.checkBlocks();
this.checkGroups(); this.checkGroups();
@ -678,6 +694,29 @@ public final class Scene {
this.afterLoadedCallbacks.add(runnable); this.afterLoadedCallbacks.add(runnable);
} }
/**
* Invoked when a player initializes loading the scene.
*
* @param player The player that initialized loading the scene.
*/
public void playerSceneInitialized(Player player) {
// Check if the player is the host.
if (!player.equals(this.getHost())) return;
// Run all callbacks.
this.afterHostInitCallbacks.forEach(Runnable::run);
this.afterHostInitCallbacks.clear();
}
/**
* Run a callback when the host initializes loading the scene.
*
* @param runnable The callback to be executed.
*/
public void runWhenHostInitialized(Runnable runnable) {
this.afterHostInitCallbacks.add(runnable);
}
public int getEntityLevel(int baseLevel, int worldLevelOverride) { public int getEntityLevel(int baseLevel, int worldLevelOverride) {
int level = worldLevelOverride > 0 ? worldLevelOverride + baseLevel - 22 : baseLevel; int level = worldLevelOverride > 0 ? worldLevelOverride + baseLevel - 22 : baseLevel;
level = Math.min(level, 100); level = Math.min(level, 100);

View File

@ -10,7 +10,7 @@ import emu.grasscutter.data.server.Grid;
import emu.grasscutter.database.DatabaseHelper; import emu.grasscutter.database.DatabaseHelper;
import emu.grasscutter.game.entity.*; import emu.grasscutter.game.entity.*;
import emu.grasscutter.game.entity.gadget.platform.BaseRoute; import emu.grasscutter.game.entity.gadget.platform.BaseRoute;
import emu.grasscutter.game.props.EntityType; import emu.grasscutter.game.props.EntityIdType;
import emu.grasscutter.game.quest.*; import emu.grasscutter.game.quest.*;
import emu.grasscutter.game.world.*; import emu.grasscutter.game.world.*;
import emu.grasscutter.net.proto.VisionTypeOuterClass; import emu.grasscutter.net.proto.VisionTypeOuterClass;
@ -99,7 +99,13 @@ public class SceneScriptManager {
} }
public SceneConfig getConfig() { public SceneConfig getConfig() {
return this.isInit ? this.meta.config : null; for (int i = 0; i < 10; ++i) {
if (this.isInit) {
return this.meta.config;
}
Utils.sleep(100);
}
return null;
} }
public Map<Integer, SceneBlock> getBlocks() { public Map<Integer, SceneBlock> getBlocks() {
@ -209,8 +215,15 @@ public class SceneScriptManager {
var suiteData = group.getSuiteByIndex(suiteIndex); var suiteData = group.getSuiteByIndex(suiteIndex);
if (suiteData == null) { if (suiteData == null) {
Grasscutter.getLogger().warn("Group {} suite {} not found", group.id, suiteIndex); Grasscutter.getLogger().warn("Group {} suite {} not found", group.id, suiteIndex);
group.setLoaded(false);
group.load(this.scene.getId());
suiteData = group.getSuiteByIndex(suiteIndex);
if (suiteData == null) {
return 0; return 0;
} }
Grasscutter.getLogger()
.error("Group {} suite {} nvm, I found it. This is BAD", group.id, suiteIndex);
}
int prevSuiteIndex = groupInstance.getActiveSuiteId(); int prevSuiteIndex = groupInstance.getActiveSuiteId();
boolean waitForOne = false; boolean waitForOne = false;
@ -627,28 +640,25 @@ public class SceneScriptManager {
// add other types of entity // add other types of entity
var entities = var entities =
getScene().getEntities().values().stream() getScene().getEntities().values().stream()
.filter( .filter(e -> region.getMetaRegion().contains(e.getPosition()))
e ->
e.getEntityType() == EntityType.Avatar
&& region.getMetaRegion().contains(e.getPosition()))
.toList(); .toList();
var entitiesIds = entities.stream().map(GameEntity::getId).toList();
var enterEntities =
entitiesIds.stream().filter(e -> !region.getEntities().contains(e)).toList();
var leaveEntities =
region.getEntities().stream().filter(e -> !entitiesIds.contains(e)).toList();
entities.forEach(region::addEntity); entities.forEach(region::addEntity);
var targetId = 0; for (var targetId : enterEntities) {
if (entities.size() > 0) {
targetId = entities.get(0).getId();
}
if (region.entityHasEntered()) {
Grasscutter.getLogger() Grasscutter.getLogger()
.trace("Call EVENT_ENTER_REGION_{}", region.getMetaRegion().config_id); .trace("Call EVENT_ENTER_REGION_{}", region.getMetaRegion().config_id);
this.callEvent( this.callEvent(
new ScriptArgs(region.getGroupId(), EventType.EVENT_ENTER_REGION, region.getConfigId()) new ScriptArgs(region.getGroupId(), EventType.EVENT_ENTER_REGION, region.getConfigId())
.setEventSource(EntityType.Avatar.getValue()) .setEventSource(EntityIdType.toEntityType(targetId >> 24).getValue())
.setSourceEntityId(region.getId()) .setSourceEntityId(region.getId())
.setTargetEntityId(targetId)); .setTargetEntityId(targetId));
region.resetNewEntities();
} }
for (var entityId : region.getEntities()) { for (var entityId : region.getEntities()) {
@ -658,14 +668,12 @@ public class SceneScriptManager {
} }
} }
if (region.entityHasLeft()) { for (var targetId : leaveEntities) {
this.callEvent( this.callEvent(
new ScriptArgs(region.getGroupId(), EventType.EVENT_LEAVE_REGION, region.getConfigId()) new ScriptArgs(region.getGroupId(), EventType.EVENT_LEAVE_REGION, region.getConfigId())
.setEventSource(EntityType.Avatar.getValue()) .setEventSource(EntityIdType.toEntityType(targetId >> 24).getValue())
.setSourceEntityId(region.getId()) .setSourceEntityId(region.getId())
.setTargetEntityId(region.getFirstEntityId())); .setTargetEntityId(targetId));
region.resetNewEntities();
} }
} }
} }
@ -893,7 +901,6 @@ public class SceneScriptManager {
.toList() .toList()
.get(0); .get(0);
this.getScene().getPlayers().forEach(p -> p.onEnterRegion(region.getMetaRegion())); this.getScene().getPlayers().forEach(p -> p.onEnterRegion(region.getMetaRegion()));
this.deregisterRegion(region.getMetaRegion());
} else if (trigger.getEvent() == EventType.EVENT_LEAVE_REGION) { } else if (trigger.getEvent() == EventType.EVENT_LEAVE_REGION) {
var region = var region =
this.regions.values().stream() this.regions.values().stream()
@ -901,7 +908,6 @@ public class SceneScriptManager {
.toList() .toList()
.get(0); .get(0);
this.getScene().getPlayers().forEach(p -> p.onLeaveRegion(region.getMetaRegion())); this.getScene().getPlayers().forEach(p -> p.onLeaveRegion(region.getMetaRegion()));
this.deregisterRegion(region.getMetaRegion());
} }
if (trigger.getEvent() == EVENT_TIMER_EVENT) { if (trigger.getEvent() == EVENT_TIMER_EVENT) {

View File

@ -8,37 +8,25 @@ import emu.grasscutter.game.dungeons.challenge.enums.FatherChallengeProperty;
import emu.grasscutter.game.dungeons.challenge.factory.ChallengeFactory; import emu.grasscutter.game.dungeons.challenge.factory.ChallengeFactory;
import emu.grasscutter.game.entity.*; import emu.grasscutter.game.entity.*;
import emu.grasscutter.game.entity.gadget.GadgetWorktop; import emu.grasscutter.game.entity.gadget.GadgetWorktop;
import emu.grasscutter.game.entity.gadget.platform.ConfigRoute; import emu.grasscutter.game.entity.gadget.platform.*;
import emu.grasscutter.game.entity.gadget.platform.PointArrayRoute; import emu.grasscutter.game.props.*;
import emu.grasscutter.game.props.ClimateType; import emu.grasscutter.game.quest.enums.*;
import emu.grasscutter.game.props.EntityIdType; import emu.grasscutter.game.world.*;
import emu.grasscutter.game.props.EntityType;
import emu.grasscutter.game.quest.enums.QuestCond;
import emu.grasscutter.game.quest.enums.QuestContent;
import emu.grasscutter.game.quest.enums.QuestState;
import emu.grasscutter.game.world.SceneGroupInstance;
import emu.grasscutter.net.proto.EnterTypeOuterClass; import emu.grasscutter.net.proto.EnterTypeOuterClass;
import emu.grasscutter.net.proto.VisionTypeOuterClass.VisionType; import emu.grasscutter.net.proto.VisionTypeOuterClass.VisionType;
import emu.grasscutter.scripts.constants.EventType; import emu.grasscutter.scripts.constants.*;
import emu.grasscutter.scripts.constants.GroupKillPolicy; import emu.grasscutter.scripts.data.*;
import emu.grasscutter.scripts.data.SceneGroup;
import emu.grasscutter.scripts.data.SceneObject;
import emu.grasscutter.scripts.data.ScriptArgs;
import emu.grasscutter.server.packet.send.*; import emu.grasscutter.server.packet.send.*;
import emu.grasscutter.game.world.Position;
import io.netty.util.concurrent.FastThreadLocal; import io.netty.util.concurrent.FastThreadLocal;
import lombok.val; import lombok.val;
import org.luaj.vm2.LuaTable; import org.luaj.vm2.*;
import org.luaj.vm2.LuaValue; import org.slf4j.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.annotation.Nullable; import javax.annotation.Nullable;
import java.util.*; import java.util.*;
import static emu.grasscutter.game.props.EnterReason.Lua; import static emu.grasscutter.game.props.EnterReason.Lua;
import static emu.grasscutter.scripts.ScriptUtils.luaToPos; import static emu.grasscutter.scripts.ScriptUtils.*;
import static emu.grasscutter.scripts.ScriptUtils.posToLua;
import static emu.grasscutter.scripts.constants.GroupKillPolicy.*; import static emu.grasscutter.scripts.constants.GroupKillPolicy.*;
@SuppressWarnings("unused") @SuppressWarnings("unused")
@ -256,12 +244,6 @@ public class ScriptLib {
Grasscutter.getLogger().warn("trying to get suite that doesn't exist: {} {}", groupId, suite); Grasscutter.getLogger().warn("trying to get suite that doesn't exist: {} {}", groupId, suite);
return 1; return 1;
} }
// avoid spawn wrong monster
if(getSceneScriptManager().getScene().getChallenge() != null)
if(!getSceneScriptManager().getScene().getChallenge().inProgress() ||
getSceneScriptManager().getScene().getChallenge().getGroup().id != groupId){
return 0;
}
this.getSceneScriptManager().addGroupSuite(groupInstance, suiteData); this.getSceneScriptManager().addGroupSuite(groupInstance, suiteData);
return 0; return 0;
@ -416,12 +398,41 @@ public class ScriptLib {
val old = variables.getOrDefault(var, 0); val old = variables.getOrDefault(var, 0);
variables.put(var, old + value); variables.put(var, old + value);
logger.debug("[LUA] Call ChangeGroupVariableValue with {},{}",
old, old+value);
getSceneScriptManager().callEvent(new ScriptArgs(groupId, EventType.EVENT_VARIABLE_CHANGE, old+value, old).setEventSource(var)); getSceneScriptManager().callEvent(new ScriptArgs(groupId, EventType.EVENT_VARIABLE_CHANGE, old+value, old).setEventSource(var));
return LuaValue.ZERO; return LuaValue.ZERO;
} }
public int GetGroupVariableValueByGroup(String var, int groupId){
logger.debug("[LUA] Call GetGroupVariableValueByGroup with {},{}",
var,groupId);
return getSceneScriptManager().getVariables(groupId).getOrDefault(var, 0);
}
public int SetGroupVariableValueByGroup(String var, int value, int groupId){
logger.debug("[LUA] Call SetGroupVariableValueByGroup with {},{},{}",
var,value,groupId);
val variables = getSceneScriptManager().getVariables(groupId);
val old = variables.getOrDefault(var, value);
variables.put(var, value);
getSceneScriptManager().callEvent(new ScriptArgs(groupId, EventType.EVENT_VARIABLE_CHANGE, value, old).setEventSource(var));
return 0;
}
public int ChangeGroupVariableValueByGroup(String var, int value, int groupId){
logger.debug("[LUA] Call ChangeGroupVariableValueByGroup with {},{}",
var,groupId);
val variables = getSceneScriptManager().getVariables(groupId);
val old = variables.getOrDefault(var, 0);
variables.put(var, old + value);
getSceneScriptManager().callEvent(new ScriptArgs(groupId, EventType.EVENT_VARIABLE_CHANGE, old+value, old).setEventSource(var));
return 0;
}
/** /**
* Set the actions and triggers to designated group * Set the actions and triggers to designated group
*/ */
@ -532,22 +543,22 @@ public class ScriptLib {
var entity = scene.getEntityByConfigId(cfgId); var entity = scene.getEntityByConfigId(cfgId);
if (entity == null) return 2; if (entity == null) return 2;
scene.broadcastPacket( scene.runWhenHostInitialized(() -> scene.broadcastPacket(
new PacketServerGlobalValueChangeNotify(entity, sgvName, value)); new PacketServerGlobalValueChangeNotify(entity, sgvName, value)));
return 0; return 0;
} }
public int GetGroupVariableValueByGroup(String name, int groupId){ public int SetEntityServerGlobalValueByEntityId(int entityId, String sgvName, int value) {
logger.debug("[LUA] Call GetGroupVariableValueByGroup with {},{}", logger.debug("[LUA] Call SetEntityServerGlobalValueByEntityId with {}, {}, {}",
name,groupId); entityId, sgvName, value);
return getSceneScriptManager().getVariables(groupId).getOrDefault(name, 0); var scriptManager = this.getSceneScriptManager();
} if (scriptManager == null) return 1;
public int ChangeGroupVariableValueByGroup(String name, int value, int groupId){
logger.debug("[LUA] Call ChangeGroupVariableValueByGroup with {},{}", var scene = scriptManager.getScene();
name,groupId);
//TODO test scene.runWhenHostInitialized(() -> scene.broadcastPacket(
getSceneScriptManager().getVariables(groupId).put(name, value); new PacketServerGlobalValueChangeNotify(entityId, sgvName, value)));
return 0; return 0;
} }
@ -575,14 +586,6 @@ public class ScriptLib {
return 0; return 0;
} }
public int SetGroupVariableValueByGroup(String key, int value, int groupId){
logger.debug("[LUA] Call SetGroupVariableValueByGroup with {},{},{}",
key,value,groupId);
getSceneScriptManager().getVariables(groupId).put(key, value);
return 0;
}
public int CreateMonster(LuaTable table){ public int CreateMonster(LuaTable table){
logger.debug("[LUA] Call CreateMonster with {}", logger.debug("[LUA] Call CreateMonster with {}",
printTable(table)); printTable(table));
@ -959,7 +962,7 @@ public class ScriptLib {
return 0; return 0;
} }
public int EndTimeAxis(String var1){ public int EndTimeAxis(String var1){
logger.warn("[LUA] Call unimplemented EndTimeAxis with {} {} {}", var1); logger.warn("[LUA] Call unimplemented EndTimeAxis with {}", var1);
//TODO implement var1 == name? //TODO implement var1 == name?
return 0; return 0;
} }
@ -1075,6 +1078,12 @@ public class ScriptLib {
return 0; return 0;
} }
public int ClearPlayerEyePoint(int var1){
logger.warn("[LUA] Call unimplemented ClearPlayerEyePoint with {}", var1);
//TODO implement
return 0;
}
public int MoveAvatarByPointArray(int uid, int targetId, LuaTable var3, String var4){ public int MoveAvatarByPointArray(int uid, int targetId, LuaTable var3, String var4){
logger.warn("[LUA] Call unimplemented MoveAvatarByPointArray with {} {} {} {}", uid, targetId, printTable(var3), var4); logger.warn("[LUA] Call unimplemented MoveAvatarByPointArray with {} {} {} {}", uid, targetId, printTable(var3), var4);
//TODO implement var3 contains int speed, var4 is a json string //TODO implement var3 contains int speed, var4 is a json string
@ -1133,7 +1142,7 @@ public class ScriptLib {
} }
public int PlayCutSceneWithParam(int cutsceneId, int var2, LuaTable var3){ public int PlayCutSceneWithParam(int cutsceneId, int var2, LuaTable var3){
logger.warn("[LUA] Call unimplemented PlayCutScene with {} {}", cutsceneId, var2, var3); logger.warn("[LUA] Call unimplemented PlayCutScene with {} {} {}", cutsceneId, var2, var3);
//TODO implement //TODO implement
return 0; return 0;
} }
@ -1310,6 +1319,13 @@ public class ScriptLib {
} }
configRoute.setRouteId(routeId); configRoute.setRouteId(routeId);
configRoute.setStartIndex(0);
configRoute.setStarted(false);
for(var task : configRoute.getScheduledIndexes()) {
sceneScriptManager.get().getScene().getScheduler().cancelTask(task);
}
configRoute.getScheduledIndexes().clear();
sceneScriptManager.get().getScene().broadcastPacket(new PacketPlatformChangeRouteNotify(entityGadget)); sceneScriptManager.get().getScene().broadcastPacket(new PacketPlatformChangeRouteNotify(entityGadget));
return 0; return 0;
} }

View File

@ -19,10 +19,10 @@ public class ScriptArgs {
} }
public ScriptArgs(int groupId, int eventType, int param1, int param2) { public ScriptArgs(int groupId, int eventType, int param1, int param2) {
this.group_id = groupId;
this.type = eventType; this.type = eventType;
this.param1 = param1; this.param1 = param1;
this.param2 = param2; this.param2 = param2;
this.group_id = groupId;
} }
public int getParam1() { public int getParam1() {

View File

@ -113,6 +113,7 @@ public final class HttpServer {
} }
serverConnector.setPort(HTTP_INFO.bindPort); serverConnector.setPort(HTTP_INFO.bindPort);
serverConnector.setHost(HTTP_INFO.bindAddress);
server.setConnectors(new ServerConnector[]{serverConnector}); server.setConnectors(new ServerConnector[]{serverConnector});
return server; return server;

View File

@ -13,42 +13,41 @@ public class HandlerEnterSceneDoneReq extends PacketHandler {
@Override @Override
public void handle(GameSession session, byte[] header, byte[] payload) throws Exception { public void handle(GameSession session, byte[] header, byte[] payload) throws Exception {
var player = session.getPlayer();
// Finished loading // Finished loading
session.getPlayer().setSceneLoadState(SceneLoadState.LOADED); player.setSceneLoadState(SceneLoadState.LOADED);
// Done // Done
session.send(new PacketPlayerTimeNotify(session.getPlayer())); // Probably not the right place session.send(new PacketPlayerTimeNotify(player)); // Probably not the right place
// Spawn player in world // Spawn player in world
session.getPlayer().getScene().spawnPlayer(session.getPlayer()); player.getScene().spawnPlayer(player);
// Spawn other entites already in world // Spawn other entites already in world
session.getPlayer().getScene().showOtherEntities(session.getPlayer()); player.getScene().showOtherEntities(player);
// Locations // Locations
session.send(new PacketWorldPlayerLocationNotify(session.getPlayer().getWorld())); session.send(new PacketWorldPlayerLocationNotify(player.getWorld()));
session.send(new PacketScenePlayerLocationNotify(session.getPlayer().getScene())); session.send(new PacketScenePlayerLocationNotify(player.getScene()));
session.send(new PacketWorldPlayerRTTNotify(session.getPlayer().getWorld())); session.send(new PacketWorldPlayerRTTNotify(player.getWorld()));
// spawn NPC // spawn NPC
session.getPlayer().getScene().loadNpcForPlayerEnter(session.getPlayer()); player.getScene().loadNpcForPlayerEnter(player);
// notify client to load the npc for quest // notify client to load the npc for quest
var questGroupSuites = var questGroupSuites = player.getQuestManager().getSceneGroupSuite(player.getSceneId());
session.getPlayer().getQuestManager().getSceneGroupSuite(session.getPlayer().getSceneId());
session.getPlayer().getScene().loadGroupForQuest(questGroupSuites); player.getScene().loadGroupForQuest(questGroupSuites);
Grasscutter.getLogger() Grasscutter.getLogger()
.trace( .trace("Loaded Scene {} Quest(s) Groupsuite(s): {}", player.getSceneId(), questGroupSuites);
"Loaded Scene {} Quest(s) Groupsuite(s): {}",
session.getPlayer().getSceneId(),
questGroupSuites);
session.send(new PacketGroupSuiteNotify(questGroupSuites)); session.send(new PacketGroupSuiteNotify(questGroupSuites));
// Reset timer for sending player locations // Reset timer for sending player locations
session.getPlayer().resetSendPlayerLocTime(); player.resetSendPlayerLocTime();
// Rsp // Rsp
session.send(new PacketEnterSceneDoneRsp(session.getPlayer())); session.send(new PacketEnterSceneDoneRsp(player));
} }
} }

View File

@ -28,6 +28,7 @@ public class HandlerPostEnterSceneReq extends PacketHandler {
if (dungeonManager != null) dungeonManager.startDungeon(); if (dungeonManager != null) dungeonManager.startDungeon();
} }
} }
questManager.queueEvent(QuestContent.QUEST_CONTENT_LEAVE_SCENE, scene.getPrevScene());
session.send(new PacketPostEnterSceneRsp(session.getPlayer())); session.send(new PacketPostEnterSceneRsp(session.getPlayer()));
} }

View File

@ -13,28 +13,33 @@ public class HandlerSceneInitFinishReq extends PacketHandler {
@Override @Override
public void handle(GameSession session, byte[] header, byte[] payload) throws Exception { public void handle(GameSession session, byte[] header, byte[] payload) throws Exception {
var player = session.getPlayer();
var world = player.getWorld();
// Info packets // Info packets
session.send(new PacketServerTimeNotify()); session.send(new PacketServerTimeNotify());
session.send(new PacketWorldPlayerInfoNotify(session.getPlayer().getWorld())); session.send(new PacketWorldPlayerInfoNotify(world));
session.send(new PacketWorldDataNotify(session.getPlayer().getWorld())); session.send(new PacketWorldDataNotify(world));
session.send(new PacketPlayerWorldSceneInfoListNotify()); session.send(new PacketPlayerWorldSceneInfoListNotify());
session.send(new BasePacket(PacketOpcodes.SceneForceUnlockNotify)); session.send(new BasePacket(PacketOpcodes.SceneForceUnlockNotify));
session.send(new PacketHostPlayerNotify(session.getPlayer().getWorld())); session.send(new PacketHostPlayerNotify(world));
session.send(new PacketSceneTimeNotify(session.getPlayer())); session.send(new PacketSceneTimeNotify(player));
session.send(new PacketPlayerGameTimeNotify(session.getPlayer())); session.send(new PacketPlayerGameTimeNotify(player));
session.send(new PacketPlayerEnterSceneInfoNotify(session.getPlayer())); session.send(new PacketPlayerEnterSceneInfoNotify(player));
session.send(new PacketSceneAreaWeatherNotify(session.getPlayer())); session.send(new PacketSceneAreaWeatherNotify(player));
session.send(new PacketScenePlayerInfoNotify(session.getPlayer().getWorld())); session.send(new PacketScenePlayerInfoNotify(world));
session.send(new PacketSceneTeamUpdateNotify(session.getPlayer())); session.send(new PacketSceneTeamUpdateNotify(player));
session.send(new PacketSyncTeamEntityNotify(session.getPlayer())); session.send(new PacketSyncTeamEntityNotify(player));
session.send(new PacketSyncScenePlayTeamEntityNotify(session.getPlayer())); session.send(new PacketSyncScenePlayTeamEntityNotify(player));
// Done Packet // Done Packet
session.send(new PacketSceneInitFinishRsp(session.getPlayer())); session.send(new PacketSceneInitFinishRsp(player));
// Set state // Set scene load state.
session.getPlayer().setSceneLoadState(SceneLoadState.INIT); player.setSceneLoadState(SceneLoadState.INIT);
// Run scene initialization.
player.getScene().playerSceneInitialized(player);
} }
} }

View File

@ -21,7 +21,6 @@ public class HandlerUnlockPersonalLineReq extends PacketHandler {
return; return;
} }
session.getPlayer().getQuestManager().addQuest(data.getStartQuestId());
session.getPlayer().addPersonalLine(data.getId()); session.getPlayer().addPersonalLine(data.getId());
session.getPlayer().useLegendaryKey(1); session.getPlayer().useLegendaryKey(1);

View File

@ -16,4 +16,14 @@ public final class PacketServerGlobalValueChangeNotify extends BasePacket {
.setValue(value) .setValue(value)
.setKeyHash(Utils.abilityHash(abilityHash))); .setKeyHash(Utils.abilityHash(abilityHash)));
} }
public PacketServerGlobalValueChangeNotify(int entityId, String abilityHash, int value) {
super(PacketOpcodes.ServerGlobalValueChangeNotify);
this.setData(
ServerGlobalValueChangeNotify.newBuilder()
.setEntityId(entityId)
.setValue(value)
.setKeyHash(Utils.abilityHash(abilityHash)));
}
} }

View File

@ -36,7 +36,7 @@ public final class ServerTask implements Runnable {
*/ */
public boolean shouldRun() { public boolean shouldRun() {
// Increase tick count. // Increase tick count.
var ticks = this.ticks++; ++this.ticks;
if (this.delay != -1 && this.considerDelay) { if (this.delay != -1 && this.considerDelay) {
// Check if the task should run. // Check if the task should run.
var shouldRun = ticks >= this.delay; var shouldRun = ticks >= this.delay;

View File

@ -64,9 +64,9 @@
"resources": { "resources": {
"loading": "Chargement des ressources...", "loading": "Chargement des ressources...",
"finish": "Chargement des ressources terminé.", "finish": "Chargement des ressources terminé.",
"custom": "🇺🇸Find additional resources at: 'Anime-Game-Servers/CustomGCResources'.", "custom": "Obtenez des ressources additionelles à: 'Anime-Game-Servers/CustomGCResources'.",
"missing_server": "🇺🇸To fully use questing, it is recommended to add the 'Server' folder.", "missing_server": "Pour utiliser pleinement les quetes, il est recommendé d'ajouter le dossier 'Server'.",
"missing_scenes": "🇺🇸To fully use questing, it is recommended to add the 'ScriptSceneData' folder." "missing_scenes": "Pour utiliser pleinement les quetes, il est recommendé d'ajouter le dossier 'ScriptSceneData'."
} }
} }
}, },
@ -87,7 +87,7 @@
"artifactId": "ID de l'artéfact invalide.", "artifactId": "ID de l'artéfact invalide.",
"avatarId": "ID de l'avatar invalide.", "avatarId": "ID de l'avatar invalide.",
"avatarLevel": "avatarLevel invalide.", "avatarLevel": "avatarLevel invalide.",
"cfgId": "🇺🇸Invalid cfg ID.", "cfgId": "ID de cfg invalide.",
"entityId": "ID de l'entité invalide.", "entityId": "ID de l'entité invalide.",
"itemId": "ID de l'objet invalide.", "itemId": "ID de l'objet invalide.",
"itemLevel": "Niveau de l'objet invalide.", "itemLevel": "Niveau de l'objet invalide.",
@ -171,8 +171,8 @@
"description": "Entrer dans un donjon" "description": "Entrer dans un donjon"
}, },
"entity": { "entity": {
"description": "🇺🇸Modify an existing entity's properties", "description": "Modifie les propriétés d'une entité existante",
"not_found_error": "🇺🇸Entity does not exist" "not_found_error": "L'entité n'existe pas"
}, },
"give": { "give": {
"usage_relic": "Utilisation: give <artifactID> [mainPropID] [<appendPropID>[,<times>]]... [lv<level 0-20>]", "usage_relic": "Utilisation: give <artifactID> [mainPropID] [<appendPropID>[,<times>]]... [lv<level 0-20>]",
@ -239,31 +239,31 @@
"not_found": "Quête introuvable.", "not_found": "Quête introuvable.",
"invalid_id": "ID de la quête invalide.", "invalid_id": "ID de la quête invalide.",
"description": "Ajoute ou termine une quête", "description": "Ajoute ou termine une quête",
"running": "🇺🇸Quest %s is %s (%s).", "running": "La quête %s est %s (%s).",
"talking": "🇺🇸Talk %s is %s for main quest %s (%s).", "talking": "Le dialogue %s est %s pour la quête principale %s (%s).",
"state": { "state": {
"none": "🇺🇸unknown (none)", "none": "Inconnu (none)",
"unstarted": "🇺🇸unfinished (not started, not completed)", "unstarted": "Pas finie (pas démarée, pas complêtée)",
"unfinished": "🇺🇸unfinished (started, not completed)", "unfinished": "Pas finie (Démarée, pas complêtée)",
"finished": "🇺🇸finished (completed)", "finished": "Terminée (complêtée)",
"failed": "🇺🇸finished (completed, but failed)", "failed": "Terminée (complêtée, mais échouée)",
"exists": "🇺🇸found", "exists": "Trouvée",
"not_exists": "🇺🇸not found" "not_exists": "Introuvable"
}, },
"enabled": "Quêtes activées." "enabled": "Quêtes activées."
}, },
"group": { "group": {
"invalid_groupid": "🇺🇸Invalid group ID.", "invalid_groupid": "ID de groupe invalide.",
"invalid_suiteid": "🇺🇸Invalid suite ID.", "invalid_suiteid": "ID de suite invalide.",
"group_not_found": "🇺🇸Group not found.", "group_not_found": "Groupe introuvable",
"description": "🇺🇸Alter group loading", "description": "Altère le chargement de groupe",
"refreshed": "🇺🇸Group %s refreshed." "refreshed": "Groupe %s actualisé"
}, },
"cutscene": { "cutscene": {
"description": "Joue une cinématique" "description": "Joue une cinématique"
}, },
"sound": { "sound": {
"description": "🇺🇸Plays a sound" "description": "Joue un son"
}, },
"reload": { "reload": {
"reload_start": "Rechargement de la configuration.", "reload_start": "Rechargement de la configuration.",
@ -408,7 +408,7 @@
"description": "Retire le bannissement d'un joueur" "description": "Retire le bannissement d'un joueur"
}, },
"troubleshoot": { "troubleshoot": {
"description": "🇺🇸Generate debugging information for troubleshooting." "description": "Génère des informations de déboguage pour le dépannage."
} }
}, },
"gacha": { "gacha": {
@ -462,9 +462,9 @@
"disabling_plugin": "Désactivation du plugin %s", "disabling_plugin": "Désactivation du plugin %s",
"disabling_failed": "Impossible de désactiver le plugin %s", "disabling_failed": "Impossible de désactiver le plugin %s",
"invalid_api": { "invalid_api": {
"not_present": "🇺🇸Plugin %s does not specify an API version.", "not_present": "Le plugin %s ne spécifie pas de version d'API.",
"lower": "🇺🇸Plugin %s is using API version %s, while the server is using API version %s.", "lower": "Le plugin %s utilise la version %s de l'API, alors que le serveur utilise la version %s.",
"outdated": "🇺🇸Plugin %s is using an outdated API method." "outdated": "Le plugin %s utilise une fonction obsolète de l'API."
} }
} }
} }

View File

@ -14,9 +14,9 @@
"request": "[Dispatch] Client %s %s richiesta: %s", "request": "[Dispatch] Client %s %s richiesta: %s",
"keystore": { "keystore": {
"general_error": "[Dispatch] Errore nel caricamento di keystore!", "general_error": "[Dispatch] Errore nel caricamento di keystore!",
"password_error": "[Dispatch] Impossibile caricare il keystore. Provando passwrd di default keystore...", "password_error": "[Dispatch] Impossibile caricare il keystore. Provando password di default keystore...",
"no_keystore_error": "[Dispatch] Nessun cert SSL trovato! Ritorno ad un server HTTP.", "no_keystore_error": "[Dispatch] Nessun cert SSL trovato! Ritornando ad un server HTTP.",
"default_password": "[Dispatch] La password di default del keystore è stata caricata con successo. Considera di impostare la password a 123456 in config.json." "default_password": "[Dispatch] La password di default del keystore è stata caricata con successo. Prova ad impostare la password a 123456 in config.json."
}, },
"authentication": { "authentication": {
"default_unable_to_verify": "[Authentication] [Autenticazione] Qualcosa ha chiamato metodo VerifyUser che non è disponibile nel gestore di autenticazione predefinito." "default_unable_to_verify": "[Authentication] [Autenticazione] Qualcosa ha chiamato metodo VerifyUser che non è disponibile nel gestore di autenticazione predefinito."
@ -24,8 +24,8 @@
"no_commands_error": "I comandi non sono supportati in modalità solo dispatch.", "no_commands_error": "I comandi non sono supportati in modalità solo dispatch.",
"unhandled_request_error": "[Dispatch] Potenziali %s richieste non gestite: %s.", "unhandled_request_error": "[Dispatch] Potenziali %s richieste non gestite: %s.",
"account": { "account": {
"login_attempt": "[Dispatch] Il client %s sta provando a fare il login.", "login_attempt": "[Dispatch] Il client %s sta provando ad accedere.",
"login_success": "[Dispatch] Client %s loggato come %s.", "login_success": "[Dispatch] Client %s ha accesso come %s.",
"login_max_player_limit": "[Dispatch] Client %s non è riuscito ad accedere: Il numero di giocatori online ha raggiunto il limite", "login_max_player_limit": "[Dispatch] Client %s non è riuscito ad accedere: Il numero di giocatori online ha raggiunto il limite",
"login_token_attempt": "[Dispatch] Il client %s sta tentando di accedere tramite token.", "login_token_attempt": "[Dispatch] Il client %s sta tentando di accedere tramite token.",
"login_token_error": "[Dispatch] Client %s non è riuscito ad accedere tramite token.", "login_token_error": "[Dispatch] Client %s non è riuscito ad accedere tramite token.",
@ -49,24 +49,24 @@
"router_error": "[Dispatch] Impossibile collegare il router." "router_error": "[Dispatch] Impossibile collegare il router."
}, },
"status": { "status": {
"free_software": "Grasscutter è un software GRATUITO. Se hai pagato per questo, potresti essere stato truffato. Homepage: https://github.com/Grasscutters/Grasscutter", "free_software": "Grasscutter è un software GRATUITO. Se lo hai pagato, potresti essere stato truffato. Homepage: https://github.com/Grasscutters/Grasscutter",
"starting": "Avvio di Grasscutter...", "starting": "Avvio di Grasscutter...",
"shutdown": "Chiusura in corso...", "shutdown": "Chiusura in corso...",
"done": "Fatto! Per aiuto, digita \"help\"", "done": "Fatto! Per aiuto, digita \"help\"",
"error": "Si è verificato un errore.", "error": "Si è verificato un errore.",
"welcome": "Benvenuto in Grasscutter!", "welcome": "Benvenuto a Grasscutter!",
"run_mode_error": "Modalità di esecuzione del server non valida: %s.", "run_mode_error": "Modalità di esecuzione del server non valida: %s.",
"run_mode_help": "La modalità di esecuzione del server deve essere 'HYBRID', 'DISPATCH_ONLY' o 'GAME_ONLY'. Impossibile avviare Grasscutter...", "run_mode_help": "La modalità di esecuzione del server deve essere 'HYBRID', 'DISPATCH_ONLY' o 'GAME_ONLY'. Impossibile avviare Grasscutter...",
"create_resources": "Creazione cartella risorse...", "create_resources": "Creazione cartella delle risorse...",
"resources_error": "Inserisci una copia di 'BinOutput' e 'ExcelBinOutput' nella cartella delle risorse.", "resources_error": "Inserisci una copia di 'BinOutput' e 'ExcelBinOutput' nella cartella delle risorse.",
"version": "Versione Grassscutter: %s-%s", "version": "Versione Grassscutter: %s-%s",
"game_version": "Versione del gioco: %s", "game_version": "Versione del gioco: %s",
"resources": { "resources": {
"loading": "Caricamento risorse...", "loading": "Caricamento risorse...",
"finish": "Terminato il caricamento delle risorse.", "finish": "Terminato il caricamento delle risorse.",
"custom": "🇺🇸Find additional resources at: 'Anime-Game-Servers/CustomGCResources'.", "custom": "Puoi trovare risorse aggiuntive a: 'Anime-Game-Servers/CustomGCResources'.",
"missing_server": "🇺🇸To fully use questing, it is recommended to add the 'Server' folder.", "missing_server": "Per usare questing al completo, è consigliato aggiungere la cartella 'Server'.",
"missing_scenes": "🇺🇸To fully use questing, it is recommended to add the 'ScriptSceneData' folder." "missing_scenes": "Per usare questing al completo, è consigliato aggiungere la cartella 'ScriptSceneData'."
} }
} }
}, },
@ -84,10 +84,10 @@
"set_for_to": "%s per %s impostato su %s.", "set_for_to": "%s per %s impostato su %s.",
"invalid": { "invalid": {
"amount": "Importo non valido.", "amount": "Importo non valido.",
"artifactId": "ID artefatto non valido.", "artifactId": "ID manufatto non valido.",
"avatarId": "ID avatar non valido.", "avatarId": "🇺🇸Invalid avatar ID.",
"avatarLevel": "Livello avatar non valido.", "avatarLevel": "🇺🇸Invalid avatarLevel.",
"cfgId": "🇺🇸Invalid cfg ID.", "cfgId": "cfg ID invalido.",
"entityId": "ID entità non valido.", "entityId": "ID entità non valido.",
"itemId": "ID articolo non valido.", "itemId": "ID articolo non valido.",
"itemLevel": "ItemLevel non valido.", "itemLevel": "ItemLevel non valido.",
@ -130,25 +130,25 @@
}, },
"achievement": { "achievement": {
"success": { "success": {
"grant": "🇺🇸Granted the achievement to %s.", "grant": "Concesso l'obbiettivo a %s.",
"revoke": "🇺🇸Revoked the achievement from %s.", "revoke": "Revocato l'obbiettivo from %s.",
"progress": "🇺🇸Set progress of %s's achievement(id: %s) to %s.", "progress": "Impostato il progresso della completazione dell'obbiettivo(id: %s) di %s a %s.",
"grantall": "🇺🇸Granted %s achievement(s) to %s.", "grantall": "Concesso/i l'/gli obbiettivo/i a %s.",
"revokeall": "🇺🇸Revoked %s achievement(s) from %s." "revokeall": "Revocato/i l'/gli obbiettivo/i da %s."
}, },
"fail": { "fail": {
"achievement_not_found": "🇺🇸Achievement not found.", "achievement_not_found": "Obbiettivo non trovato.",
"already_achieved": "🇺🇸%s has already achieved the achievement.", "already_achieved": "%s ha già completato l'obbiettivo.",
"not_yet_achieved": "🇺🇸%s hasn't achieved the achievement yet." "not_yet_achieved": "🇺🇸%s non ha ancora completato l'obbiettivo."
}, },
"description": "🇺🇸Grant, Revoke or Progress achievements." "description": "Concedi, revoca o imposta il progresso della completazione degli obbiettivi."
}, },
"announce": { "announce": {
"send_success": "Invia un annuncio con successo, puoi revocarlo /a revoca %s.", "send_success": "Invia un annuncio con successo, puoi revocarlo /a revoca %s.",
"refresh_success": "Aggiorna il file di configurazione dell'annuncio con successo. [Totale %s]", "refresh_success": "Aggiorna il file di configurazione dell'annuncio con successo. [Totale %s]",
"revoke_done": "Prova a revocare l'annuncio %s.", "revoke_done": "Prova a revocare l'annuncio %s.",
"not_found": "Impossibile trovare l'annuncio %s.", "not_found": "Impossibile trovare l'annuncio %s.",
"description": "Invia annuncio a tutti i giocatori online o gestisci l'annuncio del server" "description": "Invia un annuncio a tutti i giocatori online o gestisci l'annuncio del server"
}, },
"clear": { "clear": {
"weapons": "Armi cancellate per %s.", "weapons": "Armi cancellate per %s.",
@ -162,7 +162,7 @@
}, },
"coop": { "coop": {
"success": "Convocato %s nel mondo di %s.", "success": "Convocato %s nel mondo di %s.",
"description": "Forza qualcuno a unirsi al mondo degli altri. Se nessuno viene preso di mira, ti manda comunque in modalità cooperativa." "description": "Forza qualcuno a unirsi al mondo degli altri. Se il bersaglio non è impostato, ti manda comunque in modalità cooperativa."
}, },
"enter_dungeon": { "enter_dungeon": {
"changed": "Cambiato nel dungeon %s.", "changed": "Cambiato nel dungeon %s.",
@ -171,22 +171,22 @@
"description": "Entra in un dungeon" "description": "Entra in un dungeon"
}, },
"entity": { "entity": {
"description": "🇺🇸Modify an existing entity's properties", "description": "Modifica le proprietà di un entità esistente",
"not_found_error": "🇺🇸Entity does not exist" "not_found_error": "L'entità non esiste."
}, },
"give": { "give": {
"usage_relic": "Utilizzo: fornire <artifactID> [mainPropID] [<appendPropID>[,<times>]]... [lv<livello 0-20>]", "usage_relic": "Utilizzo: fornire <artifactID> [mainPropID] [<appendPropID>[,<times>]]... [lv<livello 0-20>]",
"illegal_relic": "Questo ID artefatto appartiene a un intervallo nella blacklist, potrebbe non essere quello che volevi.", "illegal_relic": "Questo ID manufatto appartiene a un intervallo nella blacklist, potrebbe non essere quello che volevi.",
"given": "Dato %s di %s a %s.", "given": "Dato %s di %s a %s.",
"given_with_level_and_refinement": "Dato %s con livello %s, perfezionamento %s %s volte a %s.", "given_with_level_and_refinement": "Dato %s con livello %s, perfezionamento %s %s volte a %s.",
"given_level": "Dato %s con livello %s %s volte a %s.", "given_level": "Dato %s con livello %s %s volte a %s.",
"given_avatar": "Dato %s con livello da %s a %s.", "given_avatar": "🇺🇸Given %s with level %s to %s.",
"giveall_success": "Ha dato tutti gli oggetti con successo.", "giveall_success": "Ha dato tutti gli oggetti con successo.",
"description": "Dà un oggetto a te o al giocatore specificato. Può anche dare tutte le armi, avatar e/o materiali, e può costruire artefatti personalizzati." "description": "Dà un oggetto a te o al giocatore specificato. Può anche dare tutte le armi, Avatar e/o materiali, e può costruire manufatti personalizzati."
}, },
"heal": { "heal": {
"success": "Tutti i personaggi sono stati curati.", "success": "Tutti i personaggi sono stati curati.",
"description": "Guarisci tutti i personaggi della tua squadra attuale." "description": "Guarisce tutti i personaggi della tua squadra attuale"
}, },
"help": { "help": {
"aliases": "Alias: ", "aliases": "Alias: ",
@ -199,26 +199,26 @@
}, },
"kick": { "kick": {
"player_kick_player": "Il giocatore [%s:%s] ha espulso un giocatore [%s:%s]", "player_kick_player": "Il giocatore [%s:%s] ha espulso un giocatore [%s:%s]",
"server_kick_player": "Ha preso a calci il giocatore [%s:%s]...", "server_kick_player": "È stato espulso il giocatore [%s:%s]...",
"description": "Espelle il giocatore specificato dal server (WIP)" "description": "Espelle il giocatore specificato dal server (WIP)"
}, },
"killall": { "killall": {
"scene_not_found_in_player_world": "Scena non trovata nel mondo del giocatore.", "scene_not_found_in_player_world": "Scena non trovata nel mondo del giocatore.",
"kill_monsters_in_scene": "Uccidere %s mostri nella scena %s.", "kill_monsters_in_scene": "Uccisi %s mostri nella scena %s.",
"description": "Uccidi tutte le entità" "description": "Uccide tutte le entità"
}, },
"killCharacter": { "killCharacter": {
"success": "Hai ucciso il personaggio attuale di %s.", "success": "Ucciso il personaggio attuale di %s.",
"description": "Uccide il personaggio attuale del giocatore" "description": "Uccide il personaggio attuale del giocatore"
}, },
"language": { "language": {
"current_language": "La lingua attuale è %s.", "current_language": "La lingua attuale è %s.",
"language_changed": "Lingua modificata in %s.", "language_changed": "Lingua cambiata a %s.",
"language_not_found": "Attualmente, il server non ha quella lingua.", "language_not_found": "Attualmente, il server non ha quella lingua.",
"description": "Mostra o cambia la lingua corrente" "description": "Mostra o cambia la lingua corrente"
}, },
"list": { "list": {
"success": "Ci sono %s player(s) online:", "success": "Ci sono %s player online:",
"description": "Lista player online" "description": "Lista player online"
}, },
"permission": { "permission": {
@ -231,7 +231,7 @@
}, },
"position": { "position": {
"success": "Coordinate: %s, %s, %s\nRotazione:%s, %s, %s\nID scena: %a", "success": "Coordinate: %s, %s, %s\nRotazione:%s, %s, %s\nID scena: %a",
"description": "Ottiene informazioni sulla posizione e sulla rotazione" "description": "Ottiene informazioni sulla posizione e sulla rotazione attuale del giocatore"
}, },
"quest": { "quest": {
"added": "Missione %s aggiunta.", "added": "Missione %s aggiunta.",
@ -239,39 +239,39 @@
"not_found": "Missione non trovata.", "not_found": "Missione non trovata.",
"invalid_id": "ID missione non valido.", "invalid_id": "ID missione non valido.",
"description": "Aggiungi o completa missioni", "description": "Aggiungi o completa missioni",
"running": "🇺🇸Quest %s is %s (%s).", "running": "Missione %s è %s (%s).",
"talking": "🇺🇸Talk %s is %s for main quest %s (%s).", "talking": "Talk %s è %s per la missione principale %s (%s).",
"state": { "state": {
"none": "🇺🇸unknown (none)", "none": "sconosciuta (nessuna)",
"unstarted": "🇺🇸unfinished (not started, not completed)", "unstarted": "incompleta (not cominciata, non completata)",
"unfinished": "🇺🇸unfinished (started, not completed)", "unfinished": "incompleta (cominciata, non completata)",
"finished": "🇺🇸finished (completed)", "finished": "completa (completata)",
"failed": "🇺🇸finished (completed, but failed)", "failed": "incompleta (completata, ma fallita)",
"exists": "🇺🇸found", "exists": "Missione esistente",
"not_exists": "🇺🇸not found" "not_exists": "Missione non esistente."
}, },
"enabled": "🇺🇸Questing enabled." "enabled": "Questing abilitato."
}, },
"group": { "group": {
"invalid_groupid": "🇺🇸Invalid group ID.", "invalid_groupid": "group ID non valido.",
"invalid_suiteid": "🇺🇸Invalid suite ID.", "invalid_suiteid": "suite ID non valido.",
"group_not_found": "🇺🇸Group not found.", "group_not_found": "group non trovato.",
"description": "🇺🇸Alter group loading", "description": "Carica Alter Group",
"refreshed": "🇺🇸Group %s refreshed." "refreshed": "Group %s ricaricato."
}, },
"cutscene": { "cutscene": {
"description": "Riproduce un filmato" "description": "Riproduce una cutscene"
}, },
"sound": { "sound": {
"description": "🇺🇸Plays a sound" "description": "Riproduce un suono"
}, },
"reload": { "reload": {
"reload_start": "Ricarica configurazione.", "reload_start": "Ricarica in configurazione.",
"reload_done": "Ricarica completa.", "reload_done": "Ricarica completa.",
"description": "Ricarica configurazione server" "description": "Ricarica la configurazione del server"
}, },
"resetConst": { "resetConst": {
"reset_all": "Ripristina le costellazioni di tutti gli avatar.", "reset_all": "Ripristina le costellazioni di tutti gli Avatar.",
"success": "Le costellazioni per %s sono state reimpostate. Effettua nuovamente il login per vedere le modifiche.", "success": "Le costellazioni per %s sono state reimpostate. Effettua nuovamente il login per vedere le modifiche.",
"description": "Reimposta il livello della costellazione sul tuo personaggio attivo corrente, dovrai reloggare dopo aver usato il comando per vedere eventuali modifiche" "description": "Reimposta il livello della costellazione sul tuo personaggio attivo corrente, dovrai reloggare dopo aver usato il comando per vedere eventuali modifiche"
}, },
@ -304,7 +304,7 @@
}, },
"sendMessage": { "sendMessage": {
"success": "Messaggio inviato.", "success": "Messaggio inviato.",
"description": "Invia un messaggio a un giocatore come server. Se usato senza target, invia a tutti i giocatori sul server." "description": "Invia un messaggio a un giocatore come server. Se usato senza bersaglio, invia a tutti i giocatori sul server."
}, },
"setConst": { "setConst": {
"range_error": "Il livello della costellazione deve essere compreso tra 0 e 6.", "range_error": "Il livello della costellazione deve essere compreso tra 0 e 6.",
@ -316,10 +316,10 @@
"description": "Imposta il livello di costellazione per il tuo attuale personaggio attivo" "description": "Imposta il livello di costellazione per il tuo attuale personaggio attivo"
}, },
"setFetterLevel": { "setFetterLevel": {
"range_error": "Il livello di restrizione deve essere compreso tra 0 e 10.", "range_error": "Il livello di amicizia deve essere compreso tra 0 e 10.",
"success": "Livello di restrizione impostato su %s.", "success": "Livello di amicizia impostato a %s.",
"level_error": "Livello restrizione non valido.", "level_error": "Livello di amicizia non valido.",
"description": "Imposta il tuo livello di restrizione per il tuo attuale personaggio attivo" "description": "Imposta il tuo livello di amicizia per il tuo attuale personaggio attivo"
}, },
"setProp": { "setProp": {
"description": "Imposta le proprietà dell'intero account. Cose come godmode possono essere abilitate in questo modo, oltre a cambiare cose come il pavimento dell'abisso sbloccato e il progresso del pass battaglia.\n\tValori per <prop> (senza distinzione tra maiuscole e minuscole): GodMode | UnlimitedStamina | UnlimitedEnergy | TowerLevel | WorldLevel | BPLevel | SetOpenState | UnsetOpenState | UnlockMap\n\t(cont.) vedi PlayerProperty enum per altri possibili valori, nella forma PROP_MAX_SPRING_VOLUME -> max_spring_volume" "description": "Imposta le proprietà dell'intero account. Cose come godmode possono essere abilitate in questo modo, oltre a cambiare cose come il pavimento dell'abisso sbloccato e il progresso del pass battaglia.\n\tValori per <prop> (senza distinzione tra maiuscole e minuscole): GodMode | UnlimitedStamina | UnlimitedEnergy | TowerLevel | WorldLevel | BPLevel | SetOpenState | UnsetOpenState | UnlockMap\n\t(cont.) vedi PlayerProperty enum per altri possibili valori, nella forma PROP_MAX_SPRING_VOLUME -> max_spring_volume"
@ -354,19 +354,19 @@
"team": { "team": {
"invalid_usage": "Utilizzo non valido.", "invalid_usage": "Utilizzo non valido.",
"invalid_index": "L'indice non è valido.", "invalid_index": "L'indice non è valido.",
"add_too_much": "Il server ti permette di avere al massimo %s avatar nella tua squadra.", "add_too_much": "Il server ti permette di avere al massimo %s personaggi nella tua squadra.",
"failed_to_add_avatar": "Impossibile aggiungere l'ID avatar %s.", "failed_to_add_avatar": "🇺🇸Failed to add avatar ID %s.",
"failed_to_parse_index": "Impossibile analizzare l'indice: %s", "failed_to_parse_index": "Impossibile analizzare l'indice: %s",
"remove_too_much": "Non puoi rimuovere tutti i tuoi avatar.", "remove_too_much": "Non puoi rimuovere tutti i tuoi personaggi.",
"ignore_index": "Indici ignorati: %s", "ignore_index": "Indici ignorati: %s",
"index_out_of_range": "L'indice che hai specificato non è compreso nell'intervallo.", "index_out_of_range": "L'indice che hai specificato non è compreso nell'intervallo.",
"failed_parse_avatar_id": "Impossibile analizzare l'ID avatar: %s", "failed_parse_avatar_id": "🇺🇸Failed to parse avatar ID: %s",
"avatar_already_in_team": "Avatar è già nel team.", "avatar_already_in_team": "🇺🇸Avatar is already in team.",
"avatar_not_found": "Avatar %s non trovato.", "avatar_not_found": "🇺🇸Avatar %s not found.",
"description": "Modifica manualmente la tua squadra." "description": "Modifica manualmente la tua squadra."
}, },
"teleportAll": { "teleportAll": {
"success": "Evoca tutti i giocatori nella tua posizione.", "success": "Teletrasportati tutti i giocatori nella tua posizione.",
"error": "Puoi usare questo comando solo in modalità MP.", "error": "Puoi usare questo comando solo in modalità MP.",
"description": "Teletrasporta tutti i giocatori del tuo mondo nella tua posizione" "description": "Teletrasporta tutti i giocatori del tuo mondo nella tua posizione"
}, },
@ -377,15 +377,15 @@
"description": "Cambia la posizione del giocatore" "description": "Cambia la posizione del giocatore"
}, },
"trialAvatarActivity": { "trialAvatarActivity": {
"description": "Manipolare le funzionalità dell'attività Avatar di prova. Ciò include la commutazione degli stati dei sotterranei e delle ricompense.", "description": "Manipola le funzionalità dell'attività Trial del personaggio. Ciò include la commutazione degli stati dei dungeon e delle ricompense.",
"not_found": "Dati giocatore attività avatar di prova non trovati. Forse l'attività non è abilitata.", "not_found": "Dati giocatore attività personaggi di prova non trovati. Forse l'attività non è abilitata.",
"invalid_param": "Parametro non valido.", "invalid_param": "Parametro non valido.",
"schedule_not_found": "Programma %s non trovato.", "schedule_not_found": "Programma %s non trovato.",
"success_schedule": "ID programma modificato in %s.", "success_schedule": "ID programma modificato in %s.",
"success_dungeon": "Stato del dungeon dell'avatar %s attivato.", "success_dungeon": "Stato del dungeon del personaggio %s attivato.",
"success_dungeon_all": "Modificato lo stato dei dungeon di tutti gli avatar.", "success_dungeon_all": "Modificato lo stato dei dungeon di tutti i personaggi.",
"success_reward": "Stato della ricompensa dell'avatar %s attivato/disattivato.", "success_reward": "Stato della ricompensa del personaggio %s attivato/disattivato.",
"success_reward_all": "Attivato/disattivato lo stato di ricompensa di tutti gli avatar." "success_reward_all": "Attivato/disattivato lo stato di ricompensa di tutti i personaggi."
}, },
"weather": { "weather": {
"success": "Imposta l'ID meteo su %s con il tipo di clima %s.", "success": "Imposta l'ID meteo su %s con il tipo di clima %s.",
@ -393,7 +393,7 @@
"description": "Cambia l'ID meteo e il tipo di clima. Gli ID meteo possono essere trovati in ./Resources/ExcelBinOutput/WeatherExcelConfigData.json.\nTipi di clima: soleggiato, nuvoloso, pioggia, temporale, neve, nebbia" "description": "Cambia l'ID meteo e il tipo di clima. Gli ID meteo possono essere trovati in ./Resources/ExcelBinOutput/WeatherExcelConfigData.json.\nTipi di clima: soleggiato, nuvoloso, pioggia, temporale, neve, nebbia"
}, },
"ban": { "ban": {
"success": "Soccesso.", "success": "Successo.",
"failure": "Fallito, giocatore non trovato.", "failure": "Fallito, giocatore non trovato.",
"invalid_time": "Impossibile analizzare il timestamp.", "invalid_time": "Impossibile analizzare il timestamp.",
"description": "Banna un giocatore" "description": "Banna un giocatore"
@ -408,7 +408,7 @@
"description": "Sbanna un giocatore" "description": "Sbanna un giocatore"
}, },
"troubleshoot": { "troubleshoot": {
"description": "🇺🇸Generate debugging information for troubleshooting." "description": "Genera informazioni di debugging per il troubleshooting."
} }
}, },
"gacha": { "gacha": {
@ -419,7 +419,7 @@
"available_three_stars": "Articoli a 3 stelle disponibili" "available_three_stars": "Articoli a 3 stelle disponibili"
}, },
"records": { "records": {
"title": "🇺🇸Gacha Records", "title": "Cronologia Gacha",
"date": "Data", "date": "Data",
"item": "oggetto" "item": "oggetto"
} }
@ -428,7 +428,7 @@
"handbook": { "handbook": {
"title": "Manuale GM", "title": "Manuale GM",
"title_commands": "Comandi", "title_commands": "Comandi",
"title_avatars": "Avatar", "title_avatars": "🇺🇸Avatars",
"title_items": "Articoli", "title_items": "Articoli",
"title_scenes": "Scene", "title_scenes": "Scene",
"title_monsters": "Mostri", "title_monsters": "Mostri",
@ -462,9 +462,9 @@
"disabling_plugin": "Disabilitazione plug-in: %s", "disabling_plugin": "Disabilitazione plug-in: %s",
"disabling_failed": "Impossibile disabilitare il plug-in: %s", "disabling_failed": "Impossibile disabilitare il plug-in: %s",
"invalid_api": { "invalid_api": {
"not_present": "🇺🇸Plugin %s does not specify an API version.", "not_present": "Il plugin %s non ha una versione API specifica.",
"lower": "🇺🇸Plugin %s is using API version %s, while the server is using API version %s.", "lower": "Il plugin %s sta usando la versione API %s, mentre il server sta usando la versione API %s.",
"outdated": "🇺🇸Plugin %s is using an outdated API method." "outdated": "Il plugin %s sta usando un metodo API obsoleto."
} }
} }
} }

View File

@ -64,9 +64,9 @@
"resources": { "resources": {
"loading": "Загружаем ресурсы...", "loading": "Загружаем ресурсы...",
"finish": "Загрузка ресурсов завершена.", "finish": "Загрузка ресурсов завершена.",
"custom": "🇺🇸Find additional resources at: 'Anime-Game-Servers/CustomGCResources'.", "custom": "Ищите дополнительные ресурсы в репозитории: 'Anime-Game-Servers/CustomGCResources'.",
"missing_server": "🇺🇸To fully use questing, it is recommended to add the 'Server' folder.", "missing_server": "Для полноценной поддержки квестов, рекомендуется добавить папку 'Server'.",
"missing_scenes": "🇺🇸To fully use questing, it is recommended to add the 'ScriptSceneData' folder." "missing_scenes": "Для полноценной поддержки квестов, рекомендуется добавить папку 'ScriptSceneData'."
} }
} }
}, },
@ -87,7 +87,7 @@
"artifactId": "Некорректный ID артефакта.", "artifactId": "Некорректный ID артефакта.",
"avatarId": "Некорректный ID персонажа.", "avatarId": "Некорректный ID персонажа.",
"avatarLevel": "Некорректный уровень персонажа (avatarLevel).", "avatarLevel": "Некорректный уровень персонажа (avatarLevel).",
"cfgId": "🇺🇸Invalid cfg ID.", "cfgId": "Некорректный ID конфига.",
"entityId": "Некорректный ID сущности.", "entityId": "Некорректный ID сущности.",
"itemId": "Некорректный ID предмета.", "itemId": "Некорректный ID предмета.",
"itemLevel": "Некорректный уровень предмета (itemLevel).", "itemLevel": "Некорректный уровень предмета (itemLevel).",
@ -171,8 +171,8 @@
"description": "Позволяет войти в подземелье" "description": "Позволяет войти в подземелье"
}, },
"entity": { "entity": {
"description": "🇺🇸Modify an existing entity's properties", "description": "Изменяет свойства выбранной сущности",
"not_found_error": "🇺🇸Entity does not exist" "not_found_error": "Сущность не найдена"
}, },
"give": { "give": {
"usage_relic": "Применение: give <ID_артефакта> [ID_глав_хар-ки] [<ID доп_хар-ки>[,<раз>]]... [lv<уровень 0-20>]", "usage_relic": "Применение: give <ID_артефакта> [ID_глав_хар-ки] [<ID доп_хар-ки>[,<раз>]]... [lv<уровень 0-20>]",
@ -239,31 +239,31 @@
"not_found": "Квест не найден.", "not_found": "Квест не найден.",
"invalid_id": "Некорректный ID квеста.", "invalid_id": "Некорректный ID квеста.",
"description": "Добавляет (add) или завершает (finish) квесты", "description": "Добавляет (add) или завершает (finish) квесты",
"running": "🇺🇸Quest %s is %s (%s).", "running": "Квест %s %s (%s).",
"talking": "🇺🇸Talk %s is %s for main quest %s (%s).", "talking": "Разговор %s %s для главного квеста %s (%s).",
"state": { "state": {
"none": "🇺🇸unknown (none)", "none": "неизвестно (none)",
"unstarted": "🇺🇸unfinished (not started, not completed)", "unstarted": "незавершен (не начат)",
"unfinished": "🇺🇸unfinished (started, not completed)", "unfinished": "незавершен (начат, но не закончен)",
"finished": "🇺🇸finished (completed)", "finished": "завершен",
"failed": "🇺🇸finished (completed, but failed)", "failed": "завершен (но провален)",
"exists": "🇺🇸found", "exists": "найден",
"not_exists": "🇺🇸not found" "not_exists": "не найден"
}, },
"enabled": "🇺🇸Questing enabled." "enabled": "Квестинг включен."
}, },
"group": { "group": {
"invalid_groupid": "🇺🇸Invalid group ID.", "invalid_groupid": "Некорректный ID Группы.",
"invalid_suiteid": "🇺🇸Invalid suite ID.", "invalid_suiteid": "Некорректный ID Комплекта.",
"group_not_found": "🇺🇸Group not found.", "group_not_found": "Группа не найдена.",
"description": "🇺🇸Alter group loading", "description": "Меняет загрузку группы",
"refreshed": "🇺🇸Group %s refreshed." "refreshed": "Группа %s обновлена."
}, },
"cutscene": { "cutscene": {
"description": "Odtwarza przerywnik filmowy" "description": "Odtwarza przerywnik filmowy"
}, },
"sound": { "sound": {
"description": "🇺🇸Plays a sound" "description": "Проигрывает звук"
}, },
"reload": { "reload": {
"reload_start": "Перезагружаем файл конфигурации.", "reload_start": "Перезагружаем файл конфигурации.",
@ -408,7 +408,7 @@
"description": "Разблокировывает доступ к серверу (\"разбанивает\")" "description": "Разблокировывает доступ к серверу (\"разбанивает\")"
}, },
"troubleshoot": { "troubleshoot": {
"description": "🇺🇸Generate debugging information for troubleshooting." "description": "Генерирует отладочную информацию для решения проблем."
} }
}, },
"gacha": { "gacha": {
@ -462,9 +462,9 @@
"disabling_plugin": "Отключаем Плагин: %s", "disabling_plugin": "Отключаем Плагин: %s",
"disabling_failed": "Ошибка отключения Плагина: %s", "disabling_failed": "Ошибка отключения Плагина: %s",
"invalid_api": { "invalid_api": {
"not_present": "🇺🇸Plugin %s does not specify an API version.", "not_present": "В Плагине %s не указана версия API.",
"lower": "🇺🇸Plugin %s is using API version %s, while the server is using API version %s.", "lower": "Плагин %s использует версию API %s, однако сервер работает на версии API %s.",
"outdated": "🇺🇸Plugin %s is using an outdated API method." "outdated": "Плагин %s использует устаревший API метод."
} }
} }
} }

View File

@ -1,32 +1,32 @@
{ {
"messages": { "messages": {
"game": { "game": {
"address_bind": "🇺🇸Game Server started at \u001b[1m\u001b[33m%s:%s\u001b[0m", "address_bind": "遊戲伺服器已於 \u001b[1m\u001b[33m%s:%s\u001b[0m 啟動",
"port_bind": "遊戲伺服器已成功啟動。端口號:%s", "port_bind": "遊戲伺服器已在連接埠 %s 上啟動",
"connect": "客戶端已連接至 %s", "connect": "客戶端 %s 已連線",
"disconnect": "客戶端 %s 已斷開連接。", "disconnect": "客戶端 %s 已斷開連",
"game_update_error": "遊戲更新時發生了錯誤。", "game_update_error": "遊戲更新時發生了錯誤。",
"command_error": "指令發生錯誤:" "command_error": "指令發生錯誤:"
}, },
"dispatch": { "dispatch": {
"address_bind": "🇺🇸[Dispatch] Dispatch server started at \u001b[1m\u001b[33m%s:%s\u001b[0m", "address_bind": "[Dispatch] Dispatch 伺服器已於 \u001b[1m\u001b[33m%s:%s\u001b[0m 啟動",
"port_bind": "[Dispatch] 伺服器已在端口 %s 上開啟。", "port_bind": "[Dispatch] Dispatch 伺服器已在連接埠 %s 上啟動",
"request": "[Dispatch] 客戶端 %s 請求:%s %s", "request": "[Dispatch] 客戶端 %s 請求:%s %s",
"keystore": { "keystore": {
"general_error": "[Dispatch] 載keystore文件時發生錯誤!", "general_error": "[Dispatch] 載keystore 檔案時發生錯誤!",
"password_error": "[Dispatch] 載 keystore 失敗。正在嘗試使用預設 keystore 密碼...", "password_error": "[Dispatch] 載 keystore 失敗。正在嘗試使用預設 keystore 密碼...",
"no_keystore_error": "[Dispatch] 未找到 SSL 憑證!已後降到 HTTP 伺服器。", "no_keystore_error": "[Dispatch] 未找到 SSL 憑證!已回退到 HTTP 伺服器。",
"default_password": "[Dispatch] 默認的 keystore 密碼加載成功。請考慮將 config.json 的憑證密碼設定成 123456。" "default_password": "[Dispatch] keystore 預設密碼載入成功。請考慮將 config.json 的憑證密碼設定成 123456。"
}, },
"authentication": { "authentication": {
"default_unable_to_verify": "[驗證系統] 稱為 verifyUser 方法的東西在默認身份驗證程序中不可用。" "default_unable_to_verify": "[驗證系統] 稱為 verifyUser 方法的東西在默認身份驗證程序中不可用。"
}, },
"no_commands_error": "此指令不適用於Dispatch-only模式。", "no_commands_error": "此指令不適用於僅限 Dispatch 模式。",
"unhandled_request_error": "[Dispatch] 潛在的未處理請求 %s 請求:%s", "unhandled_request_error": "[Dispatch] 潛在的未處理請求 %s 請求:%s",
"account": { "account": {
"login_attempt": "[Dispatch] 客戶端 %s 正在嘗試登入", "login_attempt": "[Dispatch] 客戶端 %s 正在嘗試登入",
"login_success": "[Dispatch] 客戶端 %s 已登入UID 為 %s", "login_success": "[Dispatch] 客戶端 %s 已登入UID 為 %s",
"login_max_player_limit": "[Dispatch] 客戶端 %s 登入失敗:線人數已滿", "login_max_player_limit": "[Dispatch] 客戶端 %s 登入失敗:線人數已滿",
"login_token_attempt": "[Dispatch] 客戶端 %s 正在嘗試用憑證登入", "login_token_attempt": "[Dispatch] 客戶端 %s 正在嘗試用憑證登入",
"login_token_error": "[Dispatch] 客戶端 %s 使用憑證登入失敗", "login_token_error": "[Dispatch] 客戶端 %s 使用憑證登入失敗",
"login_token_success": "[Dispatch] 客戶端 %s 已透過憑證登入UID 為 %s", "login_token_success": "[Dispatch] 客戶端 %s 已透過憑證登入UID 為 %s",
@ -34,27 +34,27 @@
"login_password_storage_error": "[Dispatch] 客戶端 %s 使用密碼登入失敗,因為該帳號在資料庫裡面沒有設定密碼。", "login_password_storage_error": "[Dispatch] 客戶端 %s 使用密碼登入失敗,因為該帳號在資料庫裡面沒有設定密碼。",
"combo_token_success": "[Dispatch] 客戶端 %s 交換憑證成功", "combo_token_success": "[Dispatch] 客戶端 %s 交換憑證成功",
"combo_token_error": "[Dispatch] 客戶端 %s 交換憑證失敗", "combo_token_error": "[Dispatch] 客戶端 %s 交換憑證失敗",
"account_login_create_success": "[Dispatch] 客戶端 %s 登入失敗: 已註冊UID為 %s 的帳號。", "account_login_create_success": "[Dispatch] 客戶端 %s 登入失敗已註冊 UID 為 %s 的帳號。",
"account_login_create_error": "[Dispatch] 客戶端 %s 登入失敗:帳號建立失敗。", "account_login_create_error": "[Dispatch] 客戶端 %s 登入失敗:帳號建立失敗。",
"account_login_exist_error": "[Dispatch] 客戶端 %s 登入失敗: 帳號不存在。", "account_login_exist_error": "[Dispatch] 客戶端 %s 登入失敗帳號不存在。",
"account_cache_error": "遊戲帳號緩存資訊錯誤", "account_cache_error": "遊戲帳號快取資訊錯誤",
"session_key_error": "對話密鑰不符。", "session_key_error": "工作階段金鑰錯誤。",
"username_error": "未找到此用戶名。", "username_error": "找不到此使用者名稱。",
"username_create_error": "未找到用戶名,建立失敗。", "username_create_error": "找不到使用者名稱,建立失敗。",
"password_error": "無效的密碼", "password_error": "密碼無效",
"password_length_error": "密碼長度必須大於或等於 8", "password_length_error": "密碼長度必須大於或等於 8",
"password_storage_error": "帳號沒有設定密碼,請聯繫伺服器管理員。", "password_storage_error": "您的帳號沒有設定密碼,請聯繫管理員。",
"server_max_player_limit": "伺服器線人數已滿" "server_max_player_limit": "伺服器線人數已滿"
}, },
"router_error": "[Dispatch] 無法附加路由。" "router_error": "[Dispatch] 無法附加路由。"
}, },
"status": { "status": {
"free_software": "Grasscutter 是免費開源軟體。如果你已經付錢了那你可能被騙了。主頁https://github.com/Grasscutters/Grasscutter", "free_software": "Grasscutter 是免費開源軟體。如果你已經付錢了那你可能被騙了。主頁https://github.com/Grasscutters/Grasscutter",
"starting": "正在啟動 Grasscutter...", "starting": "正在啟動 Grasscutter...",
"shutdown": "正在關閉...", "shutdown": "正在關閉...",
"done": "載完成!需要指令幫助請輸入 \"help\"", "done": "載完成!需要指令幫助請輸入 \"help\"",
"error": "發生了一個錯誤。", "error": "發生了一個錯誤。",
"welcome": "歡迎使用 Grasscutter", "welcome": "歡迎使用 Grasscutter",
"run_mode_error": "無效的伺服器運行模式:%s。", "run_mode_error": "無效的伺服器運行模式:%s。",
"run_mode_help": "伺服器運行模式必須為 HYBRID 或者 DISPATCH_ONLY 或者 GAME_ONLY。Grasscutter 啟動失敗...", "run_mode_help": "伺服器運行模式必須為 HYBRID 或者 DISPATCH_ONLY 或者 GAME_ONLY。Grasscutter 啟動失敗...",
"create_resources": "正在建立 resources 資料夾...", "create_resources": "正在建立 resources 資料夾...",
@ -62,11 +62,11 @@
"version": "Grasscutter 版本: %s-%s", "version": "Grasscutter 版本: %s-%s",
"game_version": "遊戲版本:%s", "game_version": "遊戲版本:%s",
"resources": { "resources": {
"loading": "載資源中...", "loading": "載資源中...",
"finish": "資源載完成.。", "finish": "資源載完成。",
"custom": "🇺🇸Find additional resources at: 'Anime-Game-Servers/CustomGCResources'.", "custom": " 'Anime-Game-Servers/CustomGCResources' 尋找額外資源。",
"missing_server": "🇺🇸To fully use questing, it is recommended to add the 'Server' folder.", "missing_server": "若要完整使用任務功能,建議加入 'Server' 資料夾。",
"missing_scenes": "🇺🇸To fully use questing, it is recommended to add the 'ScriptSceneData' folder." "missing_scenes": "若要完整使用任務功能,建議加入 'ScriptSceneData' 資料夾。"
} }
} }
}, },
@ -83,36 +83,36 @@
"set_to": "%s 已經設為 %s。", "set_to": "%s 已經設為 %s。",
"set_for_to": "%s 的使用者 %s 更改為 %s。", "set_for_to": "%s 的使用者 %s 更改為 %s。",
"invalid": { "invalid": {
"amount": "無效的數量。", "amount": "數量無效。",
"artifactId": "無效的聖遺物ID。", "artifactId": "聖遺物 ID 無效。",
"avatarId": "無效的角色ID。", "avatarId": "角色 ID 無效。",
"avatarLevel": "無效的角色等級。", "avatarLevel": "角色等級無效。",
"cfgId": "🇺🇸Invalid cfg ID.", "cfgId": "cfg ID 無效。",
"entityId": "無效的實體ID。", "entityId": "實體 ID 無效。",
"itemId": "無效的物品ID。", "itemId": "物品 ID 無效。",
"itemLevel": "無效的物品等級。", "itemLevel": "物品等級無效。",
"itemRefinement": "無效的物品精煉度。", "itemRefinement": "物品精煉度無效。",
"statValue": "無效的數據值。", "statValue": "數據值無效。",
"value_between": "無效的屬性值:%s 必須在 %s 到 %s 之間。", "value_between": "屬性值:%s 必須在 %s 到 %s 之間。",
"playerId": "無效的玩家ID。", "playerId": "玩家 ID 無效。",
"uid": "無效的UID。", "uid": "UID 無效。",
"id": "無效的ID。" "id": "ID 無效。"
} }
}, },
"execution": { "execution": {
"usage_prefix": "用法:", "usage_prefix": "用法:",
"player_exist_error": "用戶不存在。", "player_exist_error": "使用者不存在。",
"player_offline_error": "玩家已離線。", "player_offline_error": "玩家已離線。",
"item_player_exist_error": "無效的物品/玩家 UID。", "item_player_exist_error": "無效的物品/玩家 UID。",
"player_exist_offline_error": "玩家不存在或已離線。", "player_exist_offline_error": "玩家不存在或已離線。",
"argument_error": "無效的參數。", "argument_error": "無效的參數。",
"clear_target": "目標已清除.", "clear_target": "目標已清除.",
"set_target": "隨後的指令都會以 @%s 為預設。", "set_target": "隨後的指令都會以 @%s 為預設。",
"set_target_online": "@%s 在線。 某些命令可能需要離線目標。", "set_target_online": "@%s 在線。某些命令可能需要離線目標。",
"set_target_offline": "@%s 離線。 某些命令可能需要線目標。", "set_target_offline": "@%s 目前離線。某些命令可能需要線目標。",
"need_target": "此指令需要一個目標 UID。添加 <@UID> 引數或者使用 /target @UID 來設定持久目標。", "need_target": "此指令需要一個目標 UID。添加 <@UID> 引數或者使用 /target @UID 來設定持久目標。",
"need_target_online": "此命令需要線目標 UID前目標離線。 添加不同的 <@UID> 參數或使用 /target @UID 設置持久目標。", "need_target_online": "此命令需要線目標 UID前目標離線。添加不同的 <@UID> 參數或使用 /target @UID 設置持久目標。",
"need_target_offline": "此命令需要離線目標 UID前目標在線。 添加不同的 <@UID> 參數或使用 /target @UID 設置持久目標。" "need_target_offline": "此命令需要離線目標 UID前目標線上。添加不同的 <@UID> 參數或使用 /target @UID 設置持久目標。"
}, },
"status": { "status": {
"enabled": "已啟用", "enabled": "已啟用",
@ -130,25 +130,25 @@
}, },
"achievement": { "achievement": {
"success": { "success": {
"grant": "🇺🇸Granted the achievement to %s.", "grant": "已將成就授予 %s",
"revoke": "🇺🇸Revoked the achievement from %s.", "revoke": "已從 %s 撤回成就。",
"progress": "🇺🇸Set progress of %s's achievement(id: %s) to %s.", "progress": "將 %s 的成就 (id: %s) 進度設為 %s",
"grantall": "🇺🇸Granted %s achievement(s) to %s.", "grantall": "已授予 %s 項成就給 %s",
"revokeall": "🇺🇸Revoked %s achievement(s) from %s." "revokeall": "已撤回 %s 項成就:%s"
}, },
"fail": { "fail": {
"achievement_not_found": "🇺🇸Achievement not found.", "achievement_not_found": "找不到成就。",
"already_achieved": "🇺🇸%s has already achieved the achievement.", "already_achieved": "%s 已經達成成就了。",
"not_yet_achieved": "🇺🇸%s hasn't achieved the achievement yet." "not_yet_achieved": "%s 尚未達成成就。"
}, },
"description": "🇺🇸Grant, Revoke or Progress achievements." "description": "授予、撤回,或提升成就。"
}, },
"announce": { "announce": {
"send_success": "成功送了一則公告,你可以通過 /a revoke %s 撤銷。", "send_success": "成功送了一則公告,你可以通過 /a revoke %s 撤銷。",
"refresh_success": "已重新整理了 %s 個公告。", "refresh_success": "已重新整理了 %s 個公告。",
"revoke_done": "嘗試撤回公告 %s。", "revoke_done": "嘗試撤回公告 %s。",
"not_found": "找不到公告 %s。", "not_found": "找不到公告 %s。",
"description": "送公告給所有線玩家,或者是更改伺服器的公告。" "description": "送公告給所有線玩家,或者是更改伺服器的公告。"
}, },
"clear": { "clear": {
"weapons": "已將 %s 的武器清空。", "weapons": "已將 %s 的武器清空。",
@ -158,11 +158,11 @@
"displays": "已清除 %s 的顯示。", "displays": "已清除 %s 的顯示。",
"virtuals": "已將 %s 的所有貨幣和經驗值清空。", "virtuals": "已將 %s 的所有貨幣和經驗值清空。",
"everything": "已將 %s 的所有物品清空。", "everything": "已將 %s 的所有物品清空。",
"description": "從你的背包中刪除所有未裝備且未上鎖的物品,包括稀有物品。" "description": "從你的背包中刪除所有未裝備且未上鎖的物品,預設為 4 星且等級和精煉均為 1 以下的物品,但可以提高限制。"
}, },
"coop": { "coop": {
"success": "召喚了 %s 到 %s 的世界。", "success": "召喚了 %s 到 %s 的世界。",
"description": "強制傳送指定用戶到他人的世界。如果未指定玩家,則會將你設為多人遊戲狀態。" "description": "強制傳送指定使用者到他人的世界。如果未指定玩家,則會將你設為多人遊戲狀態。"
}, },
"enter_dungeon": { "enter_dungeon": {
"changed": "已進入祕境 %s", "changed": "已進入祕境 %s",
@ -171,11 +171,11 @@
"description": "進入指定祕境。" "description": "進入指定祕境。"
}, },
"entity": { "entity": {
"description": "🇺🇸Modify an existing entity's properties", "description": "編輯現有實體的屬性",
"not_found_error": "🇺🇸Entity does not exist" "not_found_error": "實體不存在"
}, },
"give": { "give": {
"usage_relic": "用法: give <artifactID> [mainPropID] [<appendPropID>[,<times>]]... [lv<level 0-20>]", "usage_relic": "用法: give <聖遺物 ID> [主詞條 ID] [<副詞條 ID>[,<次數>]]... [lv<等級 0-20>]",
"illegal_relic": "你不可以取得這個聖遺物,因為該聖遺物 ID 在黑名單列表內。", "illegal_relic": "你不可以取得這個聖遺物,因為該聖遺物 ID 在黑名單列表內。",
"given": "已經將 %s 個 %s 給予 %s。", "given": "已經將 %s 個 %s 給予 %s。",
"given_with_level_and_refinement": "已將 %s [等級 %s, 精煉 %s] %s 個給予 %s", "given_with_level_and_refinement": "已將 %s [等級 %s, 精煉 %s] %s 個給予 %s",
@ -186,16 +186,16 @@
}, },
"heal": { "heal": {
"success": "所有角色已被治療。", "success": "所有角色已被治療。",
"description": "治療前隊伍的角色。" "description": "治療前隊伍的角色。"
}, },
"help": { "help": {
"aliases": "別名:", "aliases": "別名:",
"available_commands": "可用指令:", "available_commands": "可用指令:",
"tip_need_permission": "需要的權限: ", "tip_need_permission": "需要的權限",
"tip_need_no_permission": "無", "tip_need_no_permission": "無",
"tip_permission_targeted": "(對其他的玩家使用這個指令還需要權限 %s)", "tip_permission_targeted": "(對其他的玩家使用這個指令還需要權限 %s)",
"warn_player_has_no_permission": "注意:你沒有執行這條指令的權限", "warn_player_has_no_permission": "注意:你沒有執行這條指令的權限",
"description": "送幫助息或顯示特定命令的信息" "description": "送幫助息或顯示特定命令的資訊"
}, },
"kick": { "kick": {
"player_kick_player": "玩家 [%s:%s] 已把 [%s:%s] 踢出", "player_kick_player": "玩家 [%s:%s] 已把 [%s:%s] 踢出",
@ -212,14 +212,14 @@
"description": "殺死玩家目前使用的場上角色。" "description": "殺死玩家目前使用的場上角色。"
}, },
"language": { "language": {
"current_language": "前語言是: %s", "current_language": "前語言是:%s",
"language_changed": "語言切換至:%s", "language_changed": "語言切換至:%s",
"language_not_found": "目前客戶端沒有這種語言:%s", "language_not_found": "目前客戶端沒有這種語言:%s",
"description": "顯示或切換前語言。" "description": "顯示或切換前語言。"
}, },
"list": { "list": {
"success": "目前總線上人數:%s", "success": "目前總線上人數:%s",
"description": "查看所有線玩家" "description": "查看所有線玩家"
}, },
"permission": { "permission": {
"add": "已指派權限。", "add": "已指派權限。",
@ -231,7 +231,7 @@
}, },
"position": { "position": {
"success": "坐標:%s, %s, %s\n旋轉%s, %s, %s\n場景ID%s", "success": "坐標:%s, %s, %s\n旋轉%s, %s, %s\n場景ID%s",
"description": "獲取所在坐標和旋轉信息" "description": "獲取所在坐標和旋轉資訊"
}, },
"quest": { "quest": {
"added": "已添加任務 %s", "added": "已添加任務 %s",
@ -239,117 +239,117 @@
"not_found": "未找到任務", "not_found": "未找到任務",
"invalid_id": "無效的任務ID", "invalid_id": "無效的任務ID",
"description": "添加或完成任務", "description": "添加或完成任務",
"running": "🇺🇸Quest %s is %s (%s).", "running": "任務 %s 的狀態為 %s (%s)",
"talking": "🇺🇸Talk %s is %s for main quest %s (%s).", "talking": "對話 %s 的狀態為 %s主任務為 %s (%s)",
"state": { "state": {
"none": "🇺🇸unknown (none)", "none": "未知 (無)",
"unstarted": "🇺🇸unfinished (not started, not completed)", "unstarted": "未完成 (未開始,未結束)",
"unfinished": "🇺🇸unfinished (started, not completed)", "unfinished": "未完成 (已開始,未結束)",
"finished": "🇺🇸finished (completed)", "finished": "已完成 (已結束)",
"failed": "🇺🇸finished (completed, but failed)", "failed": "已完成 (已結束,但失敗)",
"exists": "🇺🇸found", "exists": "存在",
"not_exists": "🇺🇸not found" "not_exists": "找不到"
}, },
"enabled": "🇺🇸Questing enabled." "enabled": "已啟用任務。"
}, },
"group": { "group": {
"invalid_groupid": "🇺🇸Invalid group ID.", "invalid_groupid": "群組 ID 無效。",
"invalid_suiteid": "🇺🇸Invalid suite ID.", "invalid_suiteid": "suite ID 無效。",
"group_not_found": "🇺🇸Group not found.", "group_not_found": "找不到群組。",
"description": "🇺🇸Alter group loading", "description": "更改群組載入",
"refreshed": "🇺🇸Group %s refreshed." "refreshed": "已重新載入群組 %s。"
}, },
"cutscene": { "cutscene": {
"description": "🇺🇸Plays a cutscene" "description": "播放過場動畫"
}, },
"sound": { "sound": {
"description": "🇺🇸Plays a sound" "description": "播放音效"
}, },
"reload": { "reload": {
"reload_start": "正在重新載設定檔。", "reload_start": "正在重新載設定檔。",
"reload_done": "重新載已完成。", "reload_done": "重新載已完成。",
"description": "重新載設定檔和數據。" "description": "重新載設定檔和數據。"
}, },
"resetConst": { "resetConst": {
"reset_all": "重設所有角色的命座。", "reset_all": "重設所有角色的命座。",
"success": "已重設 %s 的命座,重新登入後將會生效。", "success": "已重設 %s 的命座,重新登入後將會生效。",
"description": "重置前角色的命之座,重新登入後將會生效。" "description": "重置前角色的命之座,重新登入後將會生效。"
}, },
"resetShopLimit": { "resetShopLimit": {
"success": "重置完成。", "success": "重置完成。",
"description": "重置所選玩家的商店刷新時間。" "description": "重置所選玩家的商店刷新時間。"
}, },
"sendMail": { "sendMail": {
"give_usage": "用法: give <player> <itemID|itemName> [amount] [level] [refinement]", "give_usage": "用法: give <玩家> <物品 ID|物品名稱> [數量] [等級] [精煉]",
"user_not_exist": "ID '%s' 的使用者不存在。", "user_not_exist": "ID '%s' 的使用者不存在。",
"start_composition": "送郵件流程。\n請使用`/send <郵件標題>`來進到下一步。\n你可以在任何時間使用`/sendmail stop`來停止送。", "start_composition": "送郵件流程。\n請使用`/send <郵件標題>`來進到下一步。\n你可以在任何時間使用`/sendmail stop`來停止送。",
"templates": "郵件模板尚未實裝...", "templates": "郵件模板尚未實裝...",
"invalid_arguments": "無效的參數。\n指令使用方法 `/sendmail <userId|all|help> [templateId]`", "invalid_arguments": "無效的參數。\n指令使用方法 `/sendmail <userId|all|help> [templateId]`",
"send_cancel": "取消傳送信息", "send_cancel": "取消傳送郵件",
"send_done": "已將消息發送給 %s", "send_done": "已將郵件傳送給 %s",
"send_all_done": "消息已發送給全體用戶", "send_all_done": "郵件已傳送給全體使用者",
"not_composition_end": "現在郵件送未到最後階段。\n請使用 `/sendmail %s` 繼續送郵件,或者 `/sendmail stop` 來停止送郵件。", "not_composition_end": "現在郵件送未到最後階段。\n請使用 `/sendmail %s` 繼續送郵件,或者 `/sendmail stop` 來停止送郵件。",
"please_use": "請使用 `/sendmail %s`", "please_use": "請使用 `/sendmail %s`",
"set_title": "成功將郵件標題設定成 '%s'。\n接下來請繼續使用 '/sendmail <content>' 來設定郵件內容。", "set_title": "成功將郵件標題設定成 '%s'。\n接下來請繼續使用 '/sendmail <content>' 來設定郵件內容。",
"set_contents": "成功將 '%s' 為郵件內容。\n接下來請打出 '/sendmail <寄件者名稱>' 來設定郵件寄件者名稱。", "set_contents": "成功將 '%s' 為郵件內容。\n接下來請打出 '/sendmail <寄件者名稱>' 來設定郵件寄件者名稱。",
"set_message_sender": "郵件寄件者已設為 '%s'。\n使用 '/sendmail <itemId|itemName|finish> [amount] [level]' 以繼續操作。", "set_message_sender": "郵件寄件者已設為 '%s'。\n使用 '/sendmail <itemId|itemName|finish> [數量] [等級]' 以繼續操作。",
"send": "已添加 %s 個 %s (等級為 %s) 到郵件附件。\n如果沒有要繼續添加道具請使用 `/sendmail finish` 來完成郵件送。", "send": "已添加 %s 個 %s (等級為 %s) 到郵件附件。\n如果沒有要繼續添加道具請使用 `/sendmail finish` 來完成郵件送。",
"invalid_arguments_please_use": "無效的參數 \n 請改用 `/sendmail %s`", "invalid_arguments_please_use": "無效的參數 \n 請改用 `/sendmail %s`",
"title": "<標題>", "title": "<標題>",
"message": "<正文>", "message": "<正文>",
"sender": "<寄件者>", "sender": "<寄件者>",
"arguments": "<itemId|itemName|finish> [數量] [等級]", "arguments": "<itemId|itemName|finish> [數量] [等級]",
"error": "錯誤:無效的編寫階段 %s。需要 stacktrace 請查看伺服器命令提示字元。", "error": "錯誤:無效的編寫階段 %s。需要 stacktrace 請查看伺服器命令提示字元。",
"description": "向指定用戶發送郵件。此指令的用法可根據附加的參數而改變。" "description": "向指定使用者傳送郵件。此指令的用法可根據附加的參數而改變。"
}, },
"sendMessage": { "sendMessage": {
"success": "訊息已送。", "success": "訊息已送。",
"description": "向指定玩家送訊息。" "description": "向指定玩家送訊息。"
}, },
"setConst": { "setConst": {
"range_error": "命座必須在0到6之間。", "range_error": "命座必須在 0 到 6 之間。",
"level_error": "無效的命座等級。", "level_error": "無效的命座等級。",
"fail": "設定命座失敗。", "fail": "設定命座失敗。",
"failed_success": "%s的命之座已設定為成%s重新登入後將會生效。", "failed_success": "%s 的命之座已設%s重新登入後將會生效。",
"success": "%s的命之座已設定為成%s。", "success": "%s 的命之座已設%s。",
"successall": "🇺🇸Constellations for all characters have been set to %s.", "successall": "所有角色的命之座已設為 %s",
"description": "設定前角色的命之座" "description": "設定前角色的命之座"
}, },
"setFetterLevel": { "setFetterLevel": {
"range_error": "好感度必須在 0 到 10 之間。", "range_error": "好感度必須在 0 到 10 之間。",
"success": "好感等級已設定為 %s", "success": "好感等級已設定為 %s",
"level_error": "無效的好感度。", "level_error": "無效的好感度。",
"description": "設定前角色的好感度等級。" "description": "設定前角色的好感度等級。"
}, },
"setProp": { "setProp": {
"description": "設定帳號屬性。比如可以通過此命令啟用無敵,也可以解鎖深淵或更改紀行等級。\n\t可更改的屬性列表 godmode(無敵)|nostamina(無限體力)|unlimitedenergy(無限元素能量)|abyss(深淵螺旋)|worldlevel(世界等級)|bplevel(紀行等級)\n\t(cont.) `有關其他可能的數值,請參閱 PlayerProperty 列舉。 (範例 PROP_MAX_SPRING_VOLUME -> max_spring_volume)`" "description": "設定帳號屬性。比如可以通過此命令啟用無敵,也可以解鎖深淵或更改紀行等級。\n\t可更改的屬性列表 godmode(無敵)|nostamina(無限體力)|unlimitedenergy(無限元素能量)|abyss(深淵螺旋)|worldlevel(世界等級)|bplevel(紀行等級)\n\t(cont.) `有關其他可能的數值,請參閱 PlayerProperty 列舉。 (範例 PROP_MAX_SPRING_VOLUME -> max_spring_volume)`"
}, },
"setStats": { "setStats": {
"description": "設定前角色的數據類型。\n\t可使用的數據類型hp (生命值)| maxhp (最大生命值) | def(防禦力) | atk (攻擊力)| em (元素精通) | er (元素充能效率) | crate(暴擊率) | cdmg (暴擊傷害)| cdr (冷卻縮減) | heal(治療加成)| heali (受治療加成)| shield (護盾強效)| defi (無視防禦)\n\t(cont.) 元素增傷類epyro (火傷) | ecryo (冰傷) | ehydro (水傷) | egeo (岩傷) | edendro (草傷) | eelectro (雷傷) | ephys (物傷)(cont.) 元素減傷類respyro (火抗) | rescryo (冰抗) | reshydro (水抗) | resgeo (岩抗) | resdendro (草抗) | reselectro (雷抗) | resphys (物抗)", "description": "設定前角色的數據類型。\n\t可使用的數據類型hp (生命值)| maxhp (最大生命值) | def(防禦力) | atk (攻擊力)| em (元素精通) | er (元素充能效率) | crate(暴擊率) | cdmg (暴擊傷害)| cdr (冷卻縮減) | heal(治療加成)| heali (受治療加成)| shield (護盾強效)| defi (無視防禦)\n\t(cont.) 元素增傷類epyro (火傷) | ecryo (冰傷) | ehydro (水傷) | egeo (岩傷) | edendro (草傷) | eelectro (雷傷) | ephys (物傷)(cont.) 元素減傷類respyro (火抗) | rescryo (冰抗) | reshydro (水抗) | resgeo (岩抗) | resdendro (草抗) | reselectro (雷抗) | resphys (物抗)",
"locked_to": "🇺🇸%s locked to %s.", "locked_to": "%s 已鎖定為 %s.",
"locked_for_to": "🇺🇸%s for %s locked to %s.", "locked_for_to": "%s (玩家 %s) 已鎖定為 %s",
"unlocked": "🇺🇸%s unlocked.", "unlocked": "%s 已解鎖。",
"unlocked_for": "🇺🇸%s for %s unlocked." "unlocked_for": "%s (玩家 %s) 已解鎖。"
}, },
"spawn": { "spawn": {
"success": "已生成 %s 個 %s。", "success": "已生成 %s 個 %s。",
"limit_reached": "已達到場景生成上限,已改為 %s 個實體。", "limit_reached": "已達到場景生成上限,已改為 %s 個實體。",
"description": "在你附近生成一個實體動物。" "description": "在你附近生成一個實體。"
}, },
"stop": { "stop": {
"success": "正在關閉伺服器...", "success": "正在關閉伺服器...",
"description": "以正常的方式關閉伺服器" "description": "關閉伺服器"
}, },
"talent": { "talent": {
"out_of_range": "🇺🇸Invalid talent level. Level should be in range of 1-15.", "out_of_range": "天賦等級無效。等級應在 1-15 之間。",
"set_id": "🇺🇸Set talent %s - \"%s\" to %s.", "set_id": "設置天賦 %s - \"%s\" %s",
"id_desc": "🇺🇸Talent %s - \"%s\" - \"%s\"", "id_desc": "天賦 %s - \"%s\" - \"%s\"",
"invalid_skill_id": "無效的技能 ID。", "invalid_skill_id": "無效的技能 ID。",
"invalid_level": "無效的天賦等級。", "invalid_level": "無效的天賦等級。",
"normal_attack_id": "普通攻擊的 ID 為 %s。", "normal_attack_id": "普通攻擊的 ID 為 %s。",
"e_skill_id": "元素戰技技能ID %s。", "e_skill_id": "元素戰技技能 ID %s。",
"q_skill_id": "元素爆發技能ID %s。", "q_skill_id": "元素爆發技能 ID %s。",
"description": "設定前角色的天賦等級" "description": "設定前角色的天賦等級"
}, },
"team": { "team": {
"invalid_usage": "無效的用法。", "invalid_usage": "無效的用法。",
@ -377,19 +377,19 @@
"description": "將玩家的位置傳送到你所指定的座標。" "description": "將玩家的位置傳送到你所指定的座標。"
}, },
"trialAvatarActivity": { "trialAvatarActivity": {
"description": "操縱試用角色活動的功能。這包括切換地牢和獎勵的狀態。", "description": "操縱試用角色活動的功能。這包括切換秘境和獎勵的狀態。",
"not_found": "未找到試用角色活動玩家數據。也許該活動未被啟用。", "not_found": "未找到試用角色活動玩家數據。也許該活動未被啟用。",
"invalid_param": "無效的參數。", "invalid_param": "無效的參數。",
"schedule_not_found": "未找到計劃 %s。", "schedule_not_found": "未找到計劃 %s。",
"success_schedule": "已將計劃 ID 更改為 %s。", "success_schedule": "已將計劃 ID 更改為 %s。",
"success_dungeon": "已切換角色 %s 的地牢狀態。", "success_dungeon": "已切換角色 %s 的秘境狀態。",
"success_dungeon_all": "已切換所有角色的地牢狀態。", "success_dungeon_all": "已切換所有角色的秘境狀態。",
"success_reward": "已切換角色 %s 的獎勵狀態。", "success_reward": "已切換角色 %s 的獎勵狀態。",
"success_reward_all": "已切換所有角色的獎勵狀態。" "success_reward_all": "已切換所有角色的獎勵狀態。"
}, },
"weather": { "weather": {
"success": "已設定天氣 ID 為 %s氣候型別為 %s。", "success": "已設定天氣 ID 為 %s氣候型別為 %s。",
"status": "前天氣ID 為 %s氣候型別為 %s。", "status": "前天氣 ID 為 %s氣候型別為 %s。",
"description": "更改天氣 ID 和氣候型別。天氣 ID 可以在 ./Resources/ExcelBinOutput/WeatherExcelConfigData.json 中找到。\n氣候型別sunny(晴天), cloudy(多雲), rain(雨), thunderstorm(雷雨), snow(雪), mist(霧)" "description": "更改天氣 ID 和氣候型別。天氣 ID 可以在 ./Resources/ExcelBinOutput/WeatherExcelConfigData.json 中找到。\n氣候型別sunny(晴天), cloudy(多雲), rain(雨), thunderstorm(雷雨), snow(雪), mist(霧)"
}, },
"ban": { "ban": {
@ -399,8 +399,8 @@
"description": "停權指定玩家。" "description": "停權指定玩家。"
}, },
"unlockall": { "unlockall": {
"success": "🇺🇸Unlocked all open states for %s.", "success": "已為 %s 解鎖所有開放狀態。",
"description": "🇺🇸Unlocks all open states for a player." "description": "為指定玩家解鎖所有開放狀態。"
}, },
"unban": { "unban": {
"success": "撤銷停權成功。", "success": "撤銷停權成功。",
@ -408,7 +408,7 @@
"description": "撤銷停權指定玩家。" "description": "撤銷停權指定玩家。"
}, },
"troubleshoot": { "troubleshoot": {
"description": "🇺🇸Generate debugging information for troubleshooting." "description": "產生疑難排解用的除錯資訊。"
} }
}, },
"gacha": { "gacha": {
@ -426,7 +426,7 @@
}, },
"documentation": { "documentation": {
"handbook": { "handbook": {
"title": "🇺🇸GM Handbook", "title": "GM 手冊",
"title_commands": "指令", "title_commands": "指令",
"title_avatars": "角色", "title_avatars": "角色",
"title_items": "道具", "title_items": "道具",
@ -442,29 +442,29 @@
}, },
"index": { "index": {
"title": "文件", "title": "文件",
"handbook": "🇺🇸GM Handbook", "handbook": "GM 手冊",
"gacha_mapping": "祈願物品映射到 JSON 上" "gacha_mapping": "祈願物品映射到 JSON 上"
} }
}, },
"plugin": { "plugin": {
"directory_failed": "🇺🇸Failed to create plugins directory: ", "directory_failed": "無法建立外掛程式路徑:",
"unable_to_load": "🇺🇸Unable to load plugin.", "unable_to_load": "無法載入外掛程式。",
"invalid_config": "🇺🇸Plugin %s has an invalid config file.", "invalid_config": "外掛程式 %s 的設定檔無效。",
"invalid_main_class": "🇺🇸Plugin %s has an invalid main class.", "invalid_main_class": "外掛程式 %s 的主類別無效。",
"missing_config": "🇺🇸Plugin %s lacks a valid config file.", "missing_config": "外掛程式 %s 缺少有效的設定檔。",
"failed_to_load_plugin": "🇺🇸Failed to load plugin: %s", "failed_to_load_plugin": "無法載入外掛程式:%s",
"failed_to_load": "🇺🇸Failed to load a plugin.", "failed_to_load": "無法載入外掛程式。",
"failed_to_load_dependencies": "🇺🇸Failed to load plugins with dependencies.", "failed_to_load_dependencies": "無法載入外掛程式的相依項目。",
"loading_plugin": "🇺🇸Loading plugin: %s", "loading_plugin": "正在載入外掛程式:%s",
"failed_add_id": "🇺🇸Failed to add plugin identifier: %s", "failed_add_id": "無法新增外掛程式識別碼:%s",
"enabling_plugin": "🇺🇸Enabling plugin: %s", "enabling_plugin": "正在啟用外掛程式:%s",
"enabling_failed": "🇺🇸Failed to enable plugin: %s", "enabling_failed": "無法啟用外掛程式:%s",
"disabling_plugin": "🇺🇸Disabling plugin: %s", "disabling_plugin": "正在停用外掛程式:%s",
"disabling_failed": "🇺🇸Failed to disable plugin: %s", "disabling_failed": "無法停用外掛程式:%s",
"invalid_api": { "invalid_api": {
"not_present": "🇺🇸Plugin %s does not specify an API version.", "not_present": "外掛程式 %s 並未指定 API 版本。",
"lower": "🇺🇸Plugin %s is using API version %s, while the server is using API version %s.", "lower": "外掛程式 %s 使用的 API 版本為 %s而伺服器使用的 API 版本為 %s",
"outdated": "🇺🇸Plugin %s is using an outdated API method." "outdated": "外掛程式 %s 使用了舊版的 API 方法。"
} }
} }
} }