9 Commits

452 changed files with 5788 additions and 6168 deletions

View File

@ -1,11 +0,0 @@
# To get started with Dependabot version updates, you'll need to specify which
# package ecosystems to update and where the package manifests are located.
# Please see the documentation for all configuration options:
# https://docs.github.com/code-security/dependabot/dependabot-version-updates/configuration-options-for-the-dependabot.yml-file
version: 2
updates:
- package-ecosystem: "gradle" # See documentation for possible values
directory: "/" # Location of package manifests
schedule:
interval: "weekly"

View File

@ -20,9 +20,9 @@ jobs:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- name: Checkout - name: Checkout
uses: actions/checkout@v4 uses: actions/checkout@v3
- name: Setup Java - name: Setup Java
uses: actions/setup-java@v4 uses: actions/setup-java@v3
with: with:
distribution: temurin distribution: temurin
java-version: '17' java-version: '17'
@ -44,7 +44,7 @@ jobs:
- name: Run Gradle - name: Run Gradle
run: ./gradlew -PskipHandbook=1 && ./gradlew jar -PskipHandbook=1 run: ./gradlew -PskipHandbook=1 && ./gradlew jar -PskipHandbook=1
- name: Upload build - name: Upload build
uses: actions/upload-artifact@v4 uses: actions/upload-artifact@v3
with: with:
name: Grasscutter name: Grasscutter
path: grasscutter-*.jar path: grasscutter-*.jar

View File

@ -1,51 +0,0 @@
name: Build Docker Container
on:
push:
release:
types: [published]
workflow_dispatch: ~
jobs:
publish:
runs-on: ubuntu-latest
permissions:
contents: read
packages: write
steps:
- name: Checkout Project
uses: actions/checkout@v4
- name: Generate Docker Meta
uses: docker/metadata-action@v5
id: meta
with:
images: ghcr.io/${{ github.repository }}
tags: |
type=ref,event=branch
type=semver,pattern={{version}}
type=semver,pattern={{major}}.{{minor}}
type=semver,pattern={{major}}
type=sha
- name: Set up QEMU
uses: docker/setup-qemu-action@v3
- name: Setup Docker Buildx
uses: docker/setup-buildx-action@v3.1.0
- name: Login to GitHub Container Registry
uses: docker/login-action@v3.0.0
with:
registry: ghcr.io
username: ${{ github.repository_owner }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Build and Push Docker image
uses: docker/build-push-action@v5.2.0
with:
context: .
push: true
platforms: linux/amd64
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}

1
.gitignore vendored
View File

@ -64,7 +64,6 @@ tmp/
/*.jar /*.jar
/*.sh /*.sh
!entrypoint.sh
GM Handbook*.txt GM Handbook*.txt
handbook.html handbook.html

View File

@ -1,38 +0,0 @@
# Builder
FROM gradle:jdk17-alpine as builder
RUN apk add --update nodejs npm
WORKDIR /app
COPY ./ /app/
RUN gradle jar --no-daemon
# Fetch Data
FROM bitnami/git:2.43.0-debian-11-r1 as data
ARG DATA_REPOSITORY=https://gitlab.com/YuukiPS/GC-Resources.git
ARG DATA_BRANCH=4.0
WORKDIR /app
RUN git clone --branch ${DATA_BRANCH} --depth 1 ${DATA_REPOSITORY}
# Result Container
FROM amazoncorretto:17-alpine
WORKDIR /app
# Copy built assets
COPY --from=builder /app/grasscutter-*.jar /app/grasscutter.jar
COPY --from=builder /app/keystore.p12 /app/keystore.p12
# Copy the resources
COPY --from=data /app/GC-Resources/Resources /app/resources/
# Copy startup files
COPY ./entrypoint.sh /app/
CMD [ "sh", "/app/entrypoint.sh" ]
EXPOSE 80 443 8888 22102

View File

@ -24,14 +24,12 @@
### Quick Start (automatic) ### Quick Start (automatic)
- Get [Java 17](https://www.oracle.com/java/technologies/javase/jdk17-archive-downloads.html) - Get Java 17: https://www.oracle.com/java/technologies/javase/jdk17-archive-downloads.html
- Get [MongoDB Community Server](https://www.mongodb.com/try/download/community) - Get [MongoDB Community Server](https://www.mongodb.com/try/download/community)
- Get game version REL4.0.x (If you don't have a 4.0.x client, you can find it here and open any of the links to download it): - Get game version REL4.0.x (4.0.x client can be found here if you don't have it): https://github.com/MAnggiarMustofa/GI-Download-Library/blob/main/GenshinImpact/Client/4.0.0.md
[4.0.x Client-github](https://github.com/JRSKelvin/GenshinRepository/blob/main/Version%204.0.0.md)
[4.0.x Client-cloud drive](https://www.123pan.com/s/HoqUVv-U7SBA.html)
- Download the [latest Cultivation version](https://github.com/Grasscutters/Cultivation/releases/latest). Use the `.msi` installer. - Download the [latest Cultivation version](https://github.com/Grasscutters/Cultivation/releases/latest). Use the `.msi` installer.
- After opening Cultivation (as admin), press the download button in the upper right corner. - After opening Culivation (as admin), press the download button in the upper right corner.
- Click `Download All-in-One` - Click `Download All-in-One`
- Click the gear in the upper right corner - Click the gear in the upper right corner
- Set the game Install path to where your game is located. - Set the game Install path to where your game is located.
@ -40,7 +38,7 @@
- Click the small button next to launch. - Click the small button next to launch.
- Click the launch button. - Click the launch button.
- Log in with whatever username you want. Password can be anything. - Log in with whatever username you want. Password doesn't matter.
### Building ### Building

View File

@ -58,7 +58,7 @@ sourceCompatibility = JavaVersion.VERSION_17
targetCompatibility = JavaVersion.VERSION_17 targetCompatibility = JavaVersion.VERSION_17
group = 'io.grasscutter' group = 'io.grasscutter'
version = '1.7.4' version = '1.7.1'
java { java {
withJavadocJar() withJavadocJar()
@ -82,7 +82,7 @@ dependencies {
// Line reading libraries. // Line reading libraries.
implementation group: 'org.jline', name: 'jline', version: '3.21.0' implementation group: 'org.jline', name: 'jline', version: '3.21.0'
implementation group: 'org.jline', name: 'jline-terminal-jna', version: '3.26.1' implementation group: 'org.jline', name: 'jline-terminal-jna', version: '3.21.0'
implementation group: 'net.java.dev.jna', name: 'jna', version: '5.10.0' implementation group: 'net.java.dev.jna', name: 'jna', version: '5.10.0'
// Java Netty for networking. // Java Netty for networking.
@ -96,7 +96,7 @@ dependencies {
implementation group: 'com.google.protobuf', name: 'protobuf-java', version: '3.19.6' implementation group: 'com.google.protobuf', name: 'protobuf-java', version: '3.19.6'
implementation group: 'com.fasterxml.jackson.core', name: 'jackson-databind', version: '2.14.2' implementation group: 'com.fasterxml.jackson.core', name: 'jackson-databind', version: '2.14.2'
implementation platform('com.fasterxml.jackson:jackson-bom:2.17.1') implementation platform('com.fasterxml.jackson:jackson-bom:2.14.0')
implementation group: 'com.fasterxml.jackson.core', name: 'jackson-databind', version: '2.14.2' implementation group: 'com.fasterxml.jackson.core', name: 'jackson-databind', version: '2.14.2'
// Reflections libraries. // Reflections libraries.
@ -130,7 +130,7 @@ dependencies {
// Testing libraries. // Testing libraries.
testImplementation group: 'org.junit.jupiter', name: 'junit-jupiter-api', version: '5.8.2' testImplementation group: 'org.junit.jupiter', name: 'junit-jupiter-api', version: '5.8.2'
testImplementation group: 'org.junit.jupiter', name: 'junit-jupiter-params', version: '5.8.2' testImplementation group: 'org.junit.jupiter', name: 'junit-jupiter-params', version: '5.8.2'
testImplementation group: 'org.junit.jupiter', name: 'junit-jupiter-engine', version: '5.10.2' testImplementation group: 'org.junit.jupiter', name: 'junit-jupiter-engine', version: '5.8.2'
// HTTP client library for testing. // HTTP client library for testing.
testImplementation group: 'com.squareup.okhttp3', name: 'okhttp', version: '4.10.0' testImplementation group: 'com.squareup.okhttp3', name: 'okhttp', version: '4.10.0'
@ -261,7 +261,7 @@ clean {
protobuf { protobuf {
protoc { protoc {
// The artifact spec for the Protobuf Compiler // The artifact spec for the Protobuf Compiler
artifact = 'com.google.protobuf:protoc:4.27.1' artifact = 'com.google.protobuf:protoc:3.18.1'
} }
generatedFilesBaseDir = "$projectDir/src/generated/" generatedFilesBaseDir = "$projectDir/src/generated/"

View File

@ -3,7 +3,7 @@
<div align="center"><a href="https://discord.gg/T5vZU6UyeG"><img alt="Discord - Grasscutter" src="https://img.shields.io/discord/965284035985305680?label=Discord&logo=discord&style=for-the-badge"></a></div> <div align="center"><a href="https://discord.gg/T5vZU6UyeG"><img alt="Discord - Grasscutter" src="https://img.shields.io/discord/965284035985305680?label=Discord&logo=discord&style=for-the-badge"></a></div>
[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) | [हिंदी](README_hn-IN.md) [EN](README.md) | [简中](docs/README_zh-CN.md) | [繁中](docs/README_zh-TW.md) | [FR](docs/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) | [हिंदी](README_hn-IN.md)
**ध्यान:** हम हमेशा परियोजना में योगदानकर्ताओं का स्वागत करते हैं।. अपना योगदान जोड़ने से पहले कृपया हमारा ध्यानपूर्वक पढ़ें [आचार संहिता](https://github.com/Grasscutters/Grasscutter/blob/stable/CONTRIBUTING.md). **ध्यान:** हम हमेशा परियोजना में योगदानकर्ताओं का स्वागत करते हैं।. अपना योगदान जोड़ने से पहले कृपया हमारा ध्यानपूर्वक पढ़ें [आचार संहिता](https://github.com/Grasscutters/Grasscutter/blob/stable/CONTRIBUTING.md).

View File

@ -3,64 +3,81 @@
<div align="center"><a href="https://discord.gg/T5vZU6UyeG"><img alt="Discord - Grasscutter" src="https://img.shields.io/discord/965284035985305680?label=Discord&logo=discord&style=for-the-badge"></a></div> <div align="center"><a href="https://discord.gg/T5vZU6UyeG"><img alt="Discord - Grasscutter" src="https://img.shields.io/discord/965284035985305680?label=Discord&logo=discord&style=for-the-badge"></a></div>
[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) | [HI](README_hn-IN.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) | [हिंदी](README_hn-IN.md)
**Attention:** 私たちはプロジェクトへのコントリビュータをいつでも歓迎します。コントリビュートする前に、私たちの [行動規範](https://github.com/Grasscutters/Grasscutter/blob/stable/CONTRIBUTING.md)をよくお読みください。 **:** 私たちはプロジェクトへの貢献者をいつでも歓迎します。貢献を追加する前に、我々の [行動規範](https://github.com/Grasscutters/Grasscutter/blob/stable/CONTRIBUTING.md)をよくお読みください。
## 現在実装されている機能 ## 現在機能している
* ログイン * ログイン
* 戦闘 * 戦闘
* フレンドリスト * フレンドリスト
* テレポート * テレポート
* 祈願(ガチャ) * 祈願(ガチャ)
* マルチプレイ (一部) * マルチプレイは一部機能しています
* コンソールを通したモンスタースポーン * コンソールを使用してモンスタースポーンさせる
* インベントリ機能 (アイテム/キャラクターの受け取り、アイテム/キャラクターのアップグレードなど) * インベントリ機能 (アイテム/キャラクターの受け取り、アイテム/キャラクターのアップグレードなど)
## かんたんセットアップガイド ## クイックセットアップガイド
**Note:** サポートが必要な場合はGrasscutterの[Discordサーバー](https://discord.gg/T5vZU6UyeG)に参加してください。 **:** サポートが必要な場合はGrasscutterの[Discord](https://discord.gg/T5vZU6UyeG)に参加してください。
### パパっとスタートアップ ### 動作環境
- [Java (バージョン17以降)](https://www.oracle.com/java/technologies/javase/jdk17-archive-downloads.html) を用意する * [JAVAのバージョン17以降](https://www.oracle.com/java/technologies/javase/jdk17-archive-downloads.html)
- [MongoDB Community Server](https://www.mongodb.com/try/download/community) を用意する
- ゲームバージョンがREL4.0.Xのクライアントを用意する (4.0.Xのクライアントを持っていない場合は右のリンクからダウンロード): [Github](https://github.com/JRSKelvin/GenshinRepository/blob/main/Version%204.0.0.md), [クラウド(123云盘)](https://www.123pan.com/s/HoqUVv-U7SBA.html)
- [最新の Cultivation](https://github.com/Grasscutters/Cultivation/releases/latest)をダウンロードする。`.msi`インストーラを使ってください。
- 管理者権限を付与して Cultivation を実行した後、右上端にあるダウンロードアイコンのボタンを押す。
- `Download All-in-One` をクリックする
- 右上端にある歯車アイコンのボタンをクリックする。
- `Game Install Path` にゲームファイルのパスを指定する。
- `Custom Java Path` に、自分が用意したJavaのパスを指定する。 (例: `C:\Program Files\Java\jdk-17\bin\java.exe`)
- その他の設定には手を付けず次の段階に進む。
- Launch の隣にある小さいボタンを押す。
- Launchボタンを押す
- 好きなユーザ名でログインする。ログインに関する設定がデフォルトの場合、パスワードは何を入れてもいい。
**:** サーバーを動作させるだけならjreのみで十分です。 開発をしたい場合JDKが必要になるかもしれません。
* [MongoDB](https://www.mongodb.com/try/download/community) (バージョン4.0以降を推奨)
* プロキシツール: [mitmproxy](https://mitmproxy.org/) (mitmdump, 推奨)、[Fiddler Classic](https://telerik-fiddler.s3.amazonaws.com/fiddler/FiddlerSetup.exe)、その他。
### 起動方法
**:** もしサーバーをアップデートしたい場合は`config.json`を削除してから再生成してください。
1. `grasscutter.jar`を入手する
- [releases](https://github.com/Grasscutters/Grasscutter/releases/latest) か [action](https://github.com/Grasscutters/Grasscutter/actions) からダウンロードするか、[自分でビルド](#ビルド)してください。
2. `grasscutter.jar` があるディレクトリに `resources` フォルダーを作成し、そこに `BinOutput, ExcelBinOutput, Readables, Scripts, Subtitle, TextMap` を移動してください *(`resources` フォルダの中身の入手方法については [wiki](https://github.com/Grasscutters/Grasscutter/wiki) を参照してください.)*
3. コマンドプロンプトに`java -jar grasscutter.jar`を入力しGrasscutterを起動してください。**このときMongoDBも実行する必要があります。**
### クライアントとの接続
½. [このコマンド](https://github.com/Grasscutters/Grasscutter/wiki/Commands#commands-for-server-admins)をサーバーコンソールから使用してアカウントを作成してください。
1. 通信内容をリダイレクトする: (どちらか一つを選択してください)
- mitmdump: `mitmdump -s proxy.py -k`
- CA証明書を信頼する:
- **:** CA証明書は`%USERPROFILE%\.mitmproxy`に保存されています。ダブルクリックして[インストール](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を起動し(Tools -> Options -> HTTPS)から`Decrypt https traffic`をオンにしてください。 (Tools -> Options -> Connections) に有るポート番号の設定を`8888`以外に設定してください。その後この[スクリプト](https://github.com/Grasscutters/Grasscutter/wiki/Resources#fiddler-classic-jscript)をFiddlerScriptタブにコピペしてロードします。
- [ホストファイル](https://github.com/Grasscutters/Grasscutter/wiki/Resources#hosts-file)
2. ネットワークプロキシを `127.0.0.1:(自分で設定したポート番号)` に設定してください。
- mitmproxyを使用した場合プロキシの設定と証明書のインストールが終わった後、http://mitm.it/ でトラフィックがmitmproxyを通過しているか確認しましょう。
**`start.cmd`でmitmdumpとサーバーをまとめて起動することが出来ます。ただ、事前に`start_config.cmd`でJAVAのパスを指定している必要があります。**
### ビルド ### ビルド
Grasscutterは依存関係とビルド処理にGradleを使用しています。 GrasscutterはGradleを使用して依存関係とビルド処理しています。
**必要要件:** **要件:**
- [Java SE Development Kit 17以降](https://www.oracle.com/java/technologies/javase/jdk17-archive-downloads.html) - [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)
- [NodeJS](https://nodejs.org/en/download) (任意、ハンドブックの生成に必要)
##### Clone ##### Windows
```shell
git clone --recurse-submodules https://github.com/Grasscutters/Grasscutter.git
cd Grasscutter
```
##### Compile
**Note:** 環境によってはハンドブックの生成が失敗する場合があります。ハンドブックの生成をさせない場合は `gradlew jar` コマンドに `-PskipHandbook=1` を付け加えてください。
Windows:
```shell ```shell
git clone https://github.com/Grasscutters/Grasscutter.git git clone https://github.com/Grasscutters/Grasscutter.git
@ -69,7 +86,7 @@ cd Grasscutter
.\gradlew jar # コンパイル .\gradlew jar # コンパイル
``` ```
Linux: ##### Linux
```bash ```bash
git clone https://github.com/Grasscutters/Grasscutter.git git clone https://github.com/Grasscutters/Grasscutter.git
@ -78,23 +95,13 @@ chmod +x gradlew
./gradlew jar # コンパイル ./gradlew jar # コンパイル
``` ```
##### 手動によるハンドブックの生成 生成されたjarファイルはプロジェクトフォルダのルートに有ります。
Gradleを使用する場合: ### コマンドリストは[wiki](https://github.com/Grasscutters/Grasscutter/wiki/Commands)へ移動しました。
```shell
./gradlew generateHandbook
```
NPMを使用する場合: # トラブルシューティング
```shell
cd src/handbook
npm install
npm run build
```
* コンパイルが失敗した場合JDKがインストールされているか確認してください。(JDKのバージョンが17以降であることと、環境変数でJDKのパスが設定されている必要があります)
生成されたjarファイルはプロジェクトのルートフォルダにあります * クライアントが接続できない・ログインできない・エラーコード4206・またその他場合、ほとんどは、プロキシデーモンの設定が問題です。Fiddlerを使っている場合はデフォルトポートを8888以外の別のポートに変更してみてください
Fiddlerを使用している場合はポートが8888以外に設定されていることを確認してください。
### トラブルシューティング * 起動シーケンス(順番): MongoDB > Grasscutter > プロキシツール (mitmdumpかfiddler、その他) > ゲーム
よく散見されるトラブルとそれに対する解決策のまとめリストや、質問し誰かの助けを得たい場合は、Grasscutterの[Discordサーバー](https://discord.gg/T5vZU6UyeG)に参加し、サポートチャンネルを参照してください。

View File

@ -26,12 +26,10 @@
- 获取Java 17https://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) - 获取[MongoDB社区版](https://www.mongodb.com/try/download/community)
- 获取游戏4.0正式版 (如果你没有4.0的客户端,可以在这里找到): - 获取游戏4.0正式版 (如果你没有4.0的客户端,可以在这里找到):https://github.com/MAnggiarMustofa/GI-Download-Library/blob/main/GenshinImpact/Client/4.0.0.md)
[123pan share](https://www.123pan.com/s/HoqUVv-U7SBA.html)
[github](https://github.com/JRSKelvin/GenshinRepository/blob/main/Version%204.0.0.md)
- 下载[最新的Cultivation版本](https://github.com/Grasscutters/Cultivation/releases/latest)(使用以“.msi”为后缀的安装包 - 下载[最新的Cultivation版本](https://github.com/Grasscutters/Cultivation/releases/latest)(使用以“.msi”为后缀的安装包
- 以管理员身份打开Cultivation按右上角的下载按钮。 - 以管理员身份打开Culivation按右上角的下载按钮。
- 点击“下载 Grasscutter 一体化” - 点击“下载 Grasscutter 一体化”
- 点击右上角的齿轮 - 点击右上角的齿轮
- 将游戏安装路径设置为你游戏所在的位置。 - 将游戏安装路径设置为你游戏所在的位置。

View File

@ -29,7 +29,7 @@
- 下載遊戲版本 REL3.7(如果你沒有的話,可以在[這裡](https://github.com/MAnggiarMustofa/GI-Download-Library/blob/main/GenshinImpact/Client/3.7.0.md)找到 3.7 客戶端) - 下載遊戲版本 REL3.7(如果你沒有的話,可以在[這裡](https://github.com/MAnggiarMustofa/GI-Download-Library/blob/main/GenshinImpact/Client/3.7.0.md)找到 3.7 客戶端)
- 下載 [最新的 Cultivation 版本](https://github.com/Grasscutters/Cultivation/releases/latest)。使用 `.msi` 安裝程式。 - 下載 [最新的 Cultivation 版本](https://github.com/Grasscutters/Cultivation/releases/latest)。使用 `.msi` 安裝程式。
- 以管理員身分打開 Cultivation按右上角的下載按鈕。 - 以管理員身分打開 Culivation按右上角的下載按鈕。
- 點擊 `Download All-in-One` - 點擊 `Download All-in-One`
- 點擊右上角的齒輪 - 點擊右上角的齒輪
- 將遊戲安裝路徑設置為你的遊戲所在的位置。 - 將遊戲安裝路徑設置為你的遊戲所在的位置。

View File

@ -1,188 +0,0 @@
# Hide and Seek!
Documentation on how the **Hide and Seek** game works.\
Externally dubbed: `Windtrace`.
# Map IDs
TODO: Document the map IDs of Windtrace.
TODO: Investigate `ServerGlobalValueChangeNotify`
# Asking Players to Play in a Co-Op Game
1. The client will send `DraftOwnerStartInviteReq`
2. The server will send `DraftOwnerInviteNotify` to all clients.
3. The server will send `DraftOwnerStartInviteRsp`
# Matching in a Co-Op Game
1. World owner talks to Gygax and begins a Windtrace game.
2. The packet `DraftOwnerInviteNotify` is sent to clients.
3. Clients will respond with `DraftGuestReplyInviteReq` (client-side)
4. The server will respond with `DraftGuestReplyInviteRsp`
5. The server will respond with `DraftInviteResultNotify`
# Starting Windtrace
1. If `DraftInviteResultNotify` is a success, the server will send a series of packets.
1. A series of `SceneEntityAppearNotify` packets.
2. `NpcTalkStateNotify`
3. `PlayerEnterSceneNotify`
4. `MultistagePlayInfoNotify`
2. The players are then teleported to the Windtrace map in their locations.
3. Server will send packets to clients. (this is server boilerplate)
4. The server sends another `MultistagePlayInfoNotify` to clients.
# Changing Avatars - Others
1. The server will send a `AvatarEquipChangeNotify` packet to clients.
2. The server will send a `SceneTeamUpdateNotify` packet to clients.
3. The server will send a `HideAndSeekPlayerSetAvatarNotify` packet to clients.
# Getting Ready
1. The client will send `HideAndSeekSetReadyReq` to the server.
2. The server will reply with `HideAndSeekPlayerReadyNotify` to clients.
3. The server will send `MultistagePlayInfoNotify` to clients.
4. The server will reply with `HideAndSeekSetReadyRsp` to the client.
5. If all players are ready, the server will move on to start Windtrace.
# Starting Windtrace
1. When all players are ready, the server will send a series of packets to players.
1. `GalleryStartNotify`
2. `SceneGalleryInfoNotify`
3. `MultistagePlayInfoNotify`
4. `MultistagePlayStageEndNotify`
5. This will only get sent at the `1.` countdown.
### Notes:
- `GuestReplyInviteRsp` is sent **after** `DraftInviteResultNotify`.
## `DraftOwnerInviteNotify`
- `invite_deadline_time` - This is the time when the invite expires.
- `draft_id` - The value is always `3001` for Windtrace.
## `DraftOwnerStartInviteReq`
- `draft_id` - The value is always `3001` for Windtrace.
## `DraftOwnerStartInviteRsp`
- `draft_id` - The value is always `3001` for Windtrace.
- `invite_fail_info_list` - A list of players who weren't invited.
- `retcode` - The response code.
- `wrong_uid` - Always `0`. (undocumented)
## `DraftGuestReplyInviteReq`
- `draft_id` - The value is always `3001` for Windtrace.
- `is_agree` - A boolean value for whether the client accepts the invite.
## `DraftGuestReplyInviteRsp`
- `draft_id` - The value is always `3001` for Windtrace.
- `retcode` - Response code for the request.
- `is_agree` - A boolean value for whether the server acknowledges the client's invite acceptation.
## `DraftInviteResultNotify`
- `draft_id` - The value is always `3001` for Windtrace.
- `is_all_agree` - A boolean value for whether all clients accepted the invite.
## `NpcTalkStateNotify`
- `is_ban` - This value is always true when entering Windtrace.
## `PlayerEnterSceneNotify`
- `pos` - This is where the player will be teleported to.
- This value depends on if the player is a hunter or a runner.
- This value is set by the server and must be hardcoded/read from a JSON file.
## `MultistagePlayStageEndNotify`
- `play_index` - Value picked by the server. (use 1)
- `group_id` - This value is always `133002121` for Windtrace.
## `MultistagePlayInfoNotify` - Initial + PostEnterSceneReq
- Image Reference: ![img.png](images/multistageplayinfo.png)
- `info` - MultistagePlayInfo data.
- `group_id` - The value is always `133002121` for Windtrace.
- `play_index` - Value picked by the server. (use 1)
- `hide_and_seek_info` - Information about Windtrace.
- `hider_uid_list` - A list of UIDs (ints) of the hiders.
- `hunter_uid` - The UID (int) of the hunter.
- `map_id` - The ID of the Windtrace map.
- `stage_type` - Windtrace state.
- This will be `HIDE_AND_SEEK_STAGE_TYPE_PREPARE`.
- `battle_info_map` - Contains a dictionary of UID -> `HideAndSeekPlayerBattleInfo` objects.
- `skill_list` - Array of 3 values of skill IDs chosen by the player.
- `avatar_id` - The ID of the avatar the player wants to use.
- `is_ready` - The player's in-game ready state.
- `costume_id` - The costume the player's avatar is wearing.
## `MultistagePlayInfoNotify` - Picking Avatars
- Image Reference: ![img.png](images/pickavatar.png)
- **Note:** This packet matches the initial structure and data.
- `info.hide_and_seek_info.stage_type` - This will be `HIDE_AND_SEEK_STAGE_TYPE_PICK`.
## `MultistagePlayInfoNotify` - Starting Windtrace
- Image Reference: ![img.png](images/startwindtrace.png)
- **Note:** This packet matches the initial structure and data.
- `info.hide_and_seek_info.stage_type` - This will be `HIDE_AND_SEEK_STAGE_TYPE_HIDE`.
## `MultistagePlayInfoNotify` - Seeking Time
- Image Reference: ![img.png](images/seektime.png)
- **Note:** This packet matches the initial structure and data.
- `info.hide_and_seek_info.stage_type` - This will be `HIDE_AND_SEEK_STAGE_TYPE_SEEK`.
## `MultistagePlayInfoNotify` - Finish Windtrace
- Image Reference: ![img.png](images/seektime.png)
- **Note:** This packet matches the initial structure and data.
- `info.hide_and_seek_info.stage_type` - This will be `HIDE_AND_SEEK_STAGE_TYPE_SETTLE`.
## `HideAndSeekPlayerSetAvatarNotify`
- `avatar_id` - The ID of the new avatar the player wants to use.
- `uid` - The UID of the player who changed their avatar.
- `costume_id` - The costume the player's avatar is wearing.
## `HideAndSeekSetReadyRsp`
- `retcode` - Response code for the request.
## `HideAndSeekPlayerReadyNotify`
- `uid_list` - A list of UIDs (ints) of the players who are ready.
## `GalleryStartNotify`
- `gallery_id` - TODO: Check if this value is always `7056` for Windtrace.
- `start_time` - This value is always `2444` for Windtrace.
- This value is `200` when displaying game end statistics.
- `owner_uid` - The UID of the player who started the Windtrace game.
- `player_count` - The number of players in the Windtrace game.
- `end_time` - This value is always the same as `start_time`.
## `SceneGalleryInfoNotify` - Starting Windtrace
- `gallery_info` - SceneGalleryInfo data.
- `end_time` - This value is always the same as `start_time`.
- `start_time` - This value is always `2444` for Windtrace.
- This value is `200` when displaying game end statistics.
- `gallery_id` - This value is always the same as `gallery_id` from `GalleryStartNotify`.
- `stage` - The current stage of the gallery.
- This will be `GALLERY_STAGE_TYPE_START`.
- `owner_uid` - The UID of the player who started the Windtrace game.
- `hide_and_seek_info` - SceneGalleryHideAndSeekInfo
- `visible_uid_list` - List of UIDs (ints) of the players who were left alive.
- `caught_uid_list` - List of UIDs (ints) of the players who have been caught.
- `player_count` - The amount of players in the Windtrace game.
- `pre_start_end_time` - This value is always `0` for Windtrace.
## `HideAndSeekSettleNotify`
- `reason` - The reason for the game ending.
- `winner_list` - A list of UIDs (ints) of the players who won the game.
- `settle_info_list` - HideAndSeekSettleInfo data.
- This is a list of players who participated in the game.
## `HideAndSeekSettleInfo`
- `card_list` - A collection of `ExhibitionDisplayInfo`
- If unknown: hardcode the specified values. ![img.png](images/defaultexhibitioninfo.png)
- These values are repeated during testing.
- `uid` - The UID of the player who participated in the game.
- `nickname` - The player's nickname.
- `head_image` - This value is always `0`.
- `online_id` - This value is always blank.
- `profile_picture` - `ProfilePicture` object.
- `play_index` - Value picked by the server. (use 1)
- `stage_type` - The stage type. (inconclusive; TODO)
- `cost_time` - The amount of time the player took to complete the game.
- `score_list` - A list of player scores.
## `ExhibitionDisplayInfo`
- `id` - The ID of the reward.
- `param` - The amount of the reward given.
- `detail_param` - This value is *mostly* 0.
- This value **matches** param when the reward is of the amount of time spent playing. (participation reward)

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 30 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 42 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 44 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 47 KiB

View File

@ -1,3 +0,0 @@
#/bin/sh
java -jar /app/grasscutter.jar

View File

@ -68,7 +68,7 @@ class MlgmXyysd_Animation_Company_Proxy:
] ]
def request(self, flow: http.HTTPFlow) -> None: def request(self, flow: http.HTTPFlow) -> None:
if flow.request.pretty_host in self.LIST_DOMAINS: if flow.request.host in self.LIST_DOMAINS:
if USE_SSL: if USE_SSL:
flow.request.scheme = "https" flow.request.scheme = "https"
else: else:

View File

@ -1,12 +1,12 @@
package emu.grasscutter; package emu.grasscutter;
public final class DebugConstants { public final class DebugConstants {
public static final boolean LOG_ABILITIES = false; public static boolean LOG_ABILITIES = false;
public static final boolean LOG_LUA_SCRIPTS = false; public static boolean LOG_LUA_SCRIPTS = false;
public static final boolean LOG_QUEST_START = false; public static boolean LOG_QUEST_START = false;
public static final boolean LOG_MISSING_ABILITIES = false; public static boolean LOG_MISSING_ABILITIES = false;
public static final boolean LOG_MISSING_LUA_SCRIPTS = false; public static boolean LOG_MISSING_LUA_SCRIPTS = false;
public static final boolean LOG_MISSING_ABILITY_HANDLERS = false; public static boolean LOG_MISSING_ABILITY_HANDLERS = false;
/** /**
* WARNING: THIS IS A DANGEROUS SETTING. DO NOT ENABLE UNLESS YOU KNOW WHAT YOU ARE DOING. This * WARNING: THIS IS A DANGEROUS SETTING. DO NOT ENABLE UNLESS YOU KNOW WHAT YOU ARE DOING. This

View File

@ -7,8 +7,8 @@ import emu.grasscutter.utils.objects.SparseSet;
import java.util.Arrays; import java.util.Arrays;
public final class GameConstants { public final class GameConstants {
public static final String VERSION = "4.0.0"; public static String VERSION = "4.0.0";
public static final int[] VERSION_PARTS = {4, 0, 0}; public static int[] VERSION_PARTS = {4, 0, 0};
public static boolean DEBUG = false; public static boolean DEBUG = false;
public static final int DEFAULT_TEAMS = 4; public static final int DEFAULT_TEAMS = 4;

View File

@ -1,8 +1,5 @@
package emu.grasscutter; package emu.grasscutter;
import static emu.grasscutter.config.Configuration.SERVER;
import static emu.grasscutter.utils.lang.Language.translate;
import ch.qos.logback.classic.*; import ch.qos.logback.classic.*;
import emu.grasscutter.auth.*; import emu.grasscutter.auth.*;
import emu.grasscutter.command.*; import emu.grasscutter.command.*;
@ -21,16 +18,20 @@ import emu.grasscutter.tools.Tools;
import emu.grasscutter.utils.*; import emu.grasscutter.utils.*;
import emu.grasscutter.utils.lang.Language; import emu.grasscutter.utils.lang.Language;
import io.netty.util.concurrent.FastThreadLocalThread; import io.netty.util.concurrent.FastThreadLocalThread;
import java.io.*;
import java.util.Calendar;
import java.util.concurrent.*;
import javax.annotation.Nullable;
import lombok.*; import lombok.*;
import org.jline.reader.*; import org.jline.reader.*;
import org.jline.terminal.*; import org.jline.terminal.*;
import org.reflections.Reflections; import org.reflections.Reflections;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import javax.annotation.Nullable;
import java.io.*;
import java.util.Calendar;
import java.util.concurrent.*;
import static emu.grasscutter.config.Configuration.SERVER;
import static emu.grasscutter.utils.lang.Language.translate;
public final class Grasscutter { public final class Grasscutter {
public static final File configFile = new File("./config.json"); public static final File configFile = new File("./config.json");
public static final Reflections reflector = new Reflections("emu.grasscutter"); public static final Reflections reflector = new Reflections("emu.grasscutter");
@ -158,8 +159,6 @@ public final class Grasscutter {
// Generate handbooks. // Generate handbooks.
Tools.createGmHandbooks(false); Tools.createGmHandbooks(false);
// Generate gacha mappings.
Tools.generateGachaMappings();
} }
// Start servers. // Start servers.
@ -183,12 +182,18 @@ public final class Grasscutter {
// Hook into shutdown event. // Hook into shutdown event.
Runtime.getRuntime().addShutdownHook(new Thread(Grasscutter::onShutdown)); Runtime.getRuntime().addShutdownHook(new Thread(Grasscutter::onShutdown));
// Start database heartbeat.
Database.startSaveThread();
// Open console. // Open console.
Grasscutter.startConsole(); Grasscutter.startConsole();
} }
/** Server shutdown event. */ /** Server shutdown event. */
private static void onShutdown() { private static void onShutdown() {
// Save all data.
Database.saveAll();
// Disable all plugins. // Disable all plugins.
if (pluginManager != null) pluginManager.disablePlugins(); if (pluginManager != null) pluginManager.disablePlugins();
// Shutdown the game server. // Shutdown the game server.
@ -198,14 +203,14 @@ public final class Grasscutter {
// Wait for Grasscutter's thread pool to finish. // Wait for Grasscutter's thread pool to finish.
var executor = Grasscutter.getThreadPool(); var executor = Grasscutter.getThreadPool();
executor.shutdown(); executor.shutdown();
if (!executor.awaitTermination(5, TimeUnit.SECONDS)) { if (!executor.awaitTermination(1, TimeUnit.MINUTES)) {
executor.shutdownNow(); executor.shutdownNow();
} }
// Wait for database operations to finish. // Wait for database operations to finish.
var dbExecutor = DatabaseHelper.getEventExecutor(); var dbExecutor = DatabaseHelper.getEventExecutor();
dbExecutor.shutdown(); dbExecutor.shutdown();
if (!dbExecutor.awaitTermination(5, TimeUnit.SECONDS)) { if (!dbExecutor.awaitTermination(2, TimeUnit.MINUTES)) {
dbExecutor.shutdownNow(); dbExecutor.shutdownNow();
} }
} catch (InterruptedException ignored) { } catch (InterruptedException ignored) {
@ -312,7 +317,7 @@ public final class Grasscutter {
public static void updateDayOfWeek() { public static void updateDayOfWeek() {
Calendar calendar = Calendar.getInstance(); Calendar calendar = Calendar.getInstance();
Grasscutter.currentDayOfWeek = calendar.get(Calendar.DAY_OF_WEEK); Grasscutter.currentDayOfWeek = calendar.get(Calendar.DAY_OF_WEEK);
logger.debug("Set day of week to {}", currentDayOfWeek); logger.debug("Set day of week to " + currentDayOfWeek);
} }
public static void startConsole() { public static void startConsole() {

View File

@ -103,7 +103,7 @@ public final class DefaultAuthenticators {
KeyFactory keyFactory = KeyFactory.getInstance("RSA"); KeyFactory keyFactory = KeyFactory.getInstance("RSA");
RSAPrivateKey private_key = (RSAPrivateKey) keyFactory.generatePrivate(keySpec); RSAPrivateKey private_key = (RSAPrivateKey) keyFactory.generatePrivate(keySpec);
Cipher cipher = Cipher.getInstance("RSA/ECB/OAEPWithSHA-256AndMGF1Padding"); Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding");
cipher.init(Cipher.DECRYPT_MODE, private_key); cipher.init(Cipher.DECRYPT_MODE, private_key);

View File

@ -1,6 +1,5 @@
package emu.grasscutter.command; package emu.grasscutter.command;
import emu.grasscutter.game.world.Position;
import java.util.*; import java.util.*;
import java.util.function.BiConsumer; import java.util.function.BiConsumer;
import java.util.regex.*; import java.util.regex.*;
@ -55,78 +54,4 @@ public class CommandHelpers {
}); });
return args; return args;
} }
public static float parseRelative(String input, Float current) {
if (input.contains("~")) { // Relative
if (!input.equals("~")) { // Relative with offset
current += Float.parseFloat(input.replace("~", ""));
} // Else no offset, no modification
} else { // Absolute
current = Float.parseFloat(input);
}
return current;
}
public static Position parsePosition(
String inputX, String inputY, String inputZ, Position curPos, Position curRot) {
Position offset = new Position();
Position target = new Position(curPos);
if (inputX.contains("~")) { // Relative
if (!inputX.equals("~")) { // Relative with offset
target.addX(Float.parseFloat(inputX.replace("~", "")));
}
} else if (inputX.contains("^")) {
if (!inputX.equals("^")) {
offset.setX(Float.parseFloat(inputX.replace("^", "")));
}
} else { // Absolute
target.setX(Float.parseFloat(inputX));
}
if (inputY.contains("~")) { // Relative
if (!inputY.equals("~")) { // Relative with offset
target.addY(Float.parseFloat(inputY.replace("~", "")));
}
} else if (inputY.contains("^")) {
if (!inputY.equals("^")) {
offset.setY(Float.parseFloat(inputY.replace("^", "")));
}
} else { // Absolute
target.setY(Float.parseFloat(inputY));
}
if (inputZ.contains("~")) { // Relative
if (!inputZ.equals("~")) { // Relative with offset
target.addZ(Float.parseFloat(inputZ.replace("~", "")));
}
} else if (inputZ.contains("^")) {
if (!inputZ.equals("^")) {
offset.setZ(Float.parseFloat(inputZ.replace("^", "")));
}
} else { // Absolute
target.setZ(Float.parseFloat(inputZ));
}
if (!offset.equal3d(Position.ZERO)) {
return calculateOffset(target, curRot, offset);
} else {
return target;
}
}
public static Position calculateOffset(Position pos, Position rot, Position offset) {
// Degrees to radians
float angleZ = (float) Math.toRadians(rot.getY());
float angleX = (float) Math.toRadians(rot.getY() + 90);
// Calculate offset based on current position and rotation
return new Position(
pos.getX()
+ offset.getZ() * (float) Math.sin(angleZ)
+ offset.getX() * (float) Math.sin(angleX),
pos.getY() + offset.getY(),
pos.getZ()
+ offset.getZ() * (float) Math.cos(angleZ)
+ offset.getX() * (float) Math.cos(angleX));
}
} }

View File

@ -53,7 +53,7 @@ public final class CommandMap {
* @return Instance chaining. * @return Instance chaining.
*/ */
public CommandMap registerCommand(String label, CommandHandler command) { public CommandMap registerCommand(String label, CommandHandler command) {
Grasscutter.getLogger().trace("Registered command: {}", label); Grasscutter.getLogger().trace("Registered command: " + label);
label = label.toLowerCase(); label = label.toLowerCase();
// Get command data. // Get command data.
@ -76,7 +76,7 @@ public final class CommandMap {
* @return Instance chaining. * @return Instance chaining.
*/ */
public CommandMap unregisterCommand(String label) { public CommandMap unregisterCommand(String label) {
Grasscutter.getLogger().trace("Un-registered command: {}", label); Grasscutter.getLogger().trace("Un-registered command: " + label);
CommandHandler handler = this.commands.get(label); CommandHandler handler = this.commands.get(label);
if (handler == null) return this; if (handler == null) return this;
@ -231,12 +231,14 @@ public final class CommandMap {
if (player != null) { if (player != null) {
Grasscutter.getLogger() Grasscutter.getLogger()
.info( .info(
"Command used by [{} (Player UID: {})]: {}", "Command used by ["
player.getAccount().getUsername(), + player.getAccount().getUsername()
player.getUid(), + " (Player UID: "
rawMessage); + player.getUid()
+ ")]: "
+ rawMessage);
} else { } else {
Grasscutter.getLogger().info("Command used by server console: {}", rawMessage); Grasscutter.getLogger().info("Command used by server console: " + rawMessage);
} }
} }
@ -348,12 +350,11 @@ public final class CommandMap {
this.registerCommand(cmdData.label(), (CommandHandler) object); this.registerCommand(cmdData.label(), (CommandHandler) object);
else else
Grasscutter.getLogger() Grasscutter.getLogger()
.error("Class {} is not a CommandHandler!", annotated.getName()); .error("Class " + annotated.getName() + " is not a CommandHandler!");
} catch (Exception exception) { } catch (Exception exception) {
Grasscutter.getLogger() Grasscutter.getLogger()
.error( .error(
"Failed to register command handler for {}", "Failed to register command handler for " + annotated.getSimpleName(),
annotated.getSimpleName(),
exception); exception);
} }
}); });

View File

@ -69,7 +69,7 @@ public final class AchievementCommand implements CommandHandler {
@Override @Override
public void execute(Player sender, Player targetPlayer, List<String> args) { public void execute(Player sender, Player targetPlayer, List<String> args) {
if (args.isEmpty()) { if (args.size() < 1) {
this.sendUsageMessage(sender); this.sendUsageMessage(sender);
return; return;
} }
@ -88,7 +88,7 @@ public final class AchievementCommand implements CommandHandler {
private void grant( private void grant(
Player sender, Player targetPlayer, Achievements achievements, List<String> args) { Player sender, Player targetPlayer, Achievements achievements, List<String> args) {
if (args.isEmpty()) { if (args.size() < 1) {
this.sendUsageMessage(sender); this.sendUsageMessage(sender);
} }
@ -109,7 +109,7 @@ public final class AchievementCommand implements CommandHandler {
private void revoke( private void revoke(
Player sender, Player targetPlayer, Achievements achievements, List<String> args) { Player sender, Player targetPlayer, Achievements achievements, List<String> args) {
if (args.isEmpty()) { if (args.size() < 1) {
this.sendUsageMessage(sender); this.sendUsageMessage(sender);
} }
@ -136,7 +136,7 @@ public final class AchievementCommand implements CommandHandler {
parseInt(args.remove(0)) parseInt(args.remove(0))
.ifPresentOrElse( .ifPresentOrElse(
integer -> integer -> {
parseInt(args.remove(0)) parseInt(args.remove(0))
.ifPresentOrElse( .ifPresentOrElse(
progress -> { progress -> {
@ -148,7 +148,8 @@ public final class AchievementCommand implements CommandHandler {
sender, ret.getRet().getKey()); sender, ret.getRet().getKey());
} }
}, },
() -> this.sendUsageMessage(sender)), () -> this.sendUsageMessage(sender));
},
() -> this.sendUsageMessage(sender)); () -> this.sendUsageMessage(sender));
} }
} }

View File

@ -19,7 +19,7 @@ public final class AnnounceCommand implements CommandHandler {
@Override @Override
public void execute(Player sender, Player targetPlayer, List<String> args) { public void execute(Player sender, Player targetPlayer, List<String> args) {
var manager = Grasscutter.getGameServer().getAnnouncementSystem(); var manager = Grasscutter.getGameServer().getAnnouncementSystem();
if (args.isEmpty()) { if (args.size() < 1) {
sendUsageMessage(sender); sendUsageMessage(sender);
return; return;
} }

View File

@ -51,7 +51,7 @@ public final class ClearCommand implements CommandHandler {
// Extract any tagged int arguments (e.g. "lv90", "x100", "r5") // Extract any tagged int arguments (e.g. "lv90", "x100", "r5")
parseIntParameters(args, param, intCommandHandlers); parseIntParameters(args, param, intCommandHandlers);
if (args.isEmpty()) { if (args.size() < 1) {
sendUsageMessage(sender); sendUsageMessage(sender);
return; return;
} }
@ -93,10 +93,9 @@ public final class ClearCommand implements CommandHandler {
} }
} }
@Setter
private static class ClearItemParameters { private static class ClearItemParameters {
public int lvl = 1; @Setter public int lvl = 1;
public int refinement = 1; @Setter public int refinement = 1;
public int rank = 4; @Setter public int rank = 4;
} }
} }

View File

@ -32,12 +32,9 @@ public final class DebugCommand implements CommandHandler {
var scene = sender.getScene(); var scene = sender.getScene();
var entityId = Integer.parseInt(args.get(0)); var entityId = Integer.parseInt(args.get(0));
// TODO Might want to allow groupId specification,
// because there can be more than one entity with
// the given config ID.
var entity = var entity =
args.size() > 1 && args.get(1).equals("config") args.size() > 1 && args.get(1).equals("config")
? scene.getFirstEntityByConfigId(entityId) ? scene.getEntityByConfigId(entityId)
: scene.getEntityById(entityId); : scene.getEntityById(entityId);
if (entity == null) { if (entity == null) {
sender.dropMessage("Entity not found."); sender.dropMessage("Entity not found.");

View File

@ -16,7 +16,7 @@ public final class EnterDungeonCommand implements CommandHandler {
@Override @Override
public void execute(Player sender, Player targetPlayer, List<String> args) { public void execute(Player sender, Player targetPlayer, List<String> args) {
if (args.isEmpty()) { if (args.size() < 1) {
sendUsageMessage(sender); sendUsageMessage(sender);
return; return;
} }

View File

@ -51,10 +51,7 @@ public final class EntityCommand implements CommandHandler {
} }
param.scene = targetPlayer.getScene(); param.scene = targetPlayer.getScene();
// TODO Might want to allow groupId specification, var entity = param.scene.getEntityByConfigId(param.configId);
// because there can be more than one entity with
// the given config ID.
var entity = param.scene.getFirstEntityByConfigId(param.configId);
if (entity == null) { if (entity == null) {
CommandHandler.sendMessage(sender, translate(sender, "commands.entity.not_found_error")); CommandHandler.sendMessage(sender, translate(sender, "commands.entity.not_found_error"));

View File

@ -3,23 +3,16 @@ package emu.grasscutter.command.commands;
import static emu.grasscutter.GameConstants.*; import static emu.grasscutter.GameConstants.*;
import static emu.grasscutter.command.CommandHelpers.*; import static emu.grasscutter.command.CommandHelpers.*;
import emu.grasscutter.command.Command; import emu.grasscutter.command.*;
import emu.grasscutter.command.CommandHandler; import emu.grasscutter.data.*;
import emu.grasscutter.data.GameData;
import emu.grasscutter.data.GameDepot;
import emu.grasscutter.data.excels.ItemData; import emu.grasscutter.data.excels.ItemData;
import emu.grasscutter.data.excels.avatar.AvatarData; import emu.grasscutter.data.excels.avatar.AvatarData;
import emu.grasscutter.data.excels.reliquary.ReliquaryAffixData; import emu.grasscutter.data.excels.reliquary.*;
import emu.grasscutter.data.excels.reliquary.ReliquaryMainPropData;
import emu.grasscutter.game.avatar.Avatar; import emu.grasscutter.game.avatar.Avatar;
import emu.grasscutter.game.inventory.GameItem; import emu.grasscutter.game.inventory.*;
import emu.grasscutter.game.inventory.ItemType;
import emu.grasscutter.game.player.Player; import emu.grasscutter.game.player.Player;
import emu.grasscutter.game.props.ActionReason; import emu.grasscutter.game.props.*;
import emu.grasscutter.game.props.FightProperty; import java.util.*;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.function.BiConsumer; import java.util.function.BiConsumer;
import java.util.regex.Pattern; import java.util.regex.Pattern;
import lombok.Setter; import lombok.Setter;
@ -286,7 +279,7 @@ public final class GiveCommand implements CommandHandler {
parseIntParameters(args, param, intCommandHandlers); parseIntParameters(args, param, intCommandHandlers);
// At this point, first remaining argument MUST be itemId/avatarId // At this point, first remaining argument MUST be itemId/avatarId
if (args.isEmpty()) { if (args.size() < 1) {
sendUsageMessage(sender); // Reachable if someone does `/give lv90` or similar sendUsageMessage(sender); // Reachable if someone does `/give lv90` or similar
throw new IllegalArgumentException(); throw new IllegalArgumentException();
} }
@ -369,7 +362,7 @@ public final class GiveCommand implements CommandHandler {
@Override @Override
public void execute(Player sender, Player targetPlayer, List<String> args) { public void execute(Player sender, Player targetPlayer, List<String> args) {
if (args.isEmpty()) { // *No args* if (args.size() < 1) { // *No args*
sendUsageMessage(sender); sendUsageMessage(sender);
return; return;
} }

View File

@ -63,7 +63,9 @@ public final class GroupCommand implements CommandHandler {
CommandHandler.sendMessage(sender, translate(sender, "commands.group.refreshed", groupId)); CommandHandler.sendMessage(sender, translate(sender, "commands.group.refreshed", groupId));
} }
default -> sendUsageMessage(sender); default -> {
sendUsageMessage(sender);
}
} }
} }
} }

View File

@ -19,14 +19,14 @@ public final class ListCommand implements CommandHandler {
Map<Integer, Player> playersMap = Grasscutter.getGameServer().getPlayers(); Map<Integer, Player> playersMap = Grasscutter.getGameServer().getPlayers();
boolean needUID = false; boolean needUID = false;
if (!args.isEmpty()) { if (args.size() > 0) {
needUID = args.get(0).equals("uid"); needUID = args.get(0).equals("uid");
} }
CommandHandler.sendMessage( CommandHandler.sendMessage(
sender, translate(sender, "commands.list.success", playersMap.size())); sender, translate(sender, "commands.list.success", playersMap.size()));
if (!playersMap.isEmpty()) { if (playersMap.size() != 0) {
StringBuilder playerSet = new StringBuilder(); StringBuilder playerSet = new StringBuilder();
boolean finalNeedUID = needUID; boolean finalNeedUID = needUID;

View File

@ -18,7 +18,7 @@ public final class ResetConstCommand implements CommandHandler {
@Override @Override
public void execute(Player sender, Player targetPlayer, List<String> args) { public void execute(Player sender, Player targetPlayer, List<String> args) {
if (!args.isEmpty() && args.get(0).equalsIgnoreCase("all")) { if (args.size() > 0 && args.get(0).equalsIgnoreCase("all")) {
targetPlayer.getAvatars().forEach(this::resetConstellation); targetPlayer.getAvatars().forEach(this::resetConstellation);
CommandHandler.sendMessage(sender, translate(sender, "commands.resetConst.reset_all")); CommandHandler.sendMessage(sender, translate(sender, "commands.resetConst.reset_all"));
} else { } else {

View File

@ -24,7 +24,8 @@ public final class SendMailCommand implements CommandHandler {
// the command system (again). For now this will do // the command system (again). For now this will do
// Key = User that is constructing the mail. // Key = User that is constructing the mail.
private static final HashMap<Integer, MailBuilder> mailBeingConstructed = new HashMap<>(); private static final HashMap<Integer, MailBuilder> mailBeingConstructed =
new HashMap<Integer, MailBuilder>();
// Yes this is awful and I hate it. // Yes this is awful and I hate it.
@Override @Override
@ -68,7 +69,7 @@ public final class SendMailCommand implements CommandHandler {
} else { } else {
MailBuilder mailBuilder = mailBeingConstructed.get(senderId); MailBuilder mailBuilder = mailBeingConstructed.get(senderId);
if (!args.isEmpty()) { if (args.size() >= 1) {
switch (args.get(0).toLowerCase()) { switch (args.get(0).toLowerCase()) {
case "stop" -> { case "stop" -> {
mailBeingConstructed.remove(senderId); mailBeingConstructed.remove(senderId);
@ -105,12 +106,14 @@ public final class SendMailCommand implements CommandHandler {
getConstructionArgs(mailBuilder.constructionStage, sender))); getConstructionArgs(mailBuilder.constructionStage, sender)));
} }
} }
case "help" -> CommandHandler.sendMessage( case "help" -> {
CommandHandler.sendMessage(
sender, sender,
translate( translate(
sender, sender,
"commands.sendMail.please_use", "commands.sendMail.please_use",
getConstructionArgs(mailBuilder.constructionStage, sender))); getConstructionArgs(mailBuilder.constructionStage, sender)));
}
default -> { default -> {
switch (mailBuilder.constructionStage) { switch (mailBuilder.constructionStage) {
case 0 -> { case 0 -> {

View File

@ -17,7 +17,7 @@ public final class SendMessageCommand implements CommandHandler {
@Override @Override
public void execute(Player sender, Player targetPlayer, List<String> args) { public void execute(Player sender, Player targetPlayer, List<String> args) {
if (args.isEmpty()) { if (args.size() == 0) {
sendUsageMessage(sender); sendUsageMessage(sender);
return; return;
} }

View File

@ -17,7 +17,7 @@ import java.util.List;
public final class SetConstCommand implements CommandHandler { public final class SetConstCommand implements CommandHandler {
@Override @Override
public void execute(Player sender, Player targetPlayer, List<String> args) { public void execute(Player sender, Player targetPlayer, List<String> args) {
if (args.isEmpty()) { if (args.size() < 1) {
sendUsageMessage(sender); sendUsageMessage(sender);
return; return;
} }

View File

@ -291,9 +291,9 @@ public final class SetPropCommand implements CommandHandler {
} }
static class Prop { static class Prop {
final String name; String name;
final PlayerProperty prop; PlayerProperty prop;
final PseudoProp pseudoProp; PseudoProp pseudoProp;
public Prop(PlayerProperty prop) { public Prop(PlayerProperty prop) {
this(prop.toString(), prop, PseudoProp.NONE); this(prop.toString(), prop, PseudoProp.NONE);

View File

@ -12,7 +12,7 @@ import lombok.val;
@Command( @Command(
label = "setSceneTag", label = "setSceneTag",
aliases = {"tag"}, aliases = {"tag"},
usage = {"<add|remove|unlockall|reset> <sceneTagId>"}, usage = {"<add|remove|unlockall> <sceneTagId>"},
permission = "player.setscenetag", permission = "player.setscenetag",
permissionTargeted = "player.setscenetag.others") permissionTargeted = "player.setscenetag.others")
public final class SetSceneTagCommand implements CommandHandler { public final class SetSceneTagCommand implements CommandHandler {
@ -20,7 +20,7 @@ public final class SetSceneTagCommand implements CommandHandler {
@Override @Override
public void execute(Player sender, Player targetPlayer, List<String> args) { public void execute(Player sender, Player targetPlayer, List<String> args) {
if (args.isEmpty()) { if (args.size() == 0) {
sendUsageMessage(sender); sendUsageMessage(sender);
return; return;
} }
@ -39,9 +39,6 @@ public final class SetSceneTagCommand implements CommandHandler {
if (actionStr.equals("unlockall")) { if (actionStr.equals("unlockall")) {
unlockAllSceneTags(targetPlayer); unlockAllSceneTags(targetPlayer);
return; return;
} else if (actionStr.equals("reset") || actionStr.equals("restore")) {
resetAllSceneTags(targetPlayer);
return;
} else { } else {
CommandHandler.sendTranslatedMessage(sender, "commands.execution.argument_error"); CommandHandler.sendTranslatedMessage(sender, "commands.execution.argument_error");
return; return;
@ -52,7 +49,7 @@ public final class SetSceneTagCommand implements CommandHandler {
var sceneData = var sceneData =
sceneTagData.values().stream().filter(sceneTag -> sceneTag.getId() == userVal).findFirst(); sceneTagData.values().stream().filter(sceneTag -> sceneTag.getId() == userVal).findFirst();
if (sceneData.isEmpty()) { if (sceneData == null) {
CommandHandler.sendTranslatedMessage(sender, "commands.generic.invalid.id"); CommandHandler.sendTranslatedMessage(sender, "commands.generic.invalid.id");
return; return;
} }
@ -83,35 +80,20 @@ public final class SetSceneTagCommand implements CommandHandler {
.toList() .toList()
.forEach( .forEach(
sceneTag -> { sceneTag -> {
targetPlayer if (targetPlayer.getSceneTags().get(sceneTag.getSceneId()) == null) {
.getSceneTags() targetPlayer.getSceneTags().put(sceneTag.getSceneId(), new HashSet<>());
.computeIfAbsent(sceneTag.getSceneId(), k -> new HashSet<>()); }
targetPlayer.getSceneTags().get(sceneTag.getSceneId()).add(sceneTag.getId()); targetPlayer.getSceneTags().get(sceneTag.getSceneId()).add(sceneTag.getId());
}); });
// Remove default SceneTags, as most are "before" or "locked" states // Remove default SceneTags, as most are "before" or "locked" states
allData.stream() allData.stream()
.filter(SceneTagData::isDefaultValid) .filter(sceneTag -> sceneTag.isDefaultValid())
// Only remove for big world as some other scenes only have defaults // Only remove for big world as some other scenes only have defaults
.filter(sceneTag -> sceneTag.getSceneId() == 3) .filter(sceneTag -> sceneTag.getSceneId() == 3)
.forEach(
sceneTag ->
targetPlayer.getSceneTags().get(sceneTag.getSceneId()).remove(sceneTag.getId()));
this.setSceneTags(targetPlayer);
}
private void resetAllSceneTags(Player targetPlayer) {
targetPlayer.getSceneTags().clear();
// targetPlayer.applyStartingSceneTags(); // private
GameData.getSceneTagDataMap().values().stream()
.filter(SceneTagData::isDefaultValid)
.forEach( .forEach(
sceneTag -> { sceneTag -> {
targetPlayer targetPlayer.getSceneTags().get(sceneTag.getSceneId()).remove(sceneTag.getId());
.getSceneTags()
.computeIfAbsent(sceneTag.getSceneId(), k -> new HashSet<>());
targetPlayer.getSceneTags().get(sceneTag.getSceneId()).add(sceneTag.getId());
}); });
this.setSceneTags(targetPlayer); this.setSceneTags(targetPlayer);

View File

@ -171,8 +171,8 @@ public final class SetStatsCommand implements CommandHandler {
} }
private static class Stat { private static class Stat {
final String name; String name;
final FightProperty prop; FightProperty prop;
public Stat(FightProperty prop) { public Stat(FightProperty prop) {
this.name = prop.toString(); this.name = prop.toString();

View File

@ -21,9 +21,9 @@ import lombok.Setter;
label = "spawn", label = "spawn",
aliases = {"drop", "s"}, aliases = {"drop", "s"},
usage = { usage = {
"<itemId> [x<amount>] [blk<blockId>] [grp<groupId>] [cfg<configId>] [<x> <y> <z>] [<rotX> <rotY> <rotZ>]", "<itemId> [x<amount>] [blk<blockId>] [grp<groupId>] [cfg<configId>] <x> <y> <z>",
"<gadgetId> [x<amount>] [state<state>] [maxhp<maxhp>] [hp<hp>(0 for infinite)] [atk<atk>] [def<def>] [blk<blockId>] [grp<groupId>] [cfg<configId>] [<x> <y> <z>] [<rotX> <rotY> <rotZ>]", "<gadgetId> [x<amount>] [state<state>] [maxhp<maxhp>] [hp<hp>(0 for infinite)] [atk<atk>] [def<def>] [blk<blockId>] [grp<groupId>] [cfg<configId>] <x> <y> <z>",
"<monsterId> [x<amount>] [lv<level>] [ai<aiId>] [maxhp<maxhp>] [hp<hp>(0 for infinite)] [atk<atk>] [def<def>] [blk<blockId>] [grp<groupId>] [cfg<configId>] [<x> <y> <z>] [<rotX> <rotY> <rotZ>]" "<monsterId> [x<amount>] [lv<level>] [ai<aiId>] [maxhp<maxhp>] [hp<hp>(0 for infinite)] [atk<atk>] [def<def>] [blk<blockId>] [grp<groupId>] [cfg<configId>] <x> <y> <z>"
}, },
permission = "server.spawn", permission = "server.spawn",
permissionTargeted = "server.spawn.others") permissionTargeted = "server.spawn.others")
@ -49,27 +49,18 @@ public final class SpawnCommand implements CommandHandler {
parseIntParameters(args, param, intCommandHandlers); parseIntParameters(args, param, intCommandHandlers);
// At this point, first remaining argument MUST be the id and the rest the pos // At this point, first remaining argument MUST be the id and the rest the pos
if (args.isEmpty()) { if (args.size() < 1) {
sendUsageMessage(sender); // Reachable if someone does `/give lv90` or similar sendUsageMessage(sender); // Reachable if someone does `/give lv90` or similar
throw new IllegalArgumentException(); throw new IllegalArgumentException();
} }
Position pos = new Position(targetPlayer.getPosition());
Position rot = new Position(targetPlayer.getRotation());
switch (args.size()) { switch (args.size()) {
case 7:
try {
rot.setX(CommandHelpers.parseRelative(args.get(4), rot.getX()));
rot.setY(CommandHelpers.parseRelative(args.get(5), rot.getY()));
rot.setZ(CommandHelpers.parseRelative(args.get(6), rot.getZ()));
} catch (NumberFormatException ignored) {
CommandHandler.sendMessage(
sender, translate(sender, "commands.execution.argument_error"));
} // Fallthrough
case 4: case 4:
try { try {
pos = CommandHelpers.parsePosition(args.get(1), args.get(2), args.get(3), pos, rot); float x, y, z;
x = Float.parseFloat(args.get(1));
y = Float.parseFloat(args.get(2));
z = Float.parseFloat(args.get(3));
param.pos = new Position(x, y, z);
} catch (NumberFormatException ignored) { } catch (NumberFormatException ignored) {
CommandHandler.sendMessage( CommandHandler.sendMessage(
sender, translate(sender, "commands.execution.argument_error")); sender, translate(sender, "commands.execution.argument_error"));
@ -86,8 +77,6 @@ public final class SpawnCommand implements CommandHandler {
sendUsageMessage(sender); sendUsageMessage(sender);
return; return;
} }
param.pos = pos;
param.rot = rot;
MonsterData monsterData = GameData.getMonsterDataMap().get(param.id); MonsterData monsterData = GameData.getMonsterDataMap().get(param.id);
GadgetData gadgetData = GameData.getGadgetDataMap().get(param.id); GadgetData gadgetData = GameData.getGadgetDataMap().get(param.id);
@ -113,8 +102,12 @@ public final class SpawnCommand implements CommandHandler {
} }
double maxRadius = Math.sqrt(param.amount * 0.2 / Math.PI); double maxRadius = Math.sqrt(param.amount * 0.2 / Math.PI);
if (param.pos == null) {
param.pos = targetPlayer.getPosition();
}
for (int i = 0; i < param.amount; i++) { for (int i = 0; i < param.amount; i++) {
pos = GetRandomPositionInCircle(param.pos, maxRadius).addY(3); Position pos = GetRandomPositionInCircle(param.pos, maxRadius).addY(3);
GameEntity entity = null; GameEntity entity = null;
if (itemData != null) { if (itemData != null) {
entity = createItem(itemData, param, pos); entity = createItem(itemData, param, pos);
@ -135,12 +128,12 @@ public final class SpawnCommand implements CommandHandler {
} }
private EntityItem createItem(ItemData itemData, SpawnParameters param, Position pos) { private EntityItem createItem(ItemData itemData, SpawnParameters param, Position pos) {
return new EntityItem(param.scene, null, itemData, pos, param.rot, 1, true); return new EntityItem(param.scene, null, itemData, pos, 1, true);
} }
private EntityMonster createMonster( private EntityMonster createMonster(
MonsterData monsterData, SpawnParameters param, Position pos) { MonsterData monsterData, SpawnParameters param, Position pos) {
var entity = new EntityMonster(param.scene, monsterData, pos, param.rot, param.lvl); var entity = new EntityMonster(param.scene, monsterData, pos, param.lvl);
if (param.ai != -1) { if (param.ai != -1) {
entity.setAiId(param.ai); entity.setAiId(param.ai);
} }
@ -151,13 +144,16 @@ public final class SpawnCommand implements CommandHandler {
GadgetData gadgetData, SpawnParameters param, Position pos, Player targetPlayer) { GadgetData gadgetData, SpawnParameters param, Position pos, Player targetPlayer) {
EntityBaseGadget entity; EntityBaseGadget entity;
if (gadgetData.getType() == EntityType.Vehicle) { if (gadgetData.getType() == EntityType.Vehicle) {
entity = new EntityVehicle(param.scene, targetPlayer, param.id, 0, pos, param.rot); entity =
new EntityVehicle(
param.scene, targetPlayer, param.id, 0, pos, targetPlayer.getRotation());
} else { } else {
entity = new EntityGadget(param.scene, param.id, pos, param.rot); entity = new EntityGadget(param.scene, param.id, pos, targetPlayer.getRotation());
if (param.state != -1) { if (param.state != -1) {
((EntityGadget) entity).setState(param.state); ((EntityGadget) entity).setState(param.state);
} }
} }
return entity; return entity;
} }
@ -211,7 +207,6 @@ public final class SpawnCommand implements CommandHandler {
@Setter public int def = -1; @Setter public int def = -1;
@Setter public int ai = -1; @Setter public int ai = -1;
@Setter public Position pos = null; @Setter public Position pos = null;
@Setter public Position rot = null;
public Scene scene = null; public Scene scene = null;
} }
} }

View File

@ -27,7 +27,7 @@ public final class TalentCommand implements CommandHandler {
@Override @Override
public void execute(Player sender, Player targetPlayer, List<String> args) { public void execute(Player sender, Player targetPlayer, List<String> args) {
if (args.isEmpty()) { if (args.size() < 1) {
sendUsageMessage(sender); sendUsageMessage(sender);
return; return;
} }
@ -45,7 +45,9 @@ public final class TalentCommand implements CommandHandler {
String cmdSwitch = args.get(0).toLowerCase(); String cmdSwitch = args.get(0).toLowerCase();
switch (cmdSwitch) { switch (cmdSwitch) {
default -> sendUsageMessage(sender); default -> {
sendUsageMessage(sender);
}
case "set" -> { case "set" -> {
if (args.size() < 3) { if (args.size() < 3) {
sendUsageMessage(sender); sendUsageMessage(sender);

View File

@ -16,10 +16,24 @@ import java.util.List;
permissionTargeted = "player.teleport.others") permissionTargeted = "player.teleport.others")
public final class TeleportCommand implements CommandHandler { public final class TeleportCommand implements CommandHandler {
private float parseRelative(
String input, Float current) { // TODO: Maybe this will be useful elsewhere later
if (input.contains("~")) { // Relative
if (!input.equals("~")) { // Relative with offset
current += Float.parseFloat(input.replace("~", ""));
} // Else no offset, no modification
} else { // Absolute
current = Float.parseFloat(input);
}
return current;
}
@Override @Override
public void execute(Player sender, Player targetPlayer, List<String> args) { public void execute(Player sender, Player targetPlayer, List<String> args) {
Position pos = new Position(targetPlayer.getPosition()); Position pos = targetPlayer.getPosition();
Position rot = new Position(targetPlayer.getRotation()); float x = pos.getX();
float y = pos.getY();
float z = pos.getZ();
int sceneId = targetPlayer.getSceneId(); int sceneId = targetPlayer.getSceneId();
switch (args.size()) { switch (args.size()) {
@ -32,7 +46,9 @@ public final class TeleportCommand implements CommandHandler {
} // Fallthrough } // Fallthrough
case 3: case 3:
try { try {
pos = CommandHelpers.parsePosition(args.get(0), args.get(1), args.get(2), pos, rot); x = this.parseRelative(args.get(0), x);
y = this.parseRelative(args.get(1), y);
z = this.parseRelative(args.get(2), z);
} catch (NumberFormatException ignored) { } catch (NumberFormatException ignored) {
CommandHandler.sendMessage( CommandHandler.sendMessage(
sender, translate(sender, "commands.teleport.invalid_position")); sender, translate(sender, "commands.teleport.invalid_position"));
@ -43,10 +59,11 @@ public final class TeleportCommand implements CommandHandler {
return; return;
} }
Position target_pos = new Position(x, y, z);
boolean result = boolean result =
targetPlayer targetPlayer
.getWorld() .getWorld()
.transferPlayerToScene(targetPlayer, sceneId, TeleportType.COMMAND, pos); .transferPlayerToScene(targetPlayer, sceneId, TeleportType.COMMAND, target_pos);
if (!result) { if (!result) {
CommandHandler.sendMessage(sender, translate(sender, "commands.teleport.exists_error")); CommandHandler.sendMessage(sender, translate(sender, "commands.teleport.exists_error"));
@ -54,13 +71,7 @@ public final class TeleportCommand implements CommandHandler {
CommandHandler.sendMessage( CommandHandler.sendMessage(
sender, sender,
translate( translate(
sender, sender, "commands.teleport.success", targetPlayer.getNickname(), x, y, z, sceneId));
"commands.teleport.success",
targetPlayer.getNickname(),
pos.getX(),
pos.getY(),
pos.getZ(),
sceneId));
} }
} }
} }

View File

@ -131,7 +131,7 @@ public final class TrialAvatarActivityCommand implements CommandHandler {
translate( translate(
sender, "commands.trialAvatarActivity.success_reward", Integer.parseInt(param))); sender, "commands.trialAvatarActivity.success_reward", Integer.parseInt(param)));
} else { } else {
if (!param.equalsIgnoreCase("all")) { if (!param.toLowerCase().equals("all")) {
CommandHandler.sendMessage( CommandHandler.sendMessage(
sender, translate(sender, "commands.trialAvatarActivity.invalid_param")); sender, translate(sender, "commands.trialAvatarActivity.invalid_param"));
return; return;

View File

@ -78,11 +78,11 @@ public class ConfigContainer {
} }
} }
public final Structure folderStructure = new Structure(); public Structure folderStructure = new Structure();
public final Database databaseInfo = new Database(); public Database databaseInfo = new Database();
public final Language language = new Language(); public Language language = new Language();
public final Account account = new Account(); public Account account = new Account();
public final Server server = new Server(); public Server server = new Server();
// DO NOT. TOUCH. THE VERSION NUMBER. // DO NOT. TOUCH. THE VERSION NUMBER.
public int version = version(); public int version = version();
@ -90,99 +90,98 @@ public class ConfigContainer {
/* Option containers. */ /* Option containers. */
public static class Database { public static class Database {
public final DataStore server = new DataStore(); public DataStore server = new DataStore();
public final DataStore game = new DataStore(); public DataStore game = new DataStore();
public static class DataStore { public static class DataStore {
public final String connectionUri = "mongodb://localhost:27017"; public String connectionUri = "mongodb://localhost:27017";
public final String collection = "grasscutter"; public String collection = "grasscutter";
} }
} }
public static class Structure { public static class Structure {
public final String resources = "./resources/"; public String resources = "./resources/";
public final String data = "./data/"; public String data = "./data/";
public final String packets = "./packets/"; public String packets = "./packets/";
public final String scripts = "resources:Scripts/"; public String scripts = "resources:Scripts/";
public final String plugins = "./plugins/"; public String plugins = "./plugins/";
public final String cache = "./cache/"; public String cache = "./cache/";
// UNUSED (potentially added later?) // UNUSED (potentially added later?)
// public String dumps = "./dumps/"; // public String dumps = "./dumps/";
} }
public static class Server { public static class Server {
public final Set<Integer> debugWhitelist = Set.of(); public Set<Integer> debugWhitelist = Set.of();
public final Set<Integer> debugBlacklist = Set.of(); public Set<Integer> debugBlacklist = Set.of();
public final ServerRunMode runMode = ServerRunMode.HYBRID; public ServerRunMode runMode = ServerRunMode.HYBRID;
public final boolean logCommands = false; public boolean logCommands = false;
/** /**
* If enabled, the 'require' Lua function will load the script's compiled varient into the context. (faster; doesn't work as well) * If enabled, the 'require' Lua function will load the script's compiled varient into the context. (faster; doesn't work as well)
* If disabled, all 'require' calls will be replaced with the referenced script's source. (slower; works better) * If disabled, all 'require' calls will be replaced with the referenced script's source. (slower; works better)
*/ */
public final boolean fastRequire = true; public boolean fastRequire = true;
public final HTTP http = new HTTP(); public HTTP http = new HTTP();
public final Game game = new Game(); public Game game = new Game();
public final Dispatch dispatch = new Dispatch(); public Dispatch dispatch = new Dispatch();
public final DebugMode debugMode = new DebugMode(); public DebugMode debugMode = new DebugMode();
} }
public static class Language { public static class Language {
public Locale language = Locale.getDefault(); public Locale language = Locale.getDefault();
public final Locale fallback = Locale.US; public Locale fallback = Locale.US;
public final String document = "EN"; public String document = "EN";
} }
public static class Account { public static class Account {
public final boolean autoCreate = false; public boolean autoCreate = false;
public final boolean EXPERIMENTAL_RealPassword = false; public boolean EXPERIMENTAL_RealPassword = false;
public final String[] defaultPermissions = {}; public String[] defaultPermissions = {};
public final String playerEmail = "grasscutter.io"; public int maxPlayer = -1;
public final int maxPlayer = -1;
} }
/* Server options. */ /* Server options. */
public static class HTTP { public static class HTTP {
/* This starts the HTTP server before the game server. */ /* This starts the HTTP server before the game server. */
public final boolean startImmediately = false; public boolean startImmediately = false;
public final String bindAddress = "0.0.0.0"; public String bindAddress = "0.0.0.0";
public final int bindPort = 443; public int bindPort = 443;
/* This is the address used in URLs. */ /* This is the address used in URLs. */
public final String accessAddress = "127.0.0.1"; public String accessAddress = "127.0.0.1";
/* This is the port used in URLs. */ /* This is the port used in URLs. */
public final int accessPort = 0; public int accessPort = 0;
public final Encryption encryption = new Encryption(); public Encryption encryption = new Encryption();
public final Policies policies = new Policies(); public Policies policies = new Policies();
public final Files files = new Files(); public Files files = new Files();
} }
public static class Game { public static class Game {
public final String bindAddress = "0.0.0.0"; public String bindAddress = "0.0.0.0";
public final int bindPort = 22102; public int bindPort = 22102;
/* This is the address used in the default region. */ /* This is the address used in the default region. */
public final String accessAddress = "127.0.0.1"; public String accessAddress = "127.0.0.1";
/* This is the port used in the default region. */ /* This is the port used in the default region. */
public final int accessPort = 0; public int accessPort = 0;
/* Enabling this will generate a unique packet encryption key for each player. */ /* Enabling this will generate a unique packet encryption key for each player. */
public final boolean useUniquePacketKey = true; public boolean useUniquePacketKey = true;
/* Entities within a certain range will be loaded for the player */ /* Entities within a certain range will be loaded for the player */
public final int loadEntitiesForPlayerRange = 300; public int loadEntitiesForPlayerRange = 300;
/* Start in 'unstable-quests', Lua scripts will be enabled by default. */ /* Start in 'unstable-quests', Lua scripts will be enabled by default. */
public final boolean enableScriptInBigWorld = true; public boolean enableScriptInBigWorld = true;
public boolean enableConsole = true; public boolean enableConsole = true;
/* Kcp internal work interval (milliseconds) */ /* Kcp internal work interval (milliseconds) */
public final int kcpInterval = 20; public int kcpInterval = 20;
/* Controls whether packets should be logged in console or not */ /* Controls whether packets should be logged in console or not */
public ServerDebugMode logPackets = ServerDebugMode.NONE; public ServerDebugMode logPackets = ServerDebugMode.NONE;
/* Show packet payload in console or no (in any case the payload is shown in encrypted view) */ /* Show packet payload in console or no (in any case the payload is shown in encrypted view) */
@ -190,13 +189,13 @@ public class ConfigContainer {
/* Show annoying loop packets or no */ /* Show annoying loop packets or no */
public boolean isShowLoopPackets = false; public boolean isShowLoopPackets = false;
public final boolean cacheSceneEntitiesEveryRun = false; public boolean cacheSceneEntitiesEveryRun = false;
public final GameOptions gameOptions = new GameOptions(); public GameOptions gameOptions = new GameOptions();
public final JoinOptions joinOptions = new JoinOptions(); public JoinOptions joinOptions = new JoinOptions();
public final ConsoleAccount serverAccount = new ConsoleAccount(); public ConsoleAccount serverAccount = new ConsoleAccount();
public final VisionOptions[] visionOptions = new VisionOptions[] { public VisionOptions[] visionOptions = new VisionOptions[] {
new VisionOptions("VISION_LEVEL_NORMAL" , 80 , 20), new VisionOptions("VISION_LEVEL_NORMAL" , 80 , 20),
new VisionOptions("VISION_LEVEL_LITTLE_REMOTE" , 16 , 40), new VisionOptions("VISION_LEVEL_LITTLE_REMOTE" , 16 , 40),
new VisionOptions("VISION_LEVEL_REMOTE" , 1000 , 250), new VisionOptions("VISION_LEVEL_REMOTE" , 1000 , 250),
@ -210,17 +209,17 @@ public class ConfigContainer {
public static class Dispatch { public static class Dispatch {
/* An array of servers. */ /* An array of servers. */
public final List<Region> regions = List.of(); public List<Region> regions = List.of();
/* The URL used to make HTTP requests to the dispatch server. */ /* The URL used to make HTTP requests to the dispatch server. */
public final String dispatchUrl = "ws://127.0.0.1:1111"; public String dispatchUrl = "ws://127.0.0.1:1111";
/* A unique key used for encryption. */ /* A unique key used for encryption. */
public final byte[] encryptionKey = Crypto.createSessionKey(32); public byte[] encryptionKey = Crypto.createSessionKey(32);
/* A unique key used for authentication. */ /* A unique key used for authentication. */
public final String dispatchKey = Utils.base64Encode( public String dispatchKey = Utils.base64Encode(
Crypto.createSessionKey(32)); Crypto.createSessionKey(32));
public final String defaultName = "Grasscutter"; public String defaultName = "Grasscutter";
/* Controls whether http requests should be logged in console or not */ /* Controls whether http requests should be logged in console or not */
public ServerDebugMode logRequests = ServerDebugMode.NONE; public ServerDebugMode logRequests = ServerDebugMode.NONE;
@ -230,127 +229,127 @@ public class ConfigContainer {
* (see StartupArguments.enableDebug) */ * (see StartupArguments.enableDebug) */
public static class DebugMode { public static class DebugMode {
/* Log level of the main server code (works only with -debug arg) */ /* Log level of the main server code (works only with -debug arg) */
public final Level serverLoggerLevel = Level.DEBUG; public Level serverLoggerLevel = Level.DEBUG;
/* Log level of the third-party services (works only with -debug arg): /* Log level of the third-party services (works only with -debug arg):
javalin, quartz, reflections, jetty, mongodb.driver */ javalin, quartz, reflections, jetty, mongodb.driver */
public final Level servicesLoggersLevel = Level.INFO; public Level servicesLoggersLevel = Level.INFO;
/* Controls whether packets should be logged in console or not */ /* Controls whether packets should be logged in console or not */
public final ServerDebugMode logPackets = ServerDebugMode.ALL; public ServerDebugMode logPackets = ServerDebugMode.ALL;
/* Show packet payload in console or no (in any case the payload is shown in encrypted view) */ /* Show packet payload in console or no (in any case the payload is shown in encrypted view) */
public final boolean isShowPacketPayload = false; public boolean isShowPacketPayload = false;
/* Show annoying loop packets or no */ /* Show annoying loop packets or no */
public final boolean isShowLoopPackets = false; public boolean isShowLoopPackets = false;
/* Controls whether http requests should be logged in console or not */ /* Controls whether http requests should be logged in console or not */
public final ServerDebugMode logRequests = ServerDebugMode.ALL; public ServerDebugMode logRequests = ServerDebugMode.ALL;
} }
public static class Encryption { public static class Encryption {
public boolean useEncryption = true; public boolean useEncryption = true;
/* Should 'https' be appended to URLs? */ /* Should 'https' be appended to URLs? */
public boolean useInRouting = true; public boolean useInRouting = true;
public final String keystore = "./keystore.p12"; public String keystore = "./keystore.p12";
public final String keystorePassword = "123456"; public String keystorePassword = "123456";
} }
public static class Policies { public static class Policies {
public final Policies.CORS cors = new Policies.CORS(); public Policies.CORS cors = new Policies.CORS();
public static class CORS { public static class CORS {
public final boolean enabled = true; public boolean enabled = true;
public final String[] allowedOrigins = new String[]{"*"}; public String[] allowedOrigins = new String[]{"*"};
} }
} }
public static class GameOptions { public static class GameOptions {
public final InventoryLimits inventoryLimits = new InventoryLimits(); public InventoryLimits inventoryLimits = new InventoryLimits();
public final AvatarLimits avatarLimits = new AvatarLimits(); public AvatarLimits avatarLimits = new AvatarLimits();
public final int sceneEntityLimit = 1000; // Unenforced. TODO: Implement. public int sceneEntityLimit = 1000; // Unenforced. TODO: Implement.
public final boolean watchGachaConfig = false; public boolean watchGachaConfig = false;
public final boolean enableShopItems = true; public boolean enableShopItems = true;
public final boolean staminaUsage = true; public boolean staminaUsage = true;
public final boolean energyUsage = true; public boolean energyUsage = true;
public final boolean fishhookTeleport = true; public boolean fishhookTeleport = true;
public final boolean trialCostumes = false; public boolean trialCostumes = false;
@SerializedName(value = "questing", alternate = "questOptions") @SerializedName(value = "questing", alternate = "questOptions")
public final Questing questing = new Questing(); public Questing questing = new Questing();
public final ResinOptions resinOptions = new ResinOptions(); public ResinOptions resinOptions = new ResinOptions();
public final Rates rates = new Rates(); public Rates rates = new Rates();
public final HandbookOptions handbook = new HandbookOptions(); public HandbookOptions handbook = new HandbookOptions();
public static class InventoryLimits { public static class InventoryLimits {
public final int weapons = 2000; public int weapons = 2000;
public final int relics = 2000; public int relics = 2000;
public final int materials = 2000; public int materials = 2000;
public final int furniture = 2000; public int furniture = 2000;
public final int all = 30000; public int all = 30000;
} }
public static class AvatarLimits { public static class AvatarLimits {
public final int singlePlayerTeam = 4; public int singlePlayerTeam = 4;
public final int multiplayerTeam = 4; public int multiplayerTeam = 4;
} }
public static class Rates { public static class Rates {
public final float adventureExp = 1.0f; public float adventureExp = 1.0f;
public float mora = 1.0f; public float mora = 1.0f;
public float leyLines = 1.0f; public float leyLines = 1.0f;
} }
public static class ResinOptions { public static class ResinOptions {
public final boolean resinUsage = false; public boolean resinUsage = false;
public final int cap = 160; public int cap = 160;
public final int rechargeTime = 480; public int rechargeTime = 480;
} }
public static class Questing { public static class Questing {
/* Should questing behavior be used? */ /* Should questing behavior be used? */
public final boolean enabled = true; public boolean enabled = true;
} }
public static class HandbookOptions { public static class HandbookOptions {
public final boolean enable = false; public boolean enable = false;
public final boolean allowCommands = true; public boolean allowCommands = true;
public final Limits limits = new Limits(); public Limits limits = new Limits();
public final Server server = new Server(); public Server server = new Server();
public static class Limits { public static class Limits {
/* Are rate limits checked? */ /* Are rate limits checked? */
public final boolean enabled = false; public boolean enabled = false;
/* The time for limits to expire. */ /* The time for limits to expire. */
public final int interval = 3; public int interval = 3;
/* The maximum amount of normal requests. */ /* The maximum amount of normal requests. */
public final int maxRequests = 10; public int maxRequests = 10;
/* The maximum amount of entities to be spawned in one request. */ /* The maximum amount of entities to be spawned in one request. */
public final int maxEntities = 25; public int maxEntities = 25;
} }
public static class Server { public static class Server {
/* Are the server settings sent to the handbook? */ /* Are the server settings sent to the handbook? */
public final boolean enforced = false; public boolean enforced = false;
/* The default server address for the handbook's authentication. */ /* The default server address for the handbook's authentication. */
public final String address = "127.0.0.1"; public String address = "127.0.0.1";
/* The default server port for the handbook's authentication. */ /* The default server port for the handbook's authentication. */
public final int port = 443; public int port = 443;
/* Should the defaults be enforced? */ /* Should the defaults be enforced? */
public final boolean canChange = true; public boolean canChange = true;
} }
} }
} }
public static class VisionOptions { public static class VisionOptions {
public final String name; public String name;
public final int visionRange; public int visionRange;
public final int gridWidth; public int gridWidth;
public VisionOptions(String name, int visionRange, int gridWidth) { public VisionOptions(String name, int visionRange, int gridWidth) {
this.name = name; this.name = name;
@ -360,21 +359,21 @@ public class ConfigContainer {
} }
public static class JoinOptions { public static class JoinOptions {
public final int[] welcomeEmotes = {2007, 1002, 4010}; public int[] welcomeEmotes = {2007, 1002, 4010};
public final String welcomeMessage = "Welcome to a Grasscutter server."; public String welcomeMessage = "Welcome to a Grasscutter server.";
public final JoinOptions.Mail welcomeMail = new JoinOptions.Mail(); public JoinOptions.Mail welcomeMail = new JoinOptions.Mail();
public static class Mail { public static class Mail {
public final String title = "Welcome to Grasscutter!"; public String title = "Welcome to Grasscutter!";
public final String content = """ public String content = """
Hi there!\r Hi there!\r
First of all, welcome to Grasscutter. If you have any issues, please let us know so that Lawnmower can help you! \r First of all, welcome to Grasscutter. If you have any issues, please let us know so that Lawnmower can help you! \r
\r \r
Check out our:\r Check out our:\r
<type="browser" text="Discord" href="https://discord.gg/T5vZU6UyeG"/> <type="browser" text="Discord" href="https://discord.gg/T5vZU6UyeG"/>
"""; """;
public final String sender = "Lawnmower"; public String sender = "Lawnmower";
public final emu.grasscutter.game.mail.Mail.MailItem[] items = { public emu.grasscutter.game.mail.Mail.MailItem[] items = {
new emu.grasscutter.game.mail.Mail.MailItem(13509, 1, 1), new emu.grasscutter.game.mail.Mail.MailItem(13509, 1, 1),
new emu.grasscutter.game.mail.Mail.MailItem(201, 99999, 1) new emu.grasscutter.game.mail.Mail.MailItem(201, 99999, 1)
}; };
@ -382,18 +381,18 @@ public class ConfigContainer {
} }
public static class ConsoleAccount { public static class ConsoleAccount {
public final int avatarId = 10000007; public int avatarId = 10000007;
public final int nameCardId = 210001; public int nameCardId = 210001;
public final int adventureRank = 1; public int adventureRank = 1;
public final int worldLevel = 0; public int worldLevel = 0;
public final String nickName = "Server"; public String nickName = "Server";
public final String signature = "Welcome to Grasscutter!"; public String signature = "Welcome to Grasscutter!";
} }
public static class Files { public static class Files {
public final String indexFile = "./index.html"; public String indexFile = "./index.html";
public final String errorFile = "./404.html"; public String errorFile = "./404.html";
} }
/* Objects. */ /* Objects. */

View File

@ -1,17 +1,12 @@
package emu.grasscutter.data; package emu.grasscutter.data;
import emu.grasscutter.Grasscutter; import emu.grasscutter.Grasscutter;
import emu.grasscutter.utils.FileUtils; import emu.grasscutter.server.http.handlers.GachaHandler;
import emu.grasscutter.utils.JsonUtils; import emu.grasscutter.tools.Tools;
import emu.grasscutter.utils.TsvUtils; import emu.grasscutter.utils.*;
import java.io.FileNotFoundException; import java.io.*;
import java.io.IOException; import java.nio.file.*;
import java.io.InputStream; import java.util.*;
import java.io.InputStreamReader;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.List;
import java.util.Map;
import lombok.val; import lombok.val;
public class DataLoader { public class DataLoader {
@ -22,7 +17,7 @@ public class DataLoader {
* *
* @param resourcePath The path to the data file to be loaded. * @param resourcePath The path to the data file to be loaded.
* @return InputStream of the data file. * @return InputStream of the data file.
* @throws FileNotFoundException If the file is not found. * @throws FileNotFoundException
* @see #load(String, boolean) * @see #load(String, boolean)
*/ */
public static InputStream load(String resourcePath) throws FileNotFoundException { public static InputStream load(String resourcePath) throws FileNotFoundException {
@ -35,14 +30,18 @@ public class DataLoader {
* *
* @param resourcePath The path to the data file to be loaded. * @param resourcePath The path to the data file to be loaded.
* @return InputStreamReader of the data file. * @return InputStreamReader of the data file.
* @throws IOException If the file is not found. * @throws IOException
* @throws FileNotFoundException If the file is not found. * @throws FileNotFoundException
* @see #load(String, boolean) * @see #load(String, boolean)
*/ */
public static InputStreamReader loadReader(String resourcePath) throws IOException { public static InputStreamReader loadReader(String resourcePath)
throws IOException, FileNotFoundException {
try {
InputStream is = load(resourcePath, true); InputStream is = load(resourcePath, true);
if (is == null) throw new FileNotFoundException("File not found: " + resourcePath);
return new InputStreamReader(is); return new InputStreamReader(is);
} catch (FileNotFoundException exception) {
throw exception;
}
} }
/** /**
@ -52,7 +51,7 @@ public class DataLoader {
* @param useFallback If the file does not exist in the /data directory, should it use the default * @param useFallback If the file does not exist in the /data directory, should it use the default
* file in the jar? * file in the jar?
* @return InputStream of the data file. * @return InputStream of the data file.
* @throws FileNotFoundException If the file is not found. * @throws FileNotFoundException
*/ */
public static InputStream load(String resourcePath, boolean useFallback) public static InputStream load(String resourcePath, boolean useFallback)
throws FileNotFoundException { throws FileNotFoundException {
@ -92,7 +91,7 @@ public class DataLoader {
public static <T> List<T> loadTableToList(String resourcePath, Class<T> classType) public static <T> List<T> loadTableToList(String resourcePath, Class<T> classType)
throws IOException { throws IOException {
val path = FileUtils.getDataPathTsjJsonTsv(resourcePath); val path = FileUtils.getDataPathTsjJsonTsv(resourcePath);
Grasscutter.getLogger().trace("Loading data table from: {}", path); Grasscutter.getLogger().trace("Loading data table from: " + path);
return switch (FileUtils.getFileExtension(path)) { return switch (FileUtils.getFileExtension(path)) {
case "json" -> JsonUtils.loadToList(path, classType); case "json" -> JsonUtils.loadToList(path, classType);
case "tsj" -> TsvUtils.loadTsjToListSetField(path, classType); case "tsj" -> TsvUtils.loadTsjToListSetField(path, classType);
@ -115,6 +114,8 @@ public class DataLoader {
} catch (Exception e) { } catch (Exception e) {
Grasscutter.getLogger().error("An error occurred while trying to check the data folder.", e); Grasscutter.getLogger().error("An error occurred while trying to check the data folder.", e);
} }
generateGachaMappings();
} }
private static void checkAndCopyData(String name) { private static void checkAndCopyData(String name) {
@ -123,10 +124,23 @@ public class DataLoader {
if (!Files.exists(filePath)) { if (!Files.exists(filePath)) {
var root = filePath.getParent(); var root = filePath.getParent();
if (root.toFile().mkdirs()) Grasscutter.getLogger().info("Created data folder '{}'", root); if (root.toFile().mkdirs())
Grasscutter.getLogger().info("Created data folder '" + root + "'");
Grasscutter.getLogger().debug("Creating default '{}' data", name); Grasscutter.getLogger().debug("Creating default '" + name + "' data");
FileUtils.copyResource("/defaults/data/" + name, filePath.toString()); FileUtils.copyResource("/defaults/data/" + name, filePath.toString());
} }
} }
private static void generateGachaMappings() {
var path = GachaHandler.getGachaMappingsPath();
if (!Files.exists(path)) {
try {
Grasscutter.getLogger().debug("Creating default '" + path + "' data");
Tools.createGachaMappings(path);
} catch (Exception exception) {
Grasscutter.getLogger().warn("Failed to create gacha mappings. \n" + exception);
}
}
}
} }

View File

@ -214,14 +214,6 @@ public final class GameData {
private static final Int2ObjectMap<CookRecipeData> cookRecipeDataMap = private static final Int2ObjectMap<CookRecipeData> cookRecipeDataMap =
new Int2ObjectOpenHashMap<>(); new Int2ObjectOpenHashMap<>();
@Getter
private static final Int2ObjectMap<CoopChapterData> coopChapterDataMap =
new Int2ObjectOpenHashMap<>();
@Getter
private static final Int2ObjectMap<CoopPointData> coopPointDataMap =
new Int2ObjectOpenHashMap<>();
@Getter @Getter
private static final Int2ObjectMap<CompoundData> compoundDataMap = new Int2ObjectOpenHashMap<>(); private static final Int2ObjectMap<CompoundData> compoundDataMap = new Int2ObjectOpenHashMap<>();
@ -302,10 +294,6 @@ public final class GameData {
private static final Int2ObjectMap<HomeWorldLevelData> homeWorldLevelDataMap = private static final Int2ObjectMap<HomeWorldLevelData> homeWorldLevelDataMap =
new Int2ObjectOpenHashMap<>(); new Int2ObjectOpenHashMap<>();
@Getter
private static final Int2ObjectMap<HomeWorldModuleData> homeWorldModuleDataMap =
new Int2ObjectOpenHashMap<>();
@Getter @Getter
private static final Int2ObjectMap<HomeWorldNPCData> homeWorldNPCDataMap = private static final Int2ObjectMap<HomeWorldNPCData> homeWorldNPCDataMap =
new Int2ObjectOpenHashMap<>(); new Int2ObjectOpenHashMap<>();
@ -569,8 +557,8 @@ public final class GameData {
private static final Int2IntMap trialAvatarIndexIdTrialActivityDataDataMap = private static final Int2IntMap trialAvatarIndexIdTrialActivityDataDataMap =
new Int2IntOpenHashMap(); new Int2IntOpenHashMap();
private static final Map<Integer, List<Integer>> fetters = new HashMap<>(); private static Map<Integer, List<Integer>> fetters = new HashMap<>();
private static final Map<Integer, List<ShopGoodsData>> shopGoods = new HashMap<>(); private static Map<Integer, List<ShopGoodsData>> shopGoods = new HashMap<>();
// Getters with different names that stay for now // Getters with different names that stay for now
public static Int2ObjectMap<MainQuestData> getMainQuestDataMap() { public static Int2ObjectMap<MainQuestData> getMainQuestDataMap() {
@ -630,21 +618,15 @@ public final class GameData {
// Non-nullable value getters // Non-nullable value getters
public static int getAvatarLevelExpRequired(int level) { public static int getAvatarLevelExpRequired(int level) {
return Optional.ofNullable(avatarLevelDataMap.get(level)) return Optional.ofNullable(avatarLevelDataMap.get(level)).map(d -> d.getExp()).orElse(0);
.map(AvatarLevelData::getExp)
.orElse(0);
} }
public static int getAvatarFetterLevelExpRequired(int level) { public static int getAvatarFetterLevelExpRequired(int level) {
return Optional.ofNullable(avatarFetterLevelDataMap.get(level)) return Optional.ofNullable(avatarFetterLevelDataMap.get(level)).map(d -> d.getExp()).orElse(0);
.map(AvatarFetterLevelData::getExp)
.orElse(0);
} }
public static int getRelicExpRequired(int rankLevel, int level) { public static int getRelicExpRequired(int rankLevel, int level) {
return Optional.ofNullable(getRelicLevelData(rankLevel, level)) return Optional.ofNullable(getRelicLevelData(rankLevel, level)).map(d -> d.getExp()).orElse(0);
.map(ReliquaryLevelData::getExp)
.orElse(0);
} }
// Generic getter // Generic getter
@ -661,7 +643,7 @@ public final class GameData {
field.setAccessible(false); field.setAccessible(false);
} catch (Exception e) { } catch (Exception e) {
Grasscutter.getLogger() Grasscutter.getLogger()
.error("Error fetching resource map for {}", resourceDefinition.getSimpleName(), e); .error("Error fetching resource map for " + resourceDefinition.getSimpleName(), e);
} }
return map; return map;
@ -719,8 +701,8 @@ public final class GameData {
/** /**
* Fetches the trial data * Fetches the trial data
* *
* @param trialAvatarIndexId The ID of the trial avatar * @param trialAvatarIndexId
* @return The trial data for the trial avatar * @return
*/ */
@Nullable public static TrialAvatarActivityDataData getTrialAvatarActivityDataByAvatarIndex( @Nullable public static TrialAvatarActivityDataData getTrialAvatarActivityDataByAvatarIndex(
int trialAvatarIndexId) { int trialAvatarIndexId) {

View File

@ -13,17 +13,17 @@ import lombok.*;
public class GameDepot { public class GameDepot {
public static final int[] BLOCK_SIZE = new int[] {50, 500}; // Scales public static final int[] BLOCK_SIZE = new int[] {50, 500}; // Scales
private static final Int2ObjectMap<WeightedList<ReliquaryMainPropData>> relicRandomMainPropDepot = private static Int2ObjectMap<WeightedList<ReliquaryMainPropData>> relicRandomMainPropDepot =
new Int2ObjectOpenHashMap<>(); new Int2ObjectOpenHashMap<>();
private static final Int2ObjectMap<List<ReliquaryMainPropData>> relicMainPropDepot = private static Int2ObjectMap<List<ReliquaryMainPropData>> relicMainPropDepot =
new Int2ObjectOpenHashMap<>(); new Int2ObjectOpenHashMap<>();
private static final Int2ObjectMap<List<ReliquaryAffixData>> relicAffixDepot = private static Int2ObjectMap<List<ReliquaryAffixData>> relicAffixDepot =
new Int2ObjectOpenHashMap<>(); new Int2ObjectOpenHashMap<>();
@Getter @Setter private static Map<String, AvatarConfig> playerAbilities = new HashMap<>(); @Getter @Setter private static Map<String, AvatarConfig> playerAbilities = new HashMap<>();
@Getter @Getter
private static final HashMap<SpawnDataEntry.GridBlockId, ArrayList<SpawnDataEntry>> spawnLists = private static HashMap<SpawnDataEntry.GridBlockId, ArrayList<SpawnDataEntry>> spawnLists =
new HashMap<>(); new HashMap<>();
@Getter @Setter private static BlossomConfig blossomConfig; @Getter @Setter private static BlossomConfig blossomConfig;
@ -50,7 +50,7 @@ public class GameDepot {
list.add(data); list.add(data);
} }
// Let the server owner know if theyre missing weights // Let the server owner know if theyre missing weights
if (relicMainPropDepot.isEmpty() || relicAffixDepot.isEmpty()) { if (relicMainPropDepot.size() == 0 || relicAffixDepot.size() == 0) {
Grasscutter.getLogger() Grasscutter.getLogger()
.error( .error(
"Relic properties are missing weights! Please check your ReliquaryMainPropExcelConfigData or ReliquaryAffixExcelConfigData files in your ExcelBinOutput folder."); "Relic properties are missing weights! Please check your ReliquaryMainPropExcelConfigData or ReliquaryAffixExcelConfigData files in your ExcelBinOutput folder.");

View File

@ -63,7 +63,7 @@ public final class ResourceLoader {
private static List<Set<Class<?>>> getResourceDefClassesPrioritySets() { private static List<Set<Class<?>>> getResourceDefClassesPrioritySets() {
val classes = Grasscutter.reflector.getSubTypesOf(GameResource.class); val classes = Grasscutter.reflector.getSubTypesOf(GameResource.class);
val priorities = ResourceType.LoadPriority.getInOrder(); val priorities = ResourceType.LoadPriority.getInOrder();
Grasscutter.getLogger().debug("Priorities are {}", priorities); Grasscutter.getLogger().debug("Priorities are " + priorities);
val map = new LinkedHashMap<ResourceType.LoadPriority, Set<Class<?>>>(priorities.size()); val map = new LinkedHashMap<ResourceType.LoadPriority, Set<Class<?>>>(priorities.size());
priorities.forEach(p -> map.put(p, new HashSet<>())); priorities.forEach(p -> map.put(p, new HashSet<>()));
@ -140,7 +140,7 @@ public final class ResourceLoader {
getResourceDefClassesPrioritySets() getResourceDefClassesPrioritySets()
.forEach( .forEach(
classes -> classes -> {
classes.stream() classes.stream()
.parallel() .parallel()
.unordered() .unordered()
@ -157,18 +157,19 @@ public final class ResourceLoader {
} catch (Exception e) { } catch (Exception e) {
errors.add(Pair.of(Arrays.toString(type.name()), e)); errors.add(Pair.of(Arrays.toString(type.name()), e));
} }
})); });
});
errors.forEach( errors.forEach(
pair -> pair ->
Grasscutter.getLogger() Grasscutter.getLogger()
.error("Error loading resource file: {}", pair.left(), pair.right())); .error("Error loading resource file: " + pair.left(), pair.right()));
long endTime = System.nanoTime(); long endTime = System.nanoTime();
long ns = (endTime - startTime); // divide by 1000000 to get milliseconds. long ns = (endTime - startTime); // divide by 1000000 to get milliseconds.
Grasscutter.getLogger().debug("Loading resources took {}ns == {}ms", ns, ns / 1000000); Grasscutter.getLogger().debug("Loading resources took " + ns + "ns == " + ns / 1000000 + "ms");
} }
@SuppressWarnings("rawtypes") @SuppressWarnings("rawtypes")
private static void loadFromResource( protected static void loadFromResource(
Class<?> c, ResourceType type, Int2ObjectMap map, boolean doReload) throws Exception { Class<?> c, ResourceType type, Int2ObjectMap map, boolean doReload) throws Exception {
val simpleName = c.getSimpleName(); val simpleName = c.getSimpleName();
if (doReload || !loadedResources.contains(simpleName)) { if (doReload || !loadedResources.contains(simpleName)) {
@ -180,7 +181,7 @@ public final class ResourceLoader {
} }
@SuppressWarnings({"rawtypes", "unchecked"}) @SuppressWarnings({"rawtypes", "unchecked"})
private static <T> void loadFromResource(Class<T> c, Path filename, Int2ObjectMap map) protected static <T> void loadFromResource(Class<T> c, Path filename, Int2ObjectMap map)
throws Exception { throws Exception {
val results = val results =
switch (FileUtils.getFileExtension(filename)) { switch (FileUtils.getFileExtension(filename)) {
@ -199,7 +200,7 @@ public final class ResourceLoader {
} }
@SuppressWarnings({"rawtypes", "unchecked"}) @SuppressWarnings({"rawtypes", "unchecked"})
private static <T> void loadFromResource(Class<T> c, String fileName, Int2ObjectMap map) protected static <T> void loadFromResource(Class<T> c, String fileName, Int2ObjectMap map)
throws Exception { throws Exception {
JsonUtils.loadToList(getResourcePath("ExcelBinOutput/" + fileName), c) JsonUtils.loadToList(getResourcePath("ExcelBinOutput/" + fileName), c)
.forEach( .forEach(
@ -282,7 +283,7 @@ public final class ResourceLoader {
} }
}); });
Grasscutter.getLogger() Grasscutter.getLogger()
.debug("Loaded {} SceneRouteDatas.", GameData.getSceneNpcBornData().size()); .debug("Loaded " + GameData.getSceneNpcBornData().size() + " SceneRouteDatas.");
} catch (IOException e) { } catch (IOException e) {
Grasscutter.getLogger().error("Failed to load SceneRouteData folder."); Grasscutter.getLogger().error("Failed to load SceneRouteData folder.");
} }
@ -403,7 +404,7 @@ public final class ResourceLoader {
.forEach(data -> loadAbilityData(data.Default)); .forEach(data -> loadAbilityData(data.Default));
} catch (IOException e) { } catch (IOException e) {
Grasscutter.getLogger() Grasscutter.getLogger()
.error("Error loading ability modifiers from path {}: ", path.toString(), e); .error("Error loading ability modifiers from path " + path.toString() + ": ", e);
} }
} }
@ -412,7 +413,7 @@ public final class ResourceLoader {
GameData.getAbilityHashes().put(Utils.abilityHash(data.abilityName), data.abilityName); GameData.getAbilityHashes().put(Utils.abilityHash(data.abilityName), data.abilityName);
var modifiers = data.modifiers; var modifiers = data.modifiers;
if (modifiers == null || modifiers.isEmpty()) return; if (modifiers == null || modifiers.size() == 0) return;
var name = data.abilityName; var name = data.abilityName;
var modifierEntry = new AbilityModifierEntry(name); var modifierEntry = new AbilityModifierEntry(name);
@ -465,7 +466,7 @@ public final class ResourceLoader {
path, String.class, new TypeToken<List<TalentData>>() {}.getType())); path, String.class, new TypeToken<List<TalentData>>() {}.getType()));
} catch (IOException e) { } catch (IOException e) {
Grasscutter.getLogger() Grasscutter.getLogger()
.error("Error loading ability modifiers from path {}: ", path.toString(), e); .error("Error loading ability modifiers from path " + path.toString() + ": ", e);
} }
} }
@ -532,7 +533,7 @@ public final class ResourceLoader {
}); });
} catch (IOException e) { } catch (IOException e) {
Grasscutter.getLogger() Grasscutter.getLogger()
.error("Error loading open config: no files found in {}", folderName); .error("Error loading open config: no files found in " + folderName);
return; return;
} }
} }
@ -588,7 +589,7 @@ public final class ResourceLoader {
} }
Grasscutter.getLogger() Grasscutter.getLogger()
.debug("Loaded {} MainQuestDatas.", GameData.getMainQuestDataMap().size()); .debug("Loaded " + GameData.getMainQuestDataMap().size() + " MainQuestDatas.");
} }
public static void loadScriptSceneData() { public static void loadScriptSceneData() {
@ -606,7 +607,7 @@ public final class ResourceLoader {
} }
}); });
Grasscutter.getLogger() Grasscutter.getLogger()
.debug("Loaded {} ScriptSceneDatas.", GameData.getScriptSceneDataMap().size()); .debug("Loaded " + GameData.getScriptSceneDataMap().size() + " ScriptSceneDatas.");
} catch (IOException e) { } catch (IOException e) {
Grasscutter.getLogger().debug("ScriptSceneData folder missing or empty."); Grasscutter.getLogger().debug("ScriptSceneData folder missing or empty.");
} }
@ -631,8 +632,9 @@ public final class ResourceLoader {
}); });
Grasscutter.getLogger() Grasscutter.getLogger()
.debug( .debug(
"Loaded {} HomeworldDefaultSaveDatas.", "Loaded "
GameData.getHomeworldDefaultSaveData().size()); + GameData.getHomeworldDefaultSaveData().size()
+ " HomeworldDefaultSaveDatas.");
} catch (IOException e) { } catch (IOException e) {
Grasscutter.getLogger().error("Failed to load HomeworldDefaultSave folder."); Grasscutter.getLogger().error("Failed to load HomeworldDefaultSave folder.");
} }
@ -645,7 +647,7 @@ public final class ResourceLoader {
path -> { path -> {
try { try {
val data = JsonUtils.loadToClass(path, SceneNpcBornData.class); val data = JsonUtils.loadToClass(path, SceneNpcBornData.class);
if (data.getBornPosList() == null || data.getBornPosList().isEmpty()) { if (data.getBornPosList() == null || data.getBornPosList().size() == 0) {
return; return;
} }
@ -657,7 +659,7 @@ public final class ResourceLoader {
} }
}); });
Grasscutter.getLogger() Grasscutter.getLogger()
.debug("Loaded {} SceneNpcBornDatas.", GameData.getSceneNpcBornData().size()); .debug("Loaded " + GameData.getSceneNpcBornData().size() + " SceneNpcBornDatas.");
} catch (IOException e) { } catch (IOException e) {
Grasscutter.getLogger().error("Failed to load SceneNpcBorn folder."); Grasscutter.getLogger().error("Failed to load SceneNpcBorn folder.");
} }
@ -835,6 +837,7 @@ public final class ResourceLoader {
try { try {
JsonUtils.loadToList(getResourcePath("Server/SubfieldMapping.json"), SubfieldMapping.class) JsonUtils.loadToList(getResourcePath("Server/SubfieldMapping.json"), SubfieldMapping.class)
.forEach(entry -> subfieldMap.put(entry.getEntityId(), entry)); .forEach(entry -> subfieldMap.put(entry.getEntityId(), entry));
;
} catch (IOException | NullPointerException ignored) { } catch (IOException | NullPointerException ignored) {
} }
Grasscutter.getLogger().debug("Loaded {} subfield mappings.", subfieldMap.size()); Grasscutter.getLogger().debug("Loaded {} subfield mappings.", subfieldMap.size());
@ -848,6 +851,7 @@ public final class ResourceLoader {
JsonUtils.loadToList( JsonUtils.loadToList(
getResourcePath("Server/DropSubfieldMapping.json"), DropSubfieldMapping.class) getResourcePath("Server/DropSubfieldMapping.json"), DropSubfieldMapping.class)
.forEach(entry -> dropSubfieldMap.put(entry.getDropId(), entry)); .forEach(entry -> dropSubfieldMap.put(entry.getDropId(), entry));
;
} catch (IOException | NullPointerException ignored) { } catch (IOException | NullPointerException ignored) {
} }
Grasscutter.getLogger().debug("Loaded {} drop subfield mappings.", dropSubfieldMap.size()); Grasscutter.getLogger().debug("Loaded {} drop subfield mappings.", dropSubfieldMap.size());
@ -862,6 +866,7 @@ public final class ResourceLoader {
getResourcePath("Server/DropTableExcelConfigData.json"), getResourcePath("Server/DropTableExcelConfigData.json"),
DropTableExcelConfigData.class) DropTableExcelConfigData.class)
.forEach(entry -> dropTableExcelConfigDataMap.put(entry.getId(), entry)); .forEach(entry -> dropTableExcelConfigDataMap.put(entry.getId(), entry));
;
} catch (IOException | NullPointerException ignored) { } catch (IOException | NullPointerException ignored) {
} }
Grasscutter.getLogger() Grasscutter.getLogger()

View File

@ -1,8 +1,5 @@
package emu.grasscutter.data.binout; package emu.grasscutter.data.binout;
import lombok.Getter;
@Getter
public class AbilityEmbryoEntry { public class AbilityEmbryoEntry {
private String name; private String name;
private String[] abilities; private String[] abilities;
@ -13,4 +10,12 @@ public class AbilityEmbryoEntry {
this.name = avatarName; this.name = avatarName;
this.abilities = array; this.abilities = array;
} }
public String getName() {
return name;
}
public String[] getAbilities() {
return abilities;
}
} }

View File

@ -3,18 +3,17 @@ package emu.grasscutter.data.binout;
import com.google.gson.*; import com.google.gson.*;
import com.google.gson.annotations.SerializedName; import com.google.gson.annotations.SerializedName;
import com.google.gson.reflect.TypeToken; import com.google.gson.reflect.TypeToken;
import java.io.Serial;
import java.io.Serializable; import java.io.Serializable;
import java.util.*; import java.util.*;
public class AbilityMixinData implements Serializable { public class AbilityMixinData implements Serializable {
@Serial private static final long serialVersionUID = -2001232313615923575L; private static final long serialVersionUID = -2001232313615923575L;
public enum Type { public enum Type {
AttachToGadgetStateMixin, AttachToGadgetStateMixin,
AttachToStateIDMixin, AttachToStateIDMixin,
ShieldBarMixin, ShieldBarMixin,
TileAttackManagerMixin TileAttackManagerMixin;
} }
@SerializedName("$type") @SerializedName("$type")
@ -28,7 +27,7 @@ public class AbilityMixinData implements Serializable {
List<String> list = (new Gson()).fromJson(modifierName, listType); List<String> list = (new Gson()).fromJson(modifierName, listType);
return list; return list;
} else { } else {
return Collections.singletonList(modifierName.getAsString()); return Arrays.asList(modifierName.getAsString());
} }
} }
} }

View File

@ -3,12 +3,11 @@ package emu.grasscutter.data.binout;
import com.google.gson.annotations.SerializedName; import com.google.gson.annotations.SerializedName;
import emu.grasscutter.data.common.DynamicFloat; import emu.grasscutter.data.common.DynamicFloat;
import emu.grasscutter.game.props.ElementType; import emu.grasscutter.game.props.ElementType;
import java.io.Serial;
import java.io.Serializable; import java.io.Serializable;
import lombok.ToString; import lombok.ToString;
public class AbilityModifier implements Serializable { public class AbilityModifier implements Serializable {
@Serial private static final long serialVersionUID = -2001232313615923575L; private static final long serialVersionUID = -2001232313615923575L;
public State state; public State state;
@ -43,7 +42,6 @@ public class AbilityModifier implements Serializable {
public String stacking; public String stacking;
public AbilityMixinData[] modifierMixins; public AbilityMixinData[] modifierMixins;
public AbilityModifierProperty properties;
public ElementType elementType; public ElementType elementType;
public DynamicFloat elementDurability = DynamicFloat.ZERO; public DynamicFloat elementDurability = DynamicFloat.ZERO;
@ -265,7 +263,7 @@ public class AbilityModifier implements Serializable {
TurnDirectionToPos, TurnDirectionToPos,
UpdateReactionDamage, UpdateReactionDamage,
UseSkillEliteSet, UseSkillEliteSet,
WidgetSkillStart WidgetSkillStart;
} }
@SerializedName("$type") @SerializedName("$type")
@ -329,9 +327,6 @@ public class AbilityModifier implements Serializable {
public String srcKey, dstKey; public String srcKey, dstKey;
public int skillID; public int skillID;
public int resistanceListID;
public int monsterID;
public int summonTag;
public AbilityModifierAction[] actions; public AbilityModifierAction[] actions;
public AbilityModifierAction[] successActions; public AbilityModifierAction[] successActions;
@ -374,11 +369,6 @@ public class AbilityModifier implements Serializable {
} }
} }
public static class AbilityModifierProperty implements Serializable {
public float Actor_HpThresholdRatio;
// Add more properties here when GC needs them.
}
public enum State { public enum State {
LockHP, LockHP,
Invincible, Invincible,

View File

@ -2,13 +2,12 @@ package emu.grasscutter.data.binout;
import emu.grasscutter.data.binout.AbilityModifier.AbilityModifierAction; import emu.grasscutter.data.binout.AbilityModifier.AbilityModifierAction;
import java.util.*; import java.util.*;
import lombok.Getter;
public class AbilityModifierEntry { public class AbilityModifierEntry {
public final List<AbilityModifierAction> onModifierAdded; public List<AbilityModifierAction> onModifierAdded;
@Getter public final List<AbilityModifierAction> onThinkInterval; public List<AbilityModifierAction> onThinkInterval;
@Getter public final List<AbilityModifierAction> onRemoved; public List<AbilityModifierAction> onRemoved;
@Getter private final String name; // Custom value private final String name; // Custom value
public AbilityModifierEntry(String name) { public AbilityModifierEntry(String name) {
this.name = name; this.name = name;
@ -17,7 +16,19 @@ public class AbilityModifierEntry {
this.onRemoved = new ArrayList<>(); this.onRemoved = new ArrayList<>();
} }
public String getName() {
return name;
}
public List<AbilityModifierAction> getOnAdded() { public List<AbilityModifierAction> getOnAdded() {
return onModifierAdded; return onModifierAdded;
} }
public List<AbilityModifierAction> getOnThinkInterval() {
return onThinkInterval;
}
public List<AbilityModifierAction> getOnRemoved() {
return onRemoved;
}
} }

View File

@ -5,22 +5,53 @@ import emu.grasscutter.data.GameData;
import emu.grasscutter.game.quest.enums.QuestType; import emu.grasscutter.game.quest.enums.QuestType;
import java.util.*; import java.util.*;
import lombok.Data; import lombok.Data;
import lombok.Getter;
public class MainQuestData { public class MainQuestData {
@Getter private int id; private int id;
private int ICLLDPJFIMA; private int ICLLDPJFIMA;
@Getter private int series; private int series;
@Getter private QuestType type; private QuestType type;
@Getter private long titleTextMapHash; private long titleTextMapHash;
@Getter private int[] suggestTrackMainQuestList; private int[] suggestTrackMainQuestList;
@Getter private int[] rewardIdList; private int[] rewardIdList;
@Getter private SubQuestData[] subQuests; private SubQuestData[] subQuests;
@Getter private List<TalkData> talks; private List<TalkData> talks;
private long[] preloadLuaList; private long[] preloadLuaList;
public int getId() {
return id;
}
public int getSeries() {
return series;
}
public QuestType getType() {
return type;
}
public long getTitleTextMapHash() {
return titleTextMapHash;
}
public int[] getSuggestTrackMainQuestList() {
return suggestTrackMainQuestList;
}
public int[] getRewardIdList() {
return rewardIdList;
}
public SubQuestData[] getSubQuests() {
return subQuests;
}
public List<TalkData> getTalks() {
return talks;
}
public void onLoad() { public void onLoad() {
if (this.talks == null) this.talks = new ArrayList<>(); if (this.talks == null) this.talks = new ArrayList<>();
if (this.subQuests == null) this.subQuests = new SubQuestData[0]; if (this.subQuests == null) this.subQuests = new SubQuestData[0];

View File

@ -2,9 +2,7 @@ package emu.grasscutter.data.binout;
import emu.grasscutter.data.ResourceLoader.OpenConfigData; import emu.grasscutter.data.ResourceLoader.OpenConfigData;
import java.util.*; import java.util.*;
import lombok.Getter;
@Getter
public class OpenConfigEntry { public class OpenConfigEntry {
private final String name; private final String name;
private String[] addAbilities; private String[] addAbilities;
@ -27,23 +25,46 @@ public class OpenConfigEntry {
} }
} }
if (!abilityList.isEmpty()) { if (abilityList.size() > 0) {
this.addAbilities = abilityList.toArray(new String[0]); this.addAbilities = abilityList.toArray(new String[0]);
} }
if (!modList.isEmpty()) { if (modList.size() > 0) {
this.skillPointModifiers = modList.toArray(new SkillPointModifier[0]); this.skillPointModifiers = modList.toArray(new SkillPointModifier[0]);
} }
} }
@Getter public String getName() {
return name;
}
public String[] getAddAbilities() {
return addAbilities;
}
public int getExtraTalentIndex() {
return extraTalentIndex;
}
public SkillPointModifier[] getSkillPointModifiers() {
return skillPointModifiers;
}
public static class SkillPointModifier { public static class SkillPointModifier {
private final int skillId; private int skillId;
private final int delta; private int delta;
public SkillPointModifier(int skillId, int delta) { public SkillPointModifier(int skillId, int delta) {
this.skillId = skillId; this.skillId = skillId;
this.delta = delta; this.delta = delta;
} }
public int getSkillId() {
return skillId;
}
public int getDelta() {
return delta;
}
} }
} }

View File

@ -3,10 +3,9 @@ package emu.grasscutter.data.binout;
import emu.grasscutter.data.common.PointData; import emu.grasscutter.data.common.PointData;
import lombok.Getter; import lombok.Getter;
@Getter
public class ScenePointEntry { public class ScenePointEntry {
private final int sceneId; @Getter private final int sceneId;
private final PointData pointData; @Getter private final PointData pointData;
@Deprecated(forRemoval = true) @Deprecated(forRemoval = true)
public ScenePointEntry(String name, PointData pointData) { public ScenePointEntry(String name, PointData pointData) {

View File

@ -10,7 +10,7 @@ public class TalentData implements Serializable {
ModifySkillCD, ModifySkillCD,
UnlockTalentParam, UnlockTalentParam,
AddTalentExtraLevel, AddTalentExtraLevel,
ModifyAbility ModifyAbility;
} }
@SerializedName("$type") @SerializedName("$type")

View File

@ -4,14 +4,13 @@ import emu.grasscutter.data.binout.config.fields.ConfigAbilityData;
import java.util.List; import java.util.List;
import lombok.Getter; import lombok.Getter;
@Getter
public class ConfigLevelEntity { public class ConfigLevelEntity {
private List<ConfigAbilityData> abilities; @Getter private List<ConfigAbilityData> abilities;
private List<ConfigAbilityData> monsterAbilities; @Getter private List<ConfigAbilityData> monsterAbilities;
private List<ConfigAbilityData> avatarAbilities; @Getter private List<ConfigAbilityData> avatarAbilities;
private List<ConfigAbilityData> teamAbilities; @Getter private List<ConfigAbilityData> teamAbilities;
private List<Integer> preloadMonsterEntityIDs; @Getter private List<Integer> preloadMonsterEntityIDs;
private String dropElemControlType; @Getter private String dropElemControlType;
} }

View File

@ -8,5 +8,4 @@ import lombok.experimental.FieldDefaults;
public class ConfigCombat { public class ConfigCombat {
// There are more values that can be added that might be useful in the json // There are more values that can be added that might be useful in the json
ConfigCombatProperty property; ConfigCombatProperty property;
ConfigCombatSummon summon;
} }

View File

@ -1,14 +0,0 @@
package emu.grasscutter.data.binout.config.fields;
import java.util.List;
import lombok.*;
@Data
public class ConfigCombatSummon {
List<SummonTag> summonTags;
@Getter
public final class SummonTag {
int summonTag;
}
}

View File

@ -3,14 +3,13 @@ package emu.grasscutter.data.binout.routes;
// import emu.grasscutter.scripts.constants.IntValueEnum; // import emu.grasscutter.scripts.constants.IntValueEnum;
import lombok.Getter; import lombok.Getter;
@Getter
public enum RotAngleType /*implements IntValueEnum */ { public enum RotAngleType /*implements IntValueEnum */ {
ROT_NONE(-1), ROT_NONE(-1),
ROT_ANGLE_X(0), ROT_ANGLE_X(0),
ROT_ANGLE_Y(1), ROT_ANGLE_Y(1),
ROT_ANGLE_Z(2); ROT_ANGLE_Z(2);
private final int id; @Getter private final int id;
RotAngleType(int id) { RotAngleType(int id) {
this.id = id; this.id = id;

View File

@ -3,14 +3,13 @@ package emu.grasscutter.data.binout.routes;
// import emu.grasscutter.scripts.constants.IntValueEnum; // import emu.grasscutter.scripts.constants.IntValueEnum;
import lombok.Getter; import lombok.Getter;
@Getter
public enum RouteType /*implements IntValueEnum*/ { public enum RouteType /*implements IntValueEnum*/ {
Unknown(-1), Unknown(-1),
OneWay(0), OneWay(0),
Reciprocate(1), Reciprocate(1),
Loop(2); Loop(2);
private final int id; @Getter private final int id;
RouteType(int id) { RouteType(int id) {
this.id = id; this.id = id;

View File

@ -1,10 +1,19 @@
package emu.grasscutter.data.common; package emu.grasscutter.data.common;
import lombok.Getter;
@Getter
public class CurveInfo { public class CurveInfo {
private String type; private String type;
private String arith; private String arith;
private float value; private float value;
public String getType() {
return type;
}
public String getArith() {
return arith;
}
public float getValue() {
return value;
}
} }

View File

@ -92,7 +92,7 @@ public class DynamicFloat {
} }
public static class StackOp { public static class StackOp {
public final Op op; public Op op;
public float fValue; public float fValue;
public String sValue; public String sValue;

View File

@ -1,14 +1,24 @@
package emu.grasscutter.data.common; package emu.grasscutter.data.common;
import emu.grasscutter.game.props.FightProperty; import emu.grasscutter.game.props.FightProperty;
import lombok.Getter;
@Getter
public class FightPropData { public class FightPropData {
private String propType; private String propType;
private FightProperty prop; private FightProperty prop;
private float value; private float value;
public String getPropType() {
return propType;
}
public float getValue() {
return value;
}
public FightProperty getProp() {
return prop;
}
public void onLoad() { public void onLoad() {
this.prop = FightProperty.getPropByName(propType); this.prop = FightProperty.getPropByName(propType);
} }

View File

@ -1,10 +1,8 @@
package emu.grasscutter.data.common; package emu.grasscutter.data.common;
import com.google.gson.annotations.SerializedName; import com.google.gson.annotations.SerializedName;
import lombok.Getter;
// Used in excels // Used in excels
@Getter
public class ItemParamData { public class ItemParamData {
@SerializedName( @SerializedName(
value = "id", value = "id",
@ -23,10 +21,18 @@ public class ItemParamData {
this.count = count; this.count = count;
} }
public int getId() {
return id;
}
public int getItemId() { public int getItemId() {
return id; return id;
} }
public int getCount() {
return count;
}
public int getItemCount() { public int getItemCount() {
return count; return count;
} }

View File

@ -1,14 +1,19 @@
package emu.grasscutter.data.common; package emu.grasscutter.data.common;
import lombok.Getter;
@Getter
public class ItemParamStringData { public class ItemParamStringData {
private int id; private int id;
private String count; private String count;
public ItemParamStringData() {} public ItemParamStringData() {}
public int getId() {
return id;
}
public String getCount() {
return count;
}
public ItemParamData toItemParamData() { public ItemParamData toItemParamData() {
if (count.contains(";")) { if (count.contains(";")) {
String[] split = count.split(";"); String[] split = count.split(";");

View File

@ -1,11 +1,10 @@
package emu.grasscutter.data.common; package emu.grasscutter.data.common;
import emu.grasscutter.game.props.ItemUseOp; import emu.grasscutter.game.props.ItemUseOp;
import lombok.Getter;
public class ItemUseData { public class ItemUseData {
private ItemUseOp useOp; private ItemUseOp useOp;
@Getter private String[] useParam; private String[] useParam;
public ItemUseOp getUseOp() { public ItemUseOp getUseOp() {
if (useOp == null) { if (useOp == null) {
@ -13,4 +12,8 @@ public class ItemUseData {
} }
return useOp; return useOp;
} }
public String[] getUseParam() {
return useParam;
}
} }

View File

@ -1,18 +1,24 @@
package emu.grasscutter.data.common; package emu.grasscutter.data.common;
import java.util.List; import java.util.List;
import lombok.Getter;
@Getter
public class OpenCondData { public class OpenCondData {
private String condType; private String condType;
private List<Integer> paramList; private List<Integer> paramList;
public void setCondType(String cType) { public String getCondType() {
condType = cType; return condType;
} }
public void setParamList(List<Integer> pList) { public void setCondType(String condType) {
paramList = pList; condType = condType;
}
public List<Integer> getParamList() {
return paramList;
}
public void setParamList(List<Integer> paramList) {
paramList = paramList;
} }
} }

View File

@ -19,7 +19,6 @@ public final class PointData {
@Getter private Position size; @Getter private Position size;
@Getter private boolean forbidSimpleUnlock; @Getter private boolean forbidSimpleUnlock;
@Getter private boolean unlocked; @Getter private boolean unlocked;
@Getter private boolean groupLimit;
@SerializedName( @SerializedName(
value = "dungeonIds", value = "dungeonIds",
@ -29,7 +28,7 @@ public final class PointData {
@SerializedName( @SerializedName(
value = "dungeonRandomList", value = "dungeonRandomList",
alternate = {"GLEKJMEEOMH"}) alternate = {"OIBKFJNBLHO"})
@Getter @Getter
private int[] dungeonRandomList; private int[] dungeonRandomList;

View File

@ -1,9 +1,14 @@
package emu.grasscutter.data.common; package emu.grasscutter.data.common;
import lombok.Getter;
@Getter
public class PropGrowCurve { public class PropGrowCurve {
private String type; private String type;
private String growCurve; private String growCurve;
public String getType() {
return this.type;
}
public String getGrowCurve() {
return this.growCurve;
}
} }

View File

@ -4,22 +4,21 @@ import emu.grasscutter.data.*;
import emu.grasscutter.data.common.ItemParamData; import emu.grasscutter.data.common.ItemParamData;
import java.util.List; import java.util.List;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import lombok.Getter;
@ResourceType(name = "CombineExcelConfigData.json") @ResourceType(name = "CombineExcelConfigData.json")
public class CombineData extends GameResource { public class CombineData extends GameResource {
@Getter private int combineId; private int combineId;
@Getter private int playerLevel; private int playerLevel;
private boolean isDefaultShow; private boolean isDefaultShow;
@Getter private int combineType; private int combineType;
@Getter private int subCombineType; private int subCombineType;
@Getter private int resultItemId; private int resultItemId;
@Getter private int resultItemCount; private int resultItemCount;
@Getter private int scoinCost; private int scoinCost;
@Getter private List<ItemParamData> randomItems; private List<ItemParamData> randomItems;
@Getter private List<ItemParamData> materialItems; private List<ItemParamData> materialItems;
@Getter private String recipeType; private String recipeType;
@Override @Override
public int getId() { public int getId() {
@ -36,7 +35,47 @@ public class CombineData extends GameResource {
materialItems.stream().filter(item -> item.getId() > 0).collect(Collectors.toList()); materialItems.stream().filter(item -> item.getId() > 0).collect(Collectors.toList());
} }
public int getCombineId() {
return combineId;
}
public int getPlayerLevel() {
return playerLevel;
}
public boolean isDefaultShow() { public boolean isDefaultShow() {
return isDefaultShow; return isDefaultShow;
} }
public int getCombineType() {
return combineType;
}
public int getSubCombineType() {
return subCombineType;
}
public int getResultItemId() {
return resultItemId;
}
public int getResultItemCount() {
return resultItemCount;
}
public int getScoinCost() {
return scoinCost;
}
public List<ItemParamData> getRandomItems() {
return randomItems;
}
public List<ItemParamData> getMaterialItems() {
return materialItems;
}
public String getRecipeType() {
return recipeType;
}
} }

View File

@ -2,9 +2,7 @@ package emu.grasscutter.data.excels;
import emu.grasscutter.data.*; import emu.grasscutter.data.*;
import emu.grasscutter.data.ResourceType.LoadPriority; import emu.grasscutter.data.ResourceType.LoadPriority;
import lombok.Getter;
@Getter
@ResourceType( @ResourceType(
name = {"CookBonusExcelConfigData.json"}, name = {"CookBonusExcelConfigData.json"},
loadPriority = LoadPriority.LOW) loadPriority = LoadPriority.LOW)
@ -19,6 +17,22 @@ public class CookBonusData extends GameResource {
return this.avatarId; return this.avatarId;
} }
public int getAvatarId() {
return avatarId;
}
public int getRecipeId() {
return recipeId;
}
public int[] getParamVec() {
return paramVec;
}
public int[] getComplexParamVec() {
return complexParamVec;
}
public int getReplacementItemId() { public int getReplacementItemId() {
return this.paramVec[0]; return this.paramVec[0];
} }

View File

@ -1,46 +0,0 @@
package emu.grasscutter.data.excels;
import com.google.gson.annotations.SerializedName;
import emu.grasscutter.data.*;
import java.util.List;
import lombok.*;
import lombok.experimental.FieldDefaults;
@ResourceType(name = "CoopChapterExcelConfigData.json")
@Getter
@Setter // TODO: remove setters next API break
@FieldDefaults(level = AccessLevel.PRIVATE)
public class CoopChapterData extends GameResource {
@Getter(onMethod_ = @Override)
int id;
int avatarId;
// int chapterNameTextMapHash;
// int coopPageTitleTextMapHash;
// int chapterSortId;
// int avatarSortId;
// String chapterIcon;
List<CoopCondition> unlockCond;
// int [] unlockCondTips;
// int openMaterialId;
// int openMaterialNum;
// String beginTimeStr;
// int confidenceValue;
// String pointGraphPath;
// Double graphXRatio;
// Double graphYRatio;
@Data
@FieldDefaults(level = AccessLevel.PRIVATE)
private static class CoopCondition {
@SerializedName(
value = "_condType",
alternate = {"condType"})
String type = "COOP_COND_NONE";
@SerializedName(
value = "_args",
alternate = {"args"})
int[] args;
}
}

View File

@ -1,24 +0,0 @@
package emu.grasscutter.data.excels;
import emu.grasscutter.data.*;
import lombok.*;
import lombok.experimental.FieldDefaults;
@ResourceType(name = "CoopPointExcelConfigData.json")
@Getter
@Setter // TODO: remove setters next API break
@FieldDefaults(level = AccessLevel.PRIVATE)
public class CoopPointData extends GameResource {
@Getter(onMethod_ = @Override)
int id;
int chapterId;
String type;
int acceptQuest;
int[] postPointList;
// int pointNameTextMapHash;
// int pointDecTextMapHash;
int pointPosId;
// long photoMaleHash;
// long photoFemaleHash;
}

View File

@ -3,14 +3,13 @@ package emu.grasscutter.data.excels;
import emu.grasscutter.data.*; import emu.grasscutter.data.*;
import emu.grasscutter.data.common.ItemParamData; import emu.grasscutter.data.common.ItemParamData;
import java.util.List; import java.util.List;
import lombok.Getter;
@ResourceType( @ResourceType(
name = "EnvAnimalGatherExcelConfigData.json", name = "EnvAnimalGatherExcelConfigData.json",
loadPriority = ResourceType.LoadPriority.LOW) loadPriority = ResourceType.LoadPriority.LOW)
public class EnvAnimalGatherConfigData extends GameResource { public class EnvAnimalGatherConfigData extends GameResource {
@Getter private int animalId; private int animalId;
@Getter private String entityType; private String entityType;
private List<ItemParamData> gatherItemId; private List<ItemParamData> gatherItemId;
private String excludeWeathers; private String excludeWeathers;
private int aliveTime; private int aliveTime;
@ -22,7 +21,15 @@ public class EnvAnimalGatherConfigData extends GameResource {
return animalId; return animalId;
} }
public int getAnimalId() {
return animalId;
}
public String getEntityType() {
return entityType;
}
public ItemParamData getGatherItem() { public ItemParamData getGatherItem() {
return !gatherItemId.isEmpty() ? gatherItemId.get(0) : null; return gatherItemId.size() > 0 ? gatherItemId.get(0) : null;
} }
} }

View File

@ -3,18 +3,17 @@ package emu.grasscutter.data.excels;
import emu.grasscutter.data.*; import emu.grasscutter.data.*;
import emu.grasscutter.data.common.FightPropData; import emu.grasscutter.data.common.FightPropData;
import java.util.ArrayList; import java.util.ArrayList;
import lombok.Getter;
@ResourceType(name = "EquipAffixExcelConfigData.json") @ResourceType(name = "EquipAffixExcelConfigData.json")
public class EquipAffixData extends GameResource { public class EquipAffixData extends GameResource {
private int affixId; private int affixId;
private int id; private int id;
@Getter private int level; private int level;
@Getter private long nameTextMapHash; private long nameTextMapHash;
@Getter private String openConfig; private String openConfig;
@Getter private FightPropData[] addProps; private FightPropData[] addProps;
@Getter private float[] paramList; private float[] paramList;
@Override @Override
public int getId() { public int getId() {
@ -25,9 +24,29 @@ public class EquipAffixData extends GameResource {
return id; return id;
} }
public int getLevel() {
return level;
}
public long getNameTextMapHash() {
return nameTextMapHash;
}
public String getOpenConfig() {
return openConfig;
}
public FightPropData[] getAddProps() {
return addProps;
}
public float[] getParamList() {
return paramList;
}
@Override @Override
public void onLoad() { public void onLoad() {
ArrayList<FightPropData> parsed = new ArrayList<>(getAddProps().length); ArrayList<FightPropData> parsed = new ArrayList<FightPropData>(getAddProps().length);
for (FightPropData prop : getAddProps()) { for (FightPropData prop : getAddProps()) {
if (prop.getPropType() != null && prop.getValue() != 0f) { if (prop.getPropType() != null && prop.getValue() != 0f) {
prop.onLoad(); prop.onLoad();

View File

@ -2,18 +2,21 @@ package emu.grasscutter.data.excels;
import emu.grasscutter.data.*; import emu.grasscutter.data.*;
import emu.grasscutter.data.ResourceType.LoadPriority; import emu.grasscutter.data.ResourceType.LoadPriority;
import lombok.Getter;
@ResourceType(name = "FetterCharacterCardExcelConfigData.json", loadPriority = LoadPriority.HIGHEST) @ResourceType(name = "FetterCharacterCardExcelConfigData.json", loadPriority = LoadPriority.HIGHEST)
public class FetterCharacterCardData extends GameResource { public class FetterCharacterCardData extends GameResource {
private int avatarId; private int avatarId;
@Getter private int rewardId; private int rewardId;
@Override @Override
public int getId() { public int getId() {
return avatarId; return avatarId;
} }
public int getRewardId() {
return rewardId;
}
@Override @Override
public void onLoad() {} public void onLoad() {}
} }

View File

@ -4,7 +4,6 @@ import emu.grasscutter.data.*;
import emu.grasscutter.data.ResourceType.LoadPriority; import emu.grasscutter.data.ResourceType.LoadPriority;
import emu.grasscutter.data.common.OpenCondData; import emu.grasscutter.data.common.OpenCondData;
import java.util.List; import java.util.List;
import lombok.Getter;
@ResourceType( @ResourceType(
name = { name = {
@ -16,7 +15,7 @@ import lombok.Getter;
}, },
loadPriority = LoadPriority.HIGHEST) loadPriority = LoadPriority.HIGHEST)
public class FetterData extends GameResource { public class FetterData extends GameResource {
@Getter private int avatarId; private int avatarId;
private int fetterId; private int fetterId;
private List<OpenCondData> openCond; private List<OpenCondData> openCond;
@ -25,6 +24,10 @@ public class FetterData extends GameResource {
return fetterId; return fetterId;
} }
public int getAvatarId() {
return avatarId;
}
public List<OpenCondData> getOpenConds() { public List<OpenCondData> getOpenConds() {
return openCond; return openCond;
} }

View File

@ -1,15 +1,14 @@
package emu.grasscutter.data.excels; package emu.grasscutter.data.excels;
import emu.grasscutter.data.*; import emu.grasscutter.data.*;
import lombok.Getter;
@ResourceType(name = "GatherExcelConfigData.json") @ResourceType(name = "GatherExcelConfigData.json")
public class GatherData extends GameResource { public class GatherData extends GameResource {
private int pointType; private int pointType;
private int id; private int id;
@Getter private int gadgetId; private int gadgetId;
@Getter private int itemId; private int itemId;
@Getter private int cd; // Probably hours private int cd; // Probably hours
private boolean isForbidGuest; private boolean isForbidGuest;
private boolean initDisableInteract; private boolean initDisableInteract;
@ -22,6 +21,18 @@ public class GatherData extends GameResource {
return id; return id;
} }
public int getGadgetId() {
return gadgetId;
}
public int getItemId() {
return itemId;
}
public int getCd() {
return cd;
}
public boolean isForbidGuest() { public boolean isForbidGuest() {
return isForbidGuest; return isForbidGuest;
} }

View File

@ -1,17 +0,0 @@
package emu.grasscutter.data.excels;
import emu.grasscutter.data.GameResource;
import emu.grasscutter.data.ResourceType;
import lombok.AccessLevel;
import lombok.Getter;
import lombok.experimental.FieldDefaults;
@ResourceType(name = "HomeworldModuleExcelConfigData.json")
@FieldDefaults(level = AccessLevel.PRIVATE)
@Getter
public class HomeWorldModuleData extends GameResource {
int Id;
boolean isFree;
int worldSceneId;
int defaultRoomSceneId;
}

View File

@ -24,7 +24,7 @@ public class ItemData extends GameResource {
@Getter(onMethod_ = @Override) @Getter(onMethod_ = @Override)
private int id; private int id;
private final int stackLimit = 1; private int stackLimit = 1;
private int maxUseCount; private int maxUseCount;
private int rankLevel; private int rankLevel;
private String effectName; private String effectName;
@ -37,7 +37,7 @@ public class ItemData extends GameResource {
private int[] destroyReturnMaterialCount; private int[] destroyReturnMaterialCount;
// Enums // Enums
private final ItemType itemType = ItemType.ITEM_NONE; private ItemType itemType = ItemType.ITEM_NONE;
private MaterialType materialType = MaterialType.MATERIAL_NONE; private MaterialType materialType = MaterialType.MATERIAL_NONE;
private EquipType equipType = EquipType.EQUIP_NONE; private EquipType equipType = EquipType.EQUIP_NONE;
private String effectType; private String effectType;
@ -48,10 +48,10 @@ public class ItemData extends GameResource {
private int[] satiationParams; private int[] satiationParams;
// Usable item // Usable item
private final ItemUseTarget useTarget = ItemUseTarget.ITEM_USE_TARGET_NONE; private ItemUseTarget useTarget = ItemUseTarget.ITEM_USE_TARGET_NONE;
private List<ItemUseData> itemUse; private List<ItemUseData> itemUse;
private List<ItemUseAction> itemUseActions; private List<ItemUseAction> itemUseActions;
private final boolean useOnGain = false; private boolean useOnGain = false;
// Relic // Relic
private int mainPropDepotId; private int mainPropDepotId;
@ -79,7 +79,7 @@ public class ItemData extends GameResource {
private int comfort; private int comfort;
private List<Integer> furnType; private List<Integer> furnType;
private List<Integer> furnitureGadgetID; private List<Integer> furnitureGadgetID;
private final SpecialFurnitureType specialFurnitureType = SpecialFurnitureType.NOT_SPECIAL; private SpecialFurnitureType specialFurnitureType = SpecialFurnitureType.NOT_SPECIAL;
@SerializedName( @SerializedName(
value = "roomSceneId", value = "roomSceneId",

View File

@ -39,10 +39,12 @@ public class OpenStateData extends GameResource {
OPEN_STATE_COND_PARENT_QUEST OPEN_STATE_COND_PARENT_QUEST
} }
@Getter
public static class OpenStateCond { public static class OpenStateCond {
@Getter
private OpenStateCondType condType; private OpenStateCondType condType;
@Getter
private int param; private int param;
@Getter
private int param2; private int param2;
} }
} }

View File

@ -9,7 +9,7 @@ public class PlayerLevelData extends GameResource {
private int level; private int level;
private int exp; private int exp;
private int rewardId; private int rewardId;
private final int expeditionLimitAdd = 0; private int expeditionLimitAdd = 0;
private int unlockWorldLevel; private int unlockWorldLevel;
private long unlockDescTextMapHash; private long unlockDescTextMapHash;

View File

@ -25,8 +25,7 @@ public class ProudSkillData extends GameResource {
@Getter private long nameTextMapHash; @Getter private long nameTextMapHash;
@Transient private Iterable<ItemParamData> totalCostItems; @Transient private Iterable<ItemParamData> totalCostItems;
@Transient @Getter @Transient @Getter private Object2FloatMap<String> paramListMap = new Object2FloatOpenHashMap<>();
private final Object2FloatMap<String> paramListMap = new Object2FloatOpenHashMap<>();
@Override @Override
public int getId() { public int getId() {

View File

@ -6,12 +6,11 @@ import emu.grasscutter.game.world.World;
import java.util.*; import java.util.*;
import lombok.Getter; import lombok.Getter;
@Getter
@ResourceType(name = "RefreshPolicyExcelConfigData.json") @ResourceType(name = "RefreshPolicyExcelConfigData.json")
public class RefreshPolicyExcelConfigData extends GameResource { public class RefreshPolicyExcelConfigData extends GameResource {
private int id; @Getter private int id;
private RefreshType type; @Getter private RefreshType type;
private String time; @Getter private String time;
private static int upperBound(List<Integer> list, int low, int high, int value) { private static int upperBound(List<Integer> list, int low, int high, int value) {
while (low < high) { while (low < high) {
@ -49,9 +48,9 @@ public class RefreshPolicyExcelConfigData extends GameResource {
upperBound( upperBound(
params, (int) params.get(0), (int) params.get(params.size() - 1), (int) temp); params, (int) params.get(0), (int) params.get(params.size() - 1), (int) temp);
var upper_bound = params.get(upper_bound_idx); var upper_bound = params.get(upper_bound_idx);
if (params.get(params.size() - 1).equals(upper_bound)) { if (params.get(params.size() - 1) == upper_bound) {
return (params.get(params.size() - 1) - params.get(0)) + 60 * 60 * 24 * 7; return (params.get(params.size() - 1) - params.get(0)) + 60 * 60 * 24 * 7;
} else if (params.get(0).equals(upper_bound)) { } else if (params.get(0) == upper_bound) {
return (params.get(params.size() - 1) - params.get(0)) + 60 * 60 * 24 * 7; return (params.get(params.size() - 1) - params.get(0)) + 60 * 60 * 24 * 7;
} }
return (params.get(upper_bound_idx - 1) - params.get(0)); return (params.get(upper_bound_idx - 1) - params.get(0));
@ -65,15 +64,15 @@ public class RefreshPolicyExcelConfigData extends GameResource {
upperBound( upperBound(
params, (int) params.get(0), (int) params.get(params.size() - 1), (int) temp); params, (int) params.get(0), (int) params.get(params.size() - 1), (int) temp);
var upper_bound = params.get(upper_bound_idx); var upper_bound = params.get(upper_bound_idx);
if (params.get(params.size() - 1).equals(upper_bound)) { if (params.get(params.size() - 1) == upper_bound) {
return (params.get(params.size() - 1) - params.get(0)) + 60 * 60 * 24 * 7; return (params.get(params.size() - 1) - params.get(0)) + 60 * 60 * 24 * 7;
} else if (params.get(0).equals(upper_bound)) { } else if (params.get(0) == upper_bound) {
return (params.get(params.size() - 1) - params.get(0)) + 60 * 60 * 24 * 7; return (params.get(params.size() - 1) - params.get(0)) + 60 * 60 * 24 * 7;
} }
return (params.get(upper_bound_idx - 1) - params.get(0)); return (params.get(upper_bound_idx - 1) - params.get(0));
} }
case REFRESH_DAYBEGIN_INTERVAL: case REFRESH_DAYBEGIN_INTERVAL:
if (params.isEmpty()) return -1; if (params.size() == 0) return -1;
return params.get(0) * 60 * 60 * 24; return params.get(0) * 60 * 60 * 24;
} }
} catch (Exception e) { } catch (Exception e) {

View File

@ -3,18 +3,21 @@ package emu.grasscutter.data.excels;
import emu.grasscutter.data.*; import emu.grasscutter.data.*;
import emu.grasscutter.data.common.ItemParamData; import emu.grasscutter.data.common.ItemParamData;
import java.util.List; import java.util.List;
import lombok.Getter;
@ResourceType(name = "RewardExcelConfigData.json") @ResourceType(name = "RewardExcelConfigData.json")
public class RewardData extends GameResource { public class RewardData extends GameResource {
public int rewardId; public int rewardId;
@Getter public List<ItemParamData> rewardItemList; public List<ItemParamData> rewardItemList;
@Override @Override
public int getId() { public int getId() {
return rewardId; return rewardId;
} }
public List<ItemParamData> getRewardItemList() {
return rewardItemList;
}
@Override @Override
public void onLoad() { public void onLoad() {
rewardItemList = rewardItemList.stream().filter(i -> i.getId() > 0).toList(); rewardItemList = rewardItemList.stream().filter(i -> i.getId() > 0).toList();

View File

@ -8,7 +8,7 @@ import lombok.Getter;
@ResourceType(name = "RewardPreviewExcelConfigData.json", loadPriority = LoadPriority.HIGH) @ResourceType(name = "RewardPreviewExcelConfigData.json", loadPriority = LoadPriority.HIGH)
public class RewardPreviewData extends GameResource { public class RewardPreviewData extends GameResource {
@Getter(onMethod = @__(@Override)) @Getter(onMethod_ = @Override)
private int id; private int id;
private ItemParamStringData[] previewItems; private ItemParamStringData[] previewItems;
@ -25,7 +25,7 @@ public class RewardPreviewData extends GameResource {
Arrays.stream(this.previewItems) Arrays.stream(this.previewItems)
.filter(d -> d.getId() > 0 && d.getCount() != null && !d.getCount().isEmpty()) .filter(d -> d.getId() > 0 && d.getCount() != null && !d.getCount().isEmpty())
.map(ItemParamStringData::toItemParamData) .map(ItemParamStringData::toItemParamData)
.toArray(ItemParamData[]::new); .toArray(size -> new ItemParamData[size]);
} else { } else {
this.previewItemsArray = new ItemParamData[0]; this.previewItemsArray = new ItemParamData[0];
} }

View File

@ -5,27 +5,25 @@ import emu.grasscutter.data.*;
import emu.grasscutter.data.common.ItemParamData; import emu.grasscutter.data.common.ItemParamData;
import emu.grasscutter.game.shop.ShopInfo; import emu.grasscutter.game.shop.ShopInfo;
import java.util.List; import java.util.List;
import lombok.Getter;
@ResourceType(name = "ShopGoodsExcelConfigData.json") @ResourceType(name = "ShopGoodsExcelConfigData.json")
public class ShopGoodsData extends GameResource { public class ShopGoodsData extends GameResource {
@Getter private int goodsId; private int goodsId;
@Getter private int shopType; private int shopType;
@Getter private int itemId; private int itemId;
@Getter private int itemCount; private int itemCount;
@Getter private int costScoin; private int costScoin;
@Getter private int costHcoin; private int costHcoin;
@Getter private int costMcoin; private int costMcoin;
@Getter private List<ItemParamData> costItems; private List<ItemParamData> costItems;
@Getter private int minPlayerLevel; private int minPlayerLevel;
@Getter private int maxPlayerLevel; private int maxPlayerLevel;
@Getter private int buyLimit; private int buyLimit;
@Getter
@SerializedName( @SerializedName(
value = "subTabId", value = "subTabId",
alternate = {"secondarySheetId"}) alternate = {"secondarySheetId"})
@ -34,7 +32,7 @@ public class ShopGoodsData extends GameResource {
private String refreshType; private String refreshType;
private transient ShopInfo.ShopRefreshType refreshTypeEnum; private transient ShopInfo.ShopRefreshType refreshTypeEnum;
@Getter private int refreshParam; private int refreshParam;
@Override @Override
public void onLoad() { public void onLoad() {
@ -55,7 +53,59 @@ public class ShopGoodsData extends GameResource {
return getGoodsId(); return getGoodsId();
} }
public int getGoodsId() {
return goodsId;
}
public int getShopType() {
return shopType;
}
public int getItemId() {
return itemId;
}
public int getItemCount() {
return itemCount;
}
public int getCostScoin() {
return costScoin;
}
public int getCostHcoin() {
return costHcoin;
}
public int getCostMcoin() {
return costMcoin;
}
public List<ItemParamData> getCostItems() {
return costItems;
}
public int getMinPlayerLevel() {
return minPlayerLevel;
}
public int getMaxPlayerLevel() {
return maxPlayerLevel;
}
public int getBuyLimit() {
return buyLimit;
}
public int getSubTabId() {
return subTabId;
}
public ShopInfo.ShopRefreshType getRefreshType() { public ShopInfo.ShopRefreshType getRefreshType() {
return refreshTypeEnum; return refreshTypeEnum;
} }
public int getRefreshParam() {
return refreshParam;
}
} }

View File

@ -4,15 +4,13 @@ import emu.grasscutter.data.*;
import emu.grasscutter.data.common.ItemParamData; import emu.grasscutter.data.common.ItemParamData;
import lombok.*; import lombok.*;
@Setter
@Getter
@ResourceType(name = "StatuePromoteExcelConfigData.json") @ResourceType(name = "StatuePromoteExcelConfigData.json")
public class StatuePromoteData extends GameResource { public class StatuePromoteData extends GameResource {
private int level; @Getter @Setter private int level;
private int cityId; @Getter @Setter private int cityId;
private ItemParamData[] costItems; @Getter @Setter private ItemParamData[] costItems;
private int[] rewardIdList; @Getter @Setter private int[] rewardIdList;
private int stamina; @Getter @Setter private int stamina;
@Override @Override
public int getId() { public int getId() {

View File

@ -14,7 +14,7 @@ public class AchievementData extends GameResource {
private static final AtomicBoolean isDivided = new AtomicBoolean(); private static final AtomicBoolean isDivided = new AtomicBoolean();
private int goalId; private int goalId;
private int preStageAchievementId; private int preStageAchievementId;
private final Set<Integer> groupAchievementIdList = new HashSet<>(); private Set<Integer> groupAchievementIdList = new HashSet<>();
private boolean isParent; private boolean isParent;
private long titleTextMapHash; private long titleTextMapHash;
private long descTextMapHash; private long descTextMapHash;

View File

@ -15,10 +15,9 @@ public class ActivityCondExcelConfigData extends GameResource {
LogicType condComb; LogicType condComb;
List<ActivityConfigCondition> cond; List<ActivityConfigCondition> cond;
@Getter
public static class ActivityConfigCondition { public static class ActivityConfigCondition {
private ActivityConditions type; @Getter private ActivityConditions type;
private List<Integer> param; @Getter private List<Integer> param;
public int[] paramArray() { public int[] paramArray() {
return param.stream().mapToInt(Integer::intValue).toArray(); return param.stream().mapToInt(Integer::intValue).toArray();

View File

@ -5,12 +5,11 @@ import emu.grasscutter.game.shop.ShopType;
import java.util.List; import java.util.List;
import lombok.Getter; import lombok.Getter;
@Getter
@ResourceType(name = "ActivityShopOverallExcelConfigData.json") @ResourceType(name = "ActivityShopOverallExcelConfigData.json")
public class ActivityShopData extends GameResource { public class ActivityShopData extends GameResource {
private int scheduleId; @Getter private int scheduleId;
private ShopType shopType; @Getter private ShopType shopType;
private List<Integer> sheetList; @Getter private List<Integer> sheetList;
@Override @Override
public int getId() { public int getId() {

View File

@ -2,22 +2,33 @@ package emu.grasscutter.data.excels.avatar;
import com.google.gson.annotations.SerializedName; import com.google.gson.annotations.SerializedName;
import emu.grasscutter.data.*; import emu.grasscutter.data.*;
import lombok.Getter;
@ResourceType(name = "AvatarCostumeExcelConfigData.json") @ResourceType(name = "AvatarCostumeExcelConfigData.json")
public class AvatarCostumeData extends GameResource { public class AvatarCostumeData extends GameResource {
@SerializedName(value = "skinId", alternate = "costumeId") @SerializedName(value = "skinId", alternate = "costumeId")
private int skinId; private int skinId;
@Getter private int itemId; private int itemId;
@Getter private int characterId; private int characterId;
@Getter private int quality; private int quality;
@Override @Override
public int getId() { public int getId() {
return this.skinId; return this.skinId;
} }
public int getItemId() {
return this.itemId;
}
public int getCharacterId() {
return characterId;
}
public int getQuality() {
return quality;
}
@Override @Override
public void onLoad() { public void onLoad() {
GameData.getAvatarCostumeDataItemIdMap().put(this.getItemId(), this); GameData.getAvatarCostumeDataItemIdMap().put(this.getItemId(), this);

View File

@ -4,11 +4,10 @@ import emu.grasscutter.data.*;
import emu.grasscutter.data.common.CurveInfo; import emu.grasscutter.data.common.CurveInfo;
import java.util.*; import java.util.*;
import java.util.stream.Stream; import java.util.stream.Stream;
import lombok.Getter;
@ResourceType(name = "AvatarCurveExcelConfigData.json") @ResourceType(name = "AvatarCurveExcelConfigData.json")
public class AvatarCurveData extends GameResource { public class AvatarCurveData extends GameResource {
@Getter private int level; private int level;
private CurveInfo[] curveInfos; private CurveInfo[] curveInfos;
private Map<String, Float> curveInfoMap; private Map<String, Float> curveInfoMap;
@ -18,6 +17,10 @@ public class AvatarCurveData extends GameResource {
return this.level; return this.level;
} }
public int getLevel() {
return level;
}
public Map<String, Float> getCurveInfos() { public Map<String, Float> getCurveInfos() {
return curveInfoMap; return curveInfoMap;
} }

View File

@ -55,7 +55,7 @@ public class AvatarData extends GameResource {
@Getter @Getter
private IntList abilities; private IntList abilities;
@Getter @Getter
private final List<String> abilitieNames = new ArrayList<>(); private List<String> abilitieNames = new ArrayList<>();
@Getter @Getter
private List<Integer> fetters; private List<Integer> fetters;

View File

@ -1,18 +1,21 @@
package emu.grasscutter.data.excels.avatar; package emu.grasscutter.data.excels.avatar;
import emu.grasscutter.data.*; import emu.grasscutter.data.*;
import lombok.Getter;
@ResourceType(name = "AvatarFlycloakExcelConfigData.json") @ResourceType(name = "AvatarFlycloakExcelConfigData.json")
public class AvatarFlycloakData extends GameResource { public class AvatarFlycloakData extends GameResource {
private int flycloakId; private int flycloakId;
@Getter private long nameTextMapHash; private long nameTextMapHash;
@Override @Override
public int getId() { public int getId() {
return this.flycloakId; return this.flycloakId;
} }
public long getNameTextMapHash() {
return nameTextMapHash;
}
@Override @Override
public void onLoad() {} public void onLoad() {}
} }

View File

@ -1,9 +1,7 @@
package emu.grasscutter.data.excels.avatar; package emu.grasscutter.data.excels.avatar;
import emu.grasscutter.data.*; import emu.grasscutter.data.*;
import lombok.Getter;
@Getter
@ResourceType(name = "AvatarLevelExcelConfigData.json") @ResourceType(name = "AvatarLevelExcelConfigData.json")
public class AvatarLevelData extends GameResource { public class AvatarLevelData extends GameResource {
private int level; private int level;
@ -13,4 +11,12 @@ public class AvatarLevelData extends GameResource {
public int getId() { public int getId() {
return this.level; return this.level;
} }
public int getLevel() {
return level;
}
public int getExp() {
return exp;
}
} }

View File

@ -3,28 +3,51 @@ package emu.grasscutter.data.excels.avatar;
import emu.grasscutter.data.*; import emu.grasscutter.data.*;
import emu.grasscutter.data.common.*; import emu.grasscutter.data.common.*;
import java.util.ArrayList; import java.util.ArrayList;
import lombok.Getter;
@ResourceType(name = "AvatarPromoteExcelConfigData.json") @ResourceType(name = "AvatarPromoteExcelConfigData.json")
public class AvatarPromoteData extends GameResource { public class AvatarPromoteData extends GameResource {
@Getter private int avatarPromoteId; private int avatarPromoteId;
@Getter private int promoteLevel; private int promoteLevel;
private int scoinCost; private int scoinCost;
@Getter private ItemParamData[] costItems; private ItemParamData[] costItems;
@Getter private int unlockMaxLevel; private int unlockMaxLevel;
@Getter private FightPropData[] addProps; private FightPropData[] addProps;
@Getter private int requiredPlayerLevel; private int requiredPlayerLevel;
@Override @Override
public int getId() { public int getId() {
return (avatarPromoteId << 8) + promoteLevel; return (avatarPromoteId << 8) + promoteLevel;
} }
public int getAvatarPromoteId() {
return avatarPromoteId;
}
public int getPromoteLevel() {
return promoteLevel;
}
public ItemParamData[] getCostItems() {
return costItems;
}
public int getCoinCost() { public int getCoinCost() {
return scoinCost; return scoinCost;
} }
public FightPropData[] getAddProps() {
return addProps;
}
public int getUnlockMaxLevel() {
return unlockMaxLevel;
}
public int getRequiredPlayerLevel() {
return requiredPlayerLevel;
}
@Override @Override
public void onLoad() { public void onLoad() {
// Trim item params // Trim item params

Some files were not shown because too many files have changed in this diff Show More