diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md
new file mode 100644
index 000000000..dd84ea782
--- /dev/null
+++ b/.github/ISSUE_TEMPLATE/bug_report.md
@@ -0,0 +1,38 @@
+---
+name: Bug report
+about: Create a report to help us improve
+title: ''
+labels: ''
+assignees: ''
+
+---
+
+**Describe the bug**
+A clear and concise description of what the bug is.
+
+**To Reproduce**
+Steps to reproduce the behavior:
+1. Go to '...'
+2. Click on '....'
+3. Scroll down to '....'
+4. See error
+
+**Expected behavior**
+A clear and concise description of what you expected to happen.
+
+**Screenshots**
+If applicable, add screenshots to help explain your problem.
+
+**Desktop (please complete the following information):**
+ - OS: [e.g. iOS]
+ - Browser [e.g. chrome, safari]
+ - Version [e.g. 22]
+
+**Smartphone (please complete the following information):**
+ - Device: [e.g. iPhone6]
+ - OS: [e.g. iOS8.1]
+ - Browser [e.g. stock browser, safari]
+ - Version [e.g. 22]
+
+**Additional context**
+Add any other context about the problem here.
diff --git a/.github/ISSUE_TEMPLATE/custom.md b/.github/ISSUE_TEMPLATE/custom.md
new file mode 100644
index 000000000..48d5f81fa
--- /dev/null
+++ b/.github/ISSUE_TEMPLATE/custom.md
@@ -0,0 +1,10 @@
+---
+name: Custom issue template
+about: Describe this issue template's purpose here.
+title: ''
+labels: ''
+assignees: ''
+
+---
+
+
diff --git a/.github/ISSUE_TEMPLATE/feature_request.md b/.github/ISSUE_TEMPLATE/feature_request.md
new file mode 100644
index 000000000..bbcbbe7d6
--- /dev/null
+++ b/.github/ISSUE_TEMPLATE/feature_request.md
@@ -0,0 +1,20 @@
+---
+name: Feature request
+about: Suggest an idea for this project
+title: ''
+labels: ''
+assignees: ''
+
+---
+
+**Is your feature request related to a problem? Please describe.**
+A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
+
+**Describe the solution you'd like**
+A clear and concise description of what you want to happen.
+
+**Describe alternatives you've considered**
+A clear and concise description of any alternative solutions or features you've considered.
+
+**Additional context**
+Add any other context or screenshots about the feature request here.
diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml
index 8457bf7ac..cc0193c1a 100644
--- a/.github/workflows/build.yml
+++ b/.github/workflows/build.yml
@@ -3,9 +3,14 @@ on:
push:
branches:
- "stable"
+ pull_request:
+ types:
+ - opened
+ - synchronize
+ - reopened
jobs:
Build-Server-Jar:
- runs-on: windows-latest
+ runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v2
@@ -13,12 +18,11 @@ jobs:
uses: actions/setup-java@v3
with:
distribution: temurin
- java-version: '8'
+ java-version: '16'
- name: Run Gradle
- run: .\gradlew.bat && .\gradlew jar
+ run: ./gradlew && ./gradlew jar
- name: Upload build
uses: actions/upload-artifact@v3
with:
name: Grasscutter
path: grasscutter.jar
-
diff --git a/.gitignore b/.gitignore
index 9df6d0071..8422b6e18 100644
--- a/.gitignore
+++ b/.gitignore
@@ -45,15 +45,17 @@ tmp/
.loadpath
.recommenders
+# VSCode
+.vscode
+
# Grasscutter
resources/*
logs/*
data/AbilityEmbryos.json
data/OpenConfig.json
-proto/auto/
-proto/protoc.exe
+proto/*
GM Handbook.txt
config.json
mitmdump.exe
grasscutter.jar
-mongod.exe
+mongod.exe
\ No newline at end of file
diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md
new file mode 100644
index 000000000..5196d0514
--- /dev/null
+++ b/CODE_OF_CONDUCT.md
@@ -0,0 +1,127 @@
+# Contributor Covenant Code of Conduct
+
+## Our Pledge
+
+We as members, contributors, and leaders pledge to make participation in our
+community a harassment-free experience for everyone, regardless of age, body
+size, visible or invisible disability, ethnicity, sex characteristics, gender
+identity and expression, level of experience, education, socio-economic status,
+nationality, personal appearance, race, religion, or sexual identity
+and orientation.
+
+We pledge to act and interact in ways that contribute to an open, welcoming,
+diverse, inclusive, and healthy community.
+
+## Our Standards
+
+Examples of behavior that contributes to a positive environment for our
+community include:
+
+* Demonstrating empathy and kindness toward other people
+* Being respectful of differing opinions, viewpoints, and experiences
+* Giving and gracefully accepting constructive feedback
+* Accepting responsibility and apologizing to those affected by our mistakes,
+ and learning from the experience
+* Focusing on what is best not just for us as individuals, but for the
+ overall community
+
+Examples of unacceptable behavior include:
+
+* The use of sexualized language or imagery, and sexual attention or
+ advances of any kind
+* Trolling, insulting or derogatory comments, and personal or political attacks
+* Public or private harassment
+* Publishing others' private information, such as a physical or email
+ address, without their explicit permission
+* Other conduct which could reasonably be considered inappropriate in a
+ professional setting
+
+## Enforcement Responsibilities
+
+Community leaders are responsible for clarifying and enforcing our standards of
+acceptable behavior and will take appropriate and fair corrective action in
+response to any behavior that they deem inappropriate, threatening, offensive,
+or harmful.
+
+Community leaders have the right and responsibility to remove, edit, or reject
+comments, commits, code, wiki edits, issues, and other contributions that are
+not aligned to this Code of Conduct, and will communicate reasons for moderation
+decisions when appropriate.
+
+## Scope
+
+This Code of Conduct applies within all community spaces, and also applies when
+an individual is officially representing the community in public spaces.
+Examples of representing our community include using an official e-mail address,
+posting via an official social media account, or acting as an appointed
+representative at an online or offline event.
+
+## Enforcement
+
+Instances of abusive, harassing, or otherwise unacceptable behavior may be
+reported to the community leaders responsible for enforcement at
+[the Discord server](https://discord.gg/T5vZU6UyeG).
+All complaints will be reviewed and investigated promptly and fairly.
+All community leaders are obligated to respect the privacy and security of the
+reporter of any incident.
+
+## Enforcement Guidelines
+
+Community leaders will follow these Community Impact Guidelines in determining
+the consequences for any action they deem in violation of this Code of Conduct:
+
+### 1. Correction
+
+**Community Impact**: Use of inappropriate language or other behavior deemed
+unprofessional or unwelcome in the community.
+
+**Consequence**: A private, written warning from community leaders, providing
+clarity around the nature of the violation and an explanation of why the
+behavior was inappropriate. A public apology may be requested.
+
+### 2. Warning
+
+**Community Impact**: A violation through a single incident or series
+of actions.
+
+**Consequence**: A warning with consequences for continued behavior. No
+interaction with the people involved, including unsolicited interaction with
+those enforcing the Code of Conduct, for a specified period of time. This
+includes avoiding interactions in community spaces as well as external channels
+like social media. Violating these terms may lead to a temporary or
+permanent ban.
+
+### 3. Temporary Ban
+
+**Community Impact**: A serious violation of community standards, including
+sustained inappropriate behavior.
+
+**Consequence**: A temporary ban from any sort of interaction or public
+communication with the community for a specified period of time. No public or
+private interaction with the people involved, including unsolicited interaction
+with those enforcing the Code of Conduct, is allowed during this period.
+Violating these terms may lead to a permanent ban.
+
+### 4. Permanent Ban
+
+**Community Impact**: Demonstrating a pattern of violation of community
+standards, including sustained inappropriate behavior, harassment of an
+individual, or aggression toward or disparagement of classes of individuals.
+
+**Consequence**: A permanent ban from any sort of public interaction within
+the community.
+
+## Attribution
+
+This Code of Conduct is adapted from the [Contributor Covenant][homepage],
+version 2.0, available at
+https://www.contributor-covenant.org/version/2/0/code_of_conduct.html.
+
+Community Impact Guidelines were inspired by [Mozilla's code of conduct
+enforcement ladder](https://github.com/mozilla/diversity).
+
+[homepage]: https://www.contributor-covenant.org
+
+For answers to common questions about this code of conduct, see the FAQ at
+https://www.contributor-covenant.org/faq. Translations are available at
+https://www.contributor-covenant.org/translations.
\ No newline at end of file
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
new file mode 100644
index 000000000..55b94eb7d
--- /dev/null
+++ b/CONTRIBUTING.md
@@ -0,0 +1,14 @@
+# Contributing
+
+Please note we have a code of conduct, please follow it in all your interactions with the project. If you have any further questions please create an issue or ask in the Discord server.
+
+- Only fix/add the functionality in question OR address wide-spread whitespace/style issues, not both.
+- Address a single concern in the least number of changed lines as possible.
+
+**Do not make a pull request to merge into stable unless it is a hotfix. Use the development branch instead.**
+
+## Pull Request Process
+
+1. Ensure any install or build dependencies are removed before the end of the layer when doing a build.
+2. Update the README.md and wiki with details of changes to the interface, this includes new environment variables, exposed ports, useful file locations and container parameters.
+3. Write with detail on your pull request description what you have committed, to make it easier for the collaborators to make a changelog.
diff --git a/README.md b/README.md
index e6843b116..5bcec2715 100644
--- a/README.md
+++ b/README.md
@@ -1,79 +1,148 @@
-# Grasscutter
-A WIP server reimplementation for *some anime game* 2.3-2.6
+![Grasscutter](https://socialify.git.ci/Grasscutters/Grasscutter/image?description=1&forks=1&issues=1&language=1&logo=https%3A%2F%2Fs2.loli.net%2F2022%2F04%2F25%2FxOiJn7lCdcT5Mw1.png&name=1&owner=1&pulls=1&stargazers=1&theme=Light)
+
+
+
+
+EN | [中文](README_zh-CN.md)
+
+**Attention:** We always welcome contributors to the project. Before adding your contribution, please carefully read our [Code of Conduct](https://github.com/Grasscutters/Grasscutter/blob/stable/CONTRIBUTING.md).
+
+## Current features
-**Documentation**: [Grasscutter Wiki](https://github.com/Melledy/Grasscutter/wiki/)
-**Note**: For support please join the [Discord server](https://discord.gg/T5vZU6UyeG).
-# Current features
* Logging in
* Combat
+* Friends list
+* Teleportation
+* Gacha system
+* Co-op *partially* work
* Spawning monsters via console
* Inventory features (recieving items/characters, upgrading items/characters, etc)
-* Gacha system
-* Friends list
-* Co-op *partially* work
-# Quick setup guide
-### Note
-* If you update from an older version, delete `config.json` for regeneration
-### Prerequisites
-* Java 16
-* Mongodb (recommended 4.0+)
+## Quick setup guide
+
+**Note:** for support please join our [Discord](https://discord.gg/T5vZU6UyeG)
+
+### Requirements
+
+* Java SE - 16 ([mirror link](https://github.com/adoptium/temurin16-binaries/releases/tag/jdk-16.0.2+7) since Oracle required an account to download old builds)
+
+ **Note:** If you just want to **run it**, then **jre** is fine
+
+* MongoDB (recommended 4.0+)
+
* Proxy daemon: mitmproxy (mitmdump, recommended), Fiddler Classic, etc.
-### Starting up Grasscutter server (Assuming you are on Windows)
-1. Setup compile environment `gradlew.bat`
-2. Compile Grasscutter with `gradlew jar`
-3. Create a folder named `resources` in your Grasscutter directory, bring your `BinOutput` and `ExcelBinOutput` folders into it *(Check the wiki for more details how to get those.)*
-4. Run Grasscutter with `java -jar grasscutter.jar`. Make sure mongodb service is running as well.
+### Running
+
+**Note:** If you update from an older version, delete `config.json` for regeneration
+
+1. Get `grasscutter.jar`
+ - Download from [actions](https://nightly.link/Grasscutters/Grasscutter/workflows/build/stable/Grasscutter.zip)
+ - [Build by yourself](#Building)
+2. Create a `resources` folder in the directory where grasscutter.jar is located and bring your `BinOutput` and `ExcelBinOutput` folders into it *(Check the [wiki](https://github.com/Grasscutters/Grasscutter/wiki) for more details how to get those.)*
+3. Run Grasscutter with `java -jar grasscutter.jar`. **Make sure mongodb service is running as well.**
### Connecting with the client
-½. Create an account using *server console command* below
-1. Run a proxy daemon: (choose either one)
- - mitmdump: `mitmdump -s proxy.py -k`
- - Fiddler Classic: Run Fiddler Classic, turn on `Decrypt https traffic` in setting and change the default port there (Tools -> Options -> Connections) to anything other than `8888`, and load [this script](https://github.lunatic.moe/fiddlerscript).
- - [Hosts file](https://github.com/Melledy/Grasscutter/wiki/Running#traffic-route-map)
-2. Trust CA certificate:
- - mitmdump: `certutil -addstore root %USERPROFILE%\.mitmproxy\mitmproxy-ca-cert.cer`
+
+½. Create an account using [server console command](#Commands).
+
+1. Redirect traffic: (choose one)
+ - mitmdump: `mitmdump -s proxy.py -k`
+
+ Trust CA certificate:
+
+ **Note:**The CA certificate is usually stored in `% USERPROFILE%\ .mitmproxy`, or you can download it from `http://mitm.it`
+
+ Double click for [install](https://docs.microsoft.com/en-us/skype-sdk/sdn/articles/installing-the-trusted-root-certificate#installing-a-trusted-root-certificate) or ...
+
+ - Via command line
+
+ ```shell
+ certutil -addstore root %USERPROFILE%\.mitmproxy\mitmproxy-ca-cert.cer
+ ```
+
+ - Fiddler Classic: Run Fiddler Classic, turn on `Decrypt https traffic` in setting and change the default port there (Tools -> Options -> Connections) to anything other than `8888`, and load [this script](https://github.lunatic.moe/fiddlerscript).
+
+ - [Hosts file](https://github.com/Melledy/Grasscutter/wiki/Running#traffic-route-map)
+
2. Set network proxy to `127.0.0.1:8080` or the proxy port you specified.
-4. *yoink*
-* or you can use `run.cmd` to start Server & Proxy daemon with one click
+**you can also use `start.cmd` to start servers and proxy daemons automatically**
-# Grasscutter commands
-There is a dummy user named "Server" in every player's friends list that you can message to use commands. Commands also work in other chat rooms, such as private/team chats.
+### Building
-`account create [username] {playerid}` - Creates an account with the specified username and the in-game uid for that account. The playerid parameter is optional and will be auto generated if not set.
+Grasscutter uses Gradle to handle dependencies & building.
-`spawn [monster id] [level] [amount]`
+**Requirements:**
-`give [item id] [amount]`
+- Java SE Development Kits - 16
+- Git
-`givechar [avatar id] [level]`
+##### Windows
-`drop [item id] [amount]`
+```shell
+git clone https://github.com/Grasscutters/Grasscutter.git
+cd Grasscutter
+.\gradlew.bat # Setting up environments
+.\gradlew jar # Compile
+```
-`killall`
+##### Linux
-`setworldlevel [level]` - Relog to see effects properly
+```bash
+git clone https://github.com/Grasscutters/Grasscutter.git
+cd Grasscutter
+chmod +x gradlew
+./gradlew jar # Compile
+```
-`godmode` - Prevents you from taking damage
+You can find the output jar in the root of the project folder
-`resetconst` - Resets the constellation level on your current active character, will need to relog after using the command to see any changes.
+## Commands
-`setstats [stats] [amount]` - Changes the current character's specified stat.
+You might want to use this command (`java -jar grasscutter.jar -handbook`) in a cmd that is in the grasscutter folder. It will create a handbook file (GM Handbook.txt) where you can find the item IDs for stuff you want
-`clearartifacts` - Deletes all unequipped and unlocked level 0 artifacts, **including yellow rarity ones** from your inventory
+There is a dummy user named "Server" in every player's friends list that you can message to use commands. Commands also work in other chat rooms, such as private/team chats. to run commands ingame, you need to add prefix `/` or `!` such as `/pos`
-`pos` - Gets your current coordinate.
-
-`weather [weather id] [climate id]` - Changes the current weather.
-
-*More commands will be updated in the [wiki](https://github.com/Melledy/Grasscutter/wiki/).*
+| Commands | Usage | Permission node | Availability | description | Alias |
+| -------------- | ------------------------------------------------- | ------------------------- | ------------ | ------------------------------------------------------------ | ----------------------------------------------- |
+| account | account [uid] | | Server only | Creates an account with the specified username and the in-game uid for that account. The uid will be auto generated if not set. | |
+| broadcast | broadcast | server.broadcast | Both side | Sends a message to all the players. | b |
+| changescene | changescene | player.changescene | Client only | Switch scenes by scene ID. | scene |
+| clearartifacts | clearartifacts | player.clearartifacts | Client only | Deletes all unequipped and unlocked level 0 artifacts, including yellow rarity ones from your inventory. | clearart |
+| clearweapons | clearweapons | player.clearweapons | Client only | Deletes all unequipped and unlocked weapons, including yellow rarity ones from your inventory. | clearwpns |
+| drop | drop [amount] | server.drop | Client only | Drops an item around you. | `d` `dropitem` |
+| give | give [player] [amount] [level] | player.give | Both side | Gives item(s) to you or the specified player. | `g` `item` `giveitem` |
+| givechar | givechar [level] | player.givechar | Both side | Gives the player a specified character. | givec |
+| godmode | godmode [uid] | player.godmode | Client only | Prevents you from taking damage. | |
+| heal | heal | player.heal | Client only | Heal all characters in your current team. | h |
+| help | help [command] | | Both side | Sends the help message or shows information about a specified command. | |
+| kick | kick | server.kick | Both side | Kicks the specified player from the server. (WIP) | k |
+| killall | killall [playerUid] [sceneId] | server.killall | Both side | Kill all entities in the current scene or specified scene of the corresponding player. | |
+| list | list | | Both side | List online players. | |
+| permission | permission | * | Both side | Grants or removes a permission for a user. | |
+| position | position | | Client only | Get coordinates. | pos |
+| reload | reload | server.reload | Both side | Reload server config | |
+| resetconst | resetconst [all] | player.resetconstellation | Client only | Resets the constellation level on your current active character, will need to relog after using the command to see any changes. | resetconstellation |
+| restart | | | Both side | Restarts the current session | |
+| say | say | server.sendmessage | Both side | Sends a message to a player as the server | `sendservmsg` `sendservermessage` `sendmessage` |
+| setfetterlevel | setfetterlevel | player.setfetterlevel | Client only | Sets your fetter level for your current active character | setfetterlvl |
+| setstats | setstats | player.setstats | Client only | Set fight property for your current active character | stats |
+| setworldlevel | setworldlevel | player.setworldlevel | Client only | Sets your world level (Relog to see proper effects) | setworldlvl |
+| spawn | spanw [level] [amount] | server.spawn | Client only | Spawns an entity near you | |
+| stop | stop | server.stop | Both side | Stops the server | |
+| talent | talent | player.settalent | Client only | Set talent level for your current active character | |
+| teleport | teleport | player.teleport | Client only | Change the player's position. | tp |
+| weather | weather | player.weather | Client only | Changes the weather | w |
### Bonus
-When you want to teleport to somewhere, use the ingame marking function on Map, click Confirm. You will see your character falling from a very high destination, exact location that you marked.
+
+When you want to teleport to somewhere, use the ingame marking function on Map, click Confirm. You will see your
+character falling from a very high destination, exact location that you marked.
# Quick Troubleshooting
-* If compiling wasn't successful, please check your JDK installation (must be JDK 8 and validated JDK's bin PATH variable)
-* My client doesn't connect, doesn't login, 4206, etc... - Mostly your proxy daemon setup is *the issue*, if using Fiddler make sure it running on another port except 8888
+
+* If compiling wasn't successful, please check your JDK installation (JDK 16 and validated JDK's bin PATH variable)
+* My client doesn't connect, doesn't login, 4206, etc... - Mostly your proxy daemon setup is *the issue*, if using
+ Fiddler make sure it running on another port except 8888
* Startup sequence: Mongodb > Grasscutter > Proxy daemon (mitmdump, fiddler, etc.) > Client
diff --git a/README_zh-CN.md b/README_zh-CN.md
new file mode 100644
index 000000000..075bc11a1
--- /dev/null
+++ b/README_zh-CN.md
@@ -0,0 +1,147 @@
+![Grasscutter](https://socialify.git.ci/Grasscutters/Grasscutter/image?description=1&forks=1&issues=1&language=1&logo=https%3A%2F%2Fs2.loli.net%2F2022%2F04%2F25%2FxOiJn7lCdcT5Mw1.png&name=1&owner=1&pulls=1&stargazers=1&theme=Light)
+
+
+
+
+[EN](README.md) | 中文
+
+**注意:** 我们一直欢迎您成为该项目的贡献者。在添加您的代码之前,请仔细阅读我们的 [代码规范](https://github.com/Grasscutters/Grasscutter/blob/stable/CONTRIBUTING.md).
+
+## 当前特性
+
+* 登录
+* 战斗
+* 好友列表
+* 传送系统
+* 祈愿系统
+* 从控制台生成魔物
+* 多人游戏 *部分* 可用
+* 物品栏相关 (接收物品/角色, 升级角色/武器等)
+
+## 快速设置指南
+
+**附:** 加入我们的 [Discord](https://discord.gg/T5vZU6UyeG) 获取更多帮助!
+
+### 环境需求
+
+* Java SE - 16 (当您没有Oracle账户,可以使用[镜像](https://github.com/adoptium/temurin16-binaries/releases/tag/jdk-16.0.2+7))
+
+ **注:** 如果您仅仅想要简单地**运行服务端**, 那么使用 **jre** 便足够了
+
+* MongoDB (推荐 4.0+)
+
+* Proxy daemon: mitmproxy (推荐使用mitmdump), Fiddler Classic, 等
+
+### 运行
+
+**注:** 如果您从旧版本升级到新版本,最好删除 `config.json` 并启动服务端jar来重新生成它
+
+1. 获取 `grasscutter.jar`
+ - 从 [actions](https://nightly.link/Grasscutters/Grasscutter/workflows/build/stable/Grasscutter.zip) 中下载
+ - [自行构建](#构建)
+2. 在**grasscutter.jar** 所在目录中创建 `resources` 文件夹并将 `BinOutput` 和 `ExcelBinOutput` 放入其中 *(查看 [wiki](https://github.com/Grasscutters/Grasscutter/wiki) 了解更多)*
+3. 通过命令 `java -jar grasscutter.jar` 来运行Grasscutter. **在此之前请确认MongoDB服务运行正常**
+
+### 连接
+
+½. 在服务器控制台中 [创建账户](#命令列表).
+
+1. 重定向流量: (选其一)
+ - mitmdump: `mitmdump -s proxy.py -k`
+
+ 信任 CA 证书:
+
+ **注:** mitmproxy的CA证书通常存放在 `% USERPROFILE%\ .mitmproxy`, 或者你也可以从`http://mitm.it` 中下载它
+
+ 双击来[安装根证书](https://docs.microsoft.com/en-us/skype-sdk/sdn/articles/installing-the-trusted-root-certificate#installing-a-trusted-root-certificate) 或者..
+
+ - 使用命令行
+
+ ```shell
+ certutil -addstore root %USERPROFILE%\.mitmproxy\mitmproxy-ca-cert.cer
+ ```
+
+ - Fiddler Classic: 运行Fiddler Classic, 在设置中开启 `解密https通信` 并将端口切换到除`8888` 以外的任意端口 (工具 -> 选项 -> 连接) 并加载 [此脚本](https://github.lunatic.moe/fiddlerscript).
+
+ - [Hosts文件](https://github.com/Grasscutters/Grasscutter/wiki/Running#traffic-route-map)
+
+2. 设置代理为 `127.0.0.1:8080` 或其它你所设定的端口
+
+**你也可以简单地运行 `start.cmd` 来全自动启动服务端并设置代理**
+
+### 构建
+
+Grasscutter 使用 Gradle 来处理依赖及构建.
+
+**依赖:**
+
+- Java SE Development Kits - 16
+- Git
+
+##### Windows
+
+```shell
+git clone https://github.com/Grasscutters/Grasscutter.git
+cd Grasscutter
+.\gradlew.bat # Setting up environments
+.\gradlew jar # Compile
+```
+
+##### Linux
+
+```bash
+git clone https://github.com/Grasscutters/Grasscutter.git
+cd Grasscutter
+chmod +x gradlew
+./gradlew jar # Compile
+```
+
+你可以在项目根目录中找到`grasscutter.jar`
+
+## 命令列表
+
+你可能需要在终端中运行 `java -jar grasscutter.jar -handbook` 它将会创建一个 `GM Handbook.txt` 以方便您查阅物品ID等
+
+在每个玩家的朋友列表中都有一个名为“服务器”的虚拟用户,你可以通过发送消息来使用命令。命令也适用于其他聊天室,例如私人/团队聊天。
+要在游戏中使用命令,需要添加 `/` 或 `!` 前缀,如 `/pos`
+
+| 命令 | 用法 | 权限节点 | 可用性 | 注释 | 别名 |
+| -------------- | -------------------------------------------- | ------------------------- | -------- | ------------------------------------------ | ----------------------------------------------- |
+| account | account <用户名> [uid] | | 仅服务端 | 通过指定用户名和uid增删账户 | |
+| broadcast | broadcast <消息内容> | server.broadcast | 均可使用 | 给所有玩家发送公告 | b |
+| changescene | changescene <场景ID> | player.changescene | 仅客户端 | 切换到指定场景 | scene |
+| clearartifacts | clearartifacts | player.clearartifacts | 仅客户端 | 删除所有未装备及未解锁的圣遗物,包括五星 | clearart |
+| clearweapons | clearweapons | player.clearweapons | 仅客户端 | 删除所有未装备及未解锁的武器,包括五星 | clearwp |
+| drop | drop <物品ID\|物品名称> [数量] | server.drop | 仅客户端 | 在指定玩家周围掉落指定物品 | `d` `dropitem` |
+| give | give [uid] <物品ID\|物品名称> [数量] [等级] | | | 给予指定玩家一定数量及等级的物品 | `g` `item` `giveitem` |
+| givechar | givechar <角色ID> [等级] | player.givechar | 均可使用 | 给予指定玩家对应角色 | givec |
+| godmode | godmode [uid] | player.godmode | 仅客户端 | 保护你不受到任何伤害(依然会被击退) | |
+| heal | heal | player.heal | 仅客户端 | 治疗队伍中所有角色 | h |
+| help | help [命令] | | 均可使用 | 显示帮助或展示指定命令的帮助 | |
+| kick | kick | server.kick | 均可使用 | 从服务器中踢出指定玩家 (WIP) | k |
+| killall | killall [uid] [场景ID] | server.killall | 均可使用 | 杀死指定玩家世界中所在或指定场景的全部生物 | |
+| list | list | | 均可使用 | 列出在线玩家 | |
+| permission | permission <用户名> <权限节点> | * | 均可使用 | 添加或移除玩家的权限 | |
+| position | position | | 仅客户端 | 获取当前坐标 | pos |
+| reload | reload | server.reload | 均可使用 | 重载服务器配置 | |
+| resetconst | resetconst [all] | player.resetconstellation | 仅客户端 | 重置当前角色的命座,重新登录即可生效 | resetconstellation |
+| restart | restart | | 均可使用 | 重启服务端 | |
+| say | say <消息> | server.sendmessage | 均可使用 | 作为服务器发送消息给玩家 | `sendservmsg` `sendservermessage` `sendmessage` |
+| setfetterlevel | setfetterlevel <好感等级> | player.setfetterlevel | 仅客户端 | 设置当前角色的好感等级 | `setfetterlvl` `setfriendship` |
+| setstats | setstats <属性> <数值> | player.setstats | 仅客户端 | 直接修改当前角色的面板 | stats |
+| setworldlevel | setworldlevel <世界等级> | player.setworldlevel | 仅客户端 | 设置世界等级(重新登陆即可生效) | setworldlvl |
+| spawn | spanw <实体ID\|实体名称> [等级] [数量] | server.spawn | 仅客户端 | 在你周围生成实体 | |
+| stop | stop | server.stop | 均可使用 | 停止服务器 | |
+| talent | talent <天赋ID> <等级> | player.settalent | 仅客户端 | 设置当前角色的天赋等级 | |
+| teleport | teleport | player.teleport | 仅客户端 | 传送玩家到指定坐标 | tp |
+| weather | weather <天气ID> <气候ID> | player.weather | 仅客户端 | 改变天气 | w |
+
+### 额外功能
+
+当你想传送到某个地点, 只需要在地图中创建标记, 关闭地图后即可到达目标地点上空
+
+# 快速排除问题
+
+* 如果编译未能成功,请检查您的jdk安装 (JDK 16并确认jdk处于环境变量`PATH`中
+* 我的客户端无法登录/连接, 4206, 其它... - 大部分情况下这是因为您的代理存在问题.如果使用Fiddler请确认Fiddler监听端口不是`8888`
+* 启动顺序: MongoDB > Grasscutter > 代理程序 (mitmdump, fiddler等.) > 客户端
diff --git a/build.gradle b/build.gradle
index dcc109d0e..c32d56e1f 100644
--- a/build.gradle
+++ b/build.gradle
@@ -25,12 +25,12 @@ dependencies {
implementation fileTree(dir: 'lib', include: ['*.jar'])
implementation group: 'org.slf4j', name: 'slf4j-api', version: '1.7.32'
- implementation group: 'ch.qos.logback', name: 'logback-core', version: '1.2.6'
- implementation group: 'ch.qos.logback', name: 'logback-classic', version: '1.2.6'
- implementation group: 'io.netty', name: 'netty-all', version: '4.1.69.Final'
+ implementation group: 'ch.qos.logback', name: 'logback-core', version: '1.2.9'
+ implementation group: 'ch.qos.logback', name: 'logback-classic', version: '1.2.9'
+ implementation group: 'io.netty', name: 'netty-all', version: '4.1.71.Final'
implementation group: 'com.google.code.gson', name: 'gson', version: '2.8.8'
- implementation group: 'com.google.protobuf', name: 'protobuf-java', version: '3.18.1'
+ implementation group: 'com.google.protobuf', name: 'protobuf-java', version: '3.18.2'
implementation group: 'org.reflections', name: 'reflections', version: '0.10.2'
diff --git a/gradlew b/gradlew
old mode 100644
new mode 100755
diff --git a/src/main/java/emu/grasscutter/Grasscutter.java b/src/main/java/emu/grasscutter/Grasscutter.java
index 8f64f0e51..8246588ae 100644
--- a/src/main/java/emu/grasscutter/Grasscutter.java
+++ b/src/main/java/emu/grasscutter/Grasscutter.java
@@ -129,6 +129,7 @@ public final class Grasscutter {
public static void startConsole() {
String input;
+ getLogger().info("Done! For help, type \"help\"");
try (BufferedReader br = new BufferedReader(new InputStreamReader(System.in))) {
while ((input = br.readLine()) != null) {
try {
diff --git a/src/main/java/emu/grasscutter/command/commands/ClearArtifactsCommand.java b/src/main/java/emu/grasscutter/command/commands/ClearArtifactsCommand.java
deleted file mode 100644
index 258e1e1d5..000000000
--- a/src/main/java/emu/grasscutter/command/commands/ClearArtifactsCommand.java
+++ /dev/null
@@ -1,30 +0,0 @@
-package emu.grasscutter.command.commands;
-
-import emu.grasscutter.command.Command;
-import emu.grasscutter.command.CommandHandler;
-import emu.grasscutter.game.GenshinPlayer;
-import emu.grasscutter.game.inventory.Inventory;
-import emu.grasscutter.game.inventory.ItemType;
-
-import java.util.List;
-
-@Command(label = "clearartifacts", usage = "clearartifacts",
- description = "Deletes all unequipped and unlocked level 0 artifacts, including yellow rarity ones from your inventory",
- aliases = {"clearart"}, permission = "player.clearartifacts")
-public final class ClearArtifactsCommand implements CommandHandler {
-
- @Override
- public void execute(GenshinPlayer sender, List args) {
- if (sender == null) {
- CommandHandler.sendMessage(null, "Run this command in-game.");
- return; // TODO: clear player's artifacts from console or other players
- }
-
- Inventory playerInventory = sender.getInventory();
- playerInventory.getItems().values().stream()
- .filter(item -> item.getItemType() == ItemType.ITEM_RELIQUARY)
- .filter(item -> item.getLevel() == 1 && item.getExp() == 0)
- .filter(item -> !item.isLocked() && !item.isEquipped())
- .forEach(item -> playerInventory.removeItem(item, item.getCount()));
- }
-}
diff --git a/src/main/java/emu/grasscutter/command/commands/ClearCommand.java b/src/main/java/emu/grasscutter/command/commands/ClearCommand.java
new file mode 100644
index 000000000..8d16e58c1
--- /dev/null
+++ b/src/main/java/emu/grasscutter/command/commands/ClearCommand.java
@@ -0,0 +1,106 @@
+package emu.grasscutter.command.commands;
+
+import emu.grasscutter.Grasscutter;
+import emu.grasscutter.command.Command;
+import emu.grasscutter.command.CommandHandler;
+import emu.grasscutter.data.GenshinData;
+import emu.grasscutter.data.def.ItemData;
+import emu.grasscutter.game.GenshinPlayer;
+import emu.grasscutter.game.inventory.GenshinItem;
+import emu.grasscutter.game.inventory.Inventory;
+import emu.grasscutter.game.inventory.ItemType;
+
+import java.util.Collection;
+import java.util.LinkedList;
+import java.util.List;
+
+@Command(label = "clear", usage = "clear ", //Merged /clearartifacts and /clearweapons to /clear [uid]
+ description = "Deletes unequipped unlocked items, including yellow rarity ones from your inventory",
+ aliases = {"clear"}, permission = "player.clearinv")
+
+public final class ClearCommand implements CommandHandler {
+
+ @Override
+ public void execute(GenshinPlayer sender, List args) {
+ int target;
+ if (sender == null) {
+ CommandHandler.sendMessage(null, "Run this command in-game.");
+ return;
+ }
+ String cmdSwitch = args.get(1);
+
+ Inventory playerInventory = sender.getInventory();
+ try {
+ target = Integer.parseInt(args.get(0));
+ GenshinPlayer targetPlayer = Grasscutter.getGameServer().getPlayerByUid(target);
+ if (targetPlayer == null && sender != null) {
+ target = sender.getUid();
+ } else {
+ switch (cmdSwitch){
+ case "wp":
+ playerInventory.getItems().values().stream()
+ .filter(item -> item.getItemType() == ItemType.ITEM_WEAPON)
+ .filter(item -> !item.isLocked() && !item.isEquipped())
+ .forEach(item -> playerInventory.removeItem(item, item.getCount()));
+ sender.dropMessage("Cleared weapons for " + targetPlayer.getNickname() + " .");
+ break;
+ case "art":
+ playerInventory.getItems().values().stream()
+ .filter(item -> item.getItemType() == ItemType.ITEM_RELIQUARY)
+ .filter(item -> item.getLevel() == 1 && item.getExp() == 0)
+ .filter(item -> !item.isLocked() && !item.isEquipped())
+ .forEach(item -> playerInventory.removeItem(item, item.getCount()));
+ sender.dropMessage("Cleared artifacts for " + targetPlayer.getNickname() + " .");
+ break;
+ case "mat":
+ playerInventory.getItems().values().stream()
+ .filter(item -> item.getItemType() == ItemType.ITEM_MATERIAL)
+ .filter(item -> item.getLevel() == 1 && item.getExp() == 0)
+ .filter(item -> !item.isLocked() && !item.isEquipped())
+ .forEach(item -> playerInventory.removeItem(item, item.getCount()));
+ sender.dropMessage("Cleared artifacts for " + targetPlayer.getNickname() + " .");
+ break;
+ case "all":
+ playerInventory.getItems().values().stream()
+ .filter(item1 -> item1.getItemType() == ItemType.ITEM_RELIQUARY)
+ .filter(item1 -> item1.getLevel() == 1 && item1.getExp() == 0)
+ .filter(item1 -> !item1.isLocked() && !item1.isEquipped())
+ .forEach(item1 -> playerInventory.removeItem(item1, item1.getCount()));
+ playerInventory.getItems().values().stream()
+ .filter(item2 -> item2.getItemType() == ItemType.ITEM_MATERIAL)
+ .filter(item2 -> !item2.isLocked() && !item2.isEquipped())
+ .forEach(item2 -> playerInventory.removeItem(item2, item2.getCount()));
+ playerInventory.getItems().values().stream()
+ .filter(item3 -> item3.getItemType() == ItemType.ITEM_WEAPON)
+ .filter(item3 -> item3.getLevel() == 1 && item3.getExp() == 0)
+ .filter(item3 -> !item3.isLocked() && !item3.isEquipped())
+ .forEach(item3 -> playerInventory.removeItem(item3, item3.getCount()));
+ playerInventory.getItems().values().stream()
+ .filter(item4 -> item4.getItemType() == ItemType.ITEM_FURNITURE)
+ .filter(item4 -> !item4.isLocked() && !item4.isEquipped())
+ .forEach(item4 -> playerInventory.removeItem(item4, item4.getCount()));
+ playerInventory.getItems().values().stream()
+ .filter(item5 -> item5.getItemType() == ItemType.ITEM_DISPLAY)
+ .filter(item5 -> !item5.isLocked() && !item5.isEquipped())
+ .forEach(item5 -> playerInventory.removeItem(item5, item5.getCount()));
+ playerInventory.getItems().values().stream()
+ .filter(item6 -> item6.getItemType() == ItemType.ITEM_VIRTUAL)
+ .filter(item6 -> !item6.isLocked() && !item6.isEquipped())
+ .forEach(item6 -> playerInventory.removeItem(item6, item6.getCount()));
+ sender.dropMessage("Cleared everything for " + targetPlayer.getNickname() + " .");
+ break;
+ }
+ }
+ } catch (NumberFormatException ignored) {
+ // TODO: Parse from item name using GM Handbook.
+ CommandHandler.sendMessage(sender, "Invalid playerId.");
+ return;
+ }
+
+ GenshinPlayer targetPlayer = Grasscutter.getGameServer().getPlayerByUid(target);
+ if (targetPlayer == null) {
+ CommandHandler.sendMessage(sender, "Player not found.");
+ return;
+ }
+ }
+}
diff --git a/src/main/java/emu/grasscutter/command/commands/ClearWeaponsCommand.java b/src/main/java/emu/grasscutter/command/commands/ClearWeaponsCommand.java
deleted file mode 100644
index 8fab1768b..000000000
--- a/src/main/java/emu/grasscutter/command/commands/ClearWeaponsCommand.java
+++ /dev/null
@@ -1,29 +0,0 @@
-package emu.grasscutter.command.commands;
-
-import emu.grasscutter.command.Command;
-import emu.grasscutter.command.CommandHandler;
-import emu.grasscutter.game.GenshinPlayer;
-import emu.grasscutter.game.inventory.Inventory;
-import emu.grasscutter.game.inventory.ItemType;
-
-import java.util.List;
-
-@Command(label = "clearweapons", usage = "clearweapons",
- description = "Deletes all unequipped and unlocked weapons, including yellow rarity ones from your inventory",
- aliases = {"clearwpns"}, permission = "player.clearweapons")
-public final class ClearWeaponsCommand implements CommandHandler {
-
- @Override
- public void execute(GenshinPlayer sender, List args) {
- if (sender == null) {
- CommandHandler.sendMessage(null, "Run this command in-game.");
- return; // TODO: clear player's weapons from console or other players
- }
-
- Inventory playerInventory = sender.getInventory();
- playerInventory.getItems().values().stream()
- .filter(item -> item.getItemType() == ItemType.ITEM_WEAPON)
- .filter(item -> !item.isLocked() && !item.isEquipped())
- .forEach(item -> playerInventory.removeItem(item, item.getCount()));
- }
-}
diff --git a/src/main/java/emu/grasscutter/command/commands/GiveCommand.java b/src/main/java/emu/grasscutter/command/commands/GiveCommand.java
index a533130bc..2ecdac1f5 100644
--- a/src/main/java/emu/grasscutter/command/commands/GiveCommand.java
+++ b/src/main/java/emu/grasscutter/command/commands/GiveCommand.java
@@ -13,16 +13,15 @@ import emu.grasscutter.server.packet.send.PacketItemAddHintNotify;
import java.util.LinkedList;
import java.util.List;
-@Command(label = "give", usage = "give [player] [amount]",
- description = "Gives an item to you or the specified player", aliases = {"g", "item", "giveitem"}, permission = "player.give")
+@Command(label = "give", usage = "give [player] [amount] [level]", description = "Gives an item to you or the specified player", aliases = {
+ "g", "item", "giveitem" }, permission = "player.give")
public final class GiveCommand implements CommandHandler {
@Override
public void execute(GenshinPlayer sender, List args) {
- int target, item, amount = 1;
-
+ int target, item, lvl, amount = 1;
if (sender == null && args.size() < 2) {
- CommandHandler.sendMessage(null, "Usage: give [amount]");
+ CommandHandler.sendMessage(null, "Usage: give [amount] [level]");
return;
}
@@ -34,6 +33,7 @@ public final class GiveCommand implements CommandHandler {
try {
item = Integer.parseInt(args.get(0));
target = sender.getUid();
+ lvl = 1;
} catch (NumberFormatException ignored) {
// TODO: Parse from item name using GM Handbook.
CommandHandler.sendMessage(sender, "Invalid item id.");
@@ -43,6 +43,7 @@ public final class GiveCommand implements CommandHandler {
case 2: // [amount] | [player]
try {
target = Integer.parseInt(args.get(0));
+ lvl = 1;
if (Grasscutter.getGameServer().getPlayerByUid(target) == null && sender != null) {
target = sender.getUid();
@@ -57,17 +58,39 @@ public final class GiveCommand implements CommandHandler {
return;
}
break;
- case 3: // [player] [amount]
+ case 3: // [player] [amount] | [amount] [level]
+ try {
+ target = Integer.parseInt(args.get(0));
+
+ if (Grasscutter.getGameServer().getPlayerByUid(target) == null && sender != null) {
+ target = sender.getUid();
+ item = Integer.parseInt(args.get(0));
+ amount = Integer.parseInt(args.get(1));
+ lvl = Integer.parseInt(args.get(2));
+ } else {
+ item = Integer.parseInt(args.get(1));
+ amount = Integer.parseInt(args.get(2));
+ lvl = 1;
+ }
+
+ } catch (NumberFormatException ignored) {
+ // TODO: Parse from item name using GM Handbook.
+ CommandHandler.sendMessage(sender, "Invalid item or player ID.");
+ return;
+ }
+ break;
+ case 4: // [player] [amount] [level]
try {
target = Integer.parseInt(args.get(0));
if (Grasscutter.getGameServer().getPlayerByUid(target) == null) {
CommandHandler.sendMessage(sender, "Invalid player ID.");
return;
+ } else {
+ item = Integer.parseInt(args.get(1));
+ amount = Integer.parseInt(args.get(2));
+ lvl = Integer.parseInt(args.get(3));
}
-
- item = Integer.parseInt(args.get(1));
- amount = Integer.parseInt(args.get(2));
} catch (NumberFormatException ignored) {
// TODO: Parse from item name using GM Handbook.
CommandHandler.sendMessage(sender, "Invalid item or player ID.");
@@ -89,16 +112,37 @@ public final class GiveCommand implements CommandHandler {
return;
}
- this.item(targetPlayer, itemData, amount);
+ this.item(targetPlayer, itemData, amount, lvl);
- CommandHandler.sendMessage(sender, String.format("Given %s of %s to %s.", amount, item, target));
+ if (!itemData.isEquip())
+ CommandHandler.sendMessage(sender, String.format("Given %s of %s to %s.", amount, item, target));
+ else
+ CommandHandler.sendMessage(sender,
+ String.format("Given %s with level %s %s times to %s", item, lvl, amount, target));
}
- private void item(GenshinPlayer player, ItemData itemData, int amount) {
+ private void item(GenshinPlayer player, ItemData itemData, int amount, int lvl) {
if (itemData.isEquip()) {
List items = new LinkedList<>();
for (int i = 0; i < amount; i++) {
- items.add(new GenshinItem(itemData));
+ GenshinItem item = new GenshinItem(itemData);
+ item.setCount(amount);
+ item.setLevel(lvl);
+ item.setPromoteLevel(0);
+ if (lvl > 20) { // 20/40
+ item.setPromoteLevel(1);
+ } else if (lvl > 40) { // 40/50
+ item.setPromoteLevel(2);
+ } else if (lvl > 50) { // 50/60
+ item.setPromoteLevel(3);
+ } else if (lvl > 60) { // 60/70
+ item.setPromoteLevel(4);
+ } else if (lvl > 70) { // 70/80
+ item.setPromoteLevel(5);
+ } else if (lvl > 80) { // 80/90
+ item.setPromoteLevel(6);
+ }
+ items.add(item);
}
player.getInventory().addItems(items);
player.sendPacket(new PacketItemAddHintNotify(items, ActionReason.SubfieldDrop));
@@ -110,4 +154,3 @@ public final class GiveCommand implements CommandHandler {
}
}
}
-
diff --git a/src/main/java/emu/grasscutter/command/commands/GodModeCommand.java b/src/main/java/emu/grasscutter/command/commands/GodModeCommand.java
index e5635dc9f..0b4139ac1 100644
--- a/src/main/java/emu/grasscutter/command/commands/GodModeCommand.java
+++ b/src/main/java/emu/grasscutter/command/commands/GodModeCommand.java
@@ -1,5 +1,6 @@
package emu.grasscutter.command.commands;
+import emu.grasscutter.Grasscutter;
import emu.grasscutter.command.Command;
import emu.grasscutter.command.CommandHandler;
import emu.grasscutter.game.GenshinPlayer;
@@ -16,7 +17,29 @@ public final class GodModeCommand implements CommandHandler {
CommandHandler.sendMessage(null, "Run this command in-game.");
return; // TODO: toggle player's godmode statue from console or other players
}
- sender.setGodmode(!sender.inGodmode());
- sender.dropMessage("Godmode is now " + (sender.inGodmode() ? "enabled" : "disabled") + ".");
+
+ int target;
+ if (args.size() == 1) {
+ try {
+ target = Integer.parseInt(args.get(0));
+ if (Grasscutter.getGameServer().getPlayerByUid(target) == null) {
+ target = sender.getUid();
+ }
+ } catch (NumberFormatException e) {
+ CommandHandler.sendMessage(sender, "Invalid player id.");
+ return;
+ }
+ } else {
+ target = sender.getUid();
+ }
+ GenshinPlayer targetPlayer = Grasscutter.getGameServer().getPlayerByUid(target);
+ if (targetPlayer == null) {
+ CommandHandler.sendMessage(sender, "Player not found.");
+ return;
+ }
+
+ targetPlayer.setGodmode(!targetPlayer.inGodmode());
+ sender.dropMessage("Godmode is now " + (targetPlayer.inGodmode() ? "enabled" : "disabled") +
+ "for " + targetPlayer.getNickname() + " .");
}
}
diff --git a/src/main/java/emu/grasscutter/command/commands/KillCharacterCommand.java b/src/main/java/emu/grasscutter/command/commands/KillCharacterCommand.java
new file mode 100644
index 000000000..beaa4f959
--- /dev/null
+++ b/src/main/java/emu/grasscutter/command/commands/KillCharacterCommand.java
@@ -0,0 +1,68 @@
+package emu.grasscutter.command.commands;
+
+import emu.grasscutter.Grasscutter;
+import emu.grasscutter.command.Command;
+import emu.grasscutter.command.CommandHandler;
+import emu.grasscutter.game.GenshinPlayer;
+import emu.grasscutter.game.entity.EntityAvatar;
+import emu.grasscutter.game.props.FightProperty;
+import emu.grasscutter.game.props.LifeState;
+import emu.grasscutter.server.packet.send.PacketEntityFightPropUpdateNotify;
+import emu.grasscutter.server.packet.send.PacketLifeStateChangeNotify;
+
+import java.util.List;
+
+@Command(label = "killcharacter", usage = "killcharacter [playerId]", aliases = {"suicide", "kill"},
+ description = "Kills the players current character", permission = "player.killcharacter")
+public final class KillCharacterCommand implements CommandHandler {
+
+ @Override
+ public void execute(GenshinPlayer sender, List args) {
+ int target;
+ if (sender == null) {
+ // from console
+ if (args.size() == 1) {
+ try {
+ target = Integer.parseInt(args.get(0));
+ } catch (NumberFormatException e) {
+ CommandHandler.sendMessage(null, "Invalid player id.");
+ return;
+ }
+ } else {
+ CommandHandler.sendMessage(null, "Usage: /killcharacter [playerId]");
+ return;
+ }
+ } else {
+ if (args.size() == 1) {
+ try {
+ target = Integer.parseInt(args.get(0));
+ if (Grasscutter.getGameServer().getPlayerByUid(target) == null) {
+ target = sender.getUid();
+ }
+ } catch (NumberFormatException e) {
+ CommandHandler.sendMessage(sender, "Invalid player id.");
+ return;
+ }
+ } else {
+ target = sender.getUid();
+ }
+ }
+
+ GenshinPlayer targetPlayer = Grasscutter.getGameServer().getPlayerByUid(target);
+ if (targetPlayer == null) {
+ CommandHandler.sendMessage(sender, "Player not found or offline.");
+ return;
+ }
+
+ EntityAvatar entity = targetPlayer.getTeamManager().getCurrentAvatarEntity();
+ entity.setFightProperty(FightProperty.FIGHT_PROP_CUR_HP, 0f);
+ // Packets
+ entity.getWorld().broadcastPacket(new PacketEntityFightPropUpdateNotify(entity, FightProperty.FIGHT_PROP_CUR_HP));
+ entity.getWorld().broadcastPacket(new PacketLifeStateChangeNotify(0, entity, LifeState.LIFE_DEAD));
+ // remove
+ targetPlayer.getScene().removeEntity(entity);
+ entity.onDeath(0);
+
+ CommandHandler.sendMessage(sender, "Killed " + targetPlayer.getNickname() + " current character.");
+ }
+}
diff --git a/src/main/java/emu/grasscutter/command/commands/PositionCommand.java b/src/main/java/emu/grasscutter/command/commands/PositionCommand.java
index 639301c7f..8bdf3c754 100644
--- a/src/main/java/emu/grasscutter/command/commands/PositionCommand.java
+++ b/src/main/java/emu/grasscutter/command/commands/PositionCommand.java
@@ -17,6 +17,7 @@ public final class PositionCommand implements CommandHandler {
return;
}
- sender.dropMessage(String.format("Coord: %.3f, %.3f, %.3f", sender.getPos().getX(), sender.getPos().getY(), sender.getPos().getZ()));
+ sender.dropMessage(String.format("Coord: %.3f, %.3f, %.3f\nScene id: %d",
+ sender.getPos().getX(), sender.getPos().getY(), sender.getPos().getZ(), sender.getSceneId()));
}
}
diff --git a/src/main/java/emu/grasscutter/command/commands/SetFetterLevelCommand.java b/src/main/java/emu/grasscutter/command/commands/SetFetterLevelCommand.java
new file mode 100644
index 000000000..676a2b279
--- /dev/null
+++ b/src/main/java/emu/grasscutter/command/commands/SetFetterLevelCommand.java
@@ -0,0 +1,50 @@
+package emu.grasscutter.command.commands;
+
+import java.util.List;
+
+import emu.grasscutter.command.Command;
+import emu.grasscutter.command.CommandHandler;
+import emu.grasscutter.data.GenshinData;
+import emu.grasscutter.game.GenshinPlayer;
+import emu.grasscutter.game.avatar.GenshinAvatar;
+import emu.grasscutter.server.packet.send.PacketAvatarFetterDataNotify;
+
+@Command(label = "setfetterlevel", usage = "setfetterlevel ",
+ description = "Sets your fetter level for your current active character",
+ aliases = {"setfetterlvl", "setfriendship"}, permission = "player.setfetterlevel")
+public final class SetFetterLevelCommand implements CommandHandler {
+
+ @Override
+ public void execute(GenshinPlayer sender, List args) {
+ if (sender == null) {
+ CommandHandler.sendMessage(null, "Run this command in-game.");
+ return;
+ }
+
+ if (args.size() < 1) {
+ CommandHandler.sendMessage(sender, "Usage: setfetterlevel ");
+ return;
+ }
+
+ try {
+ int fetterLevel = Integer.parseInt(args.get(0));
+ if (fetterLevel < 0 || fetterLevel > 10) {
+ CommandHandler.sendMessage(sender, "Fetter level must be between 0 and 10.");
+ return;
+ }
+ GenshinAvatar avatar = sender.getTeamManager().getCurrentAvatarEntity().getAvatar();
+
+ avatar.setFetterLevel(fetterLevel);
+ if (fetterLevel != 10) {
+ avatar.setFetterExp(GenshinData.getAvatarFetterLevelDataMap().get(fetterLevel).getExp());
+ }
+ avatar.save();
+
+ sender.sendPacket(new PacketAvatarFetterDataNotify(avatar));
+ CommandHandler.sendMessage(sender, "Fetter level set to " + fetterLevel);
+ } catch (NumberFormatException ignored) {
+ CommandHandler.sendMessage(null, "Invalid fetter level.");
+ }
+ }
+
+}
diff --git a/src/main/java/emu/grasscutter/command/commands/SetStatsCommand.java b/src/main/java/emu/grasscutter/command/commands/SetStatsCommand.java
index e3efdb0d5..d7d27a24a 100644
--- a/src/main/java/emu/grasscutter/command/commands/SetStatsCommand.java
+++ b/src/main/java/emu/grasscutter/command/commands/SetStatsCommand.java
@@ -197,8 +197,8 @@ public final class SetStatsCommand implements CommandHandler {
float eelec = Integer.parseInt(args.get(1));
EntityAvatar entity = sender.getTeamManager().getCurrentAvatarEntity();
float elec = eelec / 10000;
- entity.setFightProperty(FightProperty.FIGHT_PROP_CRITICAL_HURT, elec);
- entity.getWorld().broadcastPacket(new PacketEntityFightPropUpdateNotify(entity, FightProperty.FIGHT_PROP_CRITICAL_HURT));
+ entity.setFightProperty(FightProperty.FIGHT_PROP_ELEC_ADD_HURT, elec);
+ entity.getWorld().broadcastPacket(new PacketEntityFightPropUpdateNotify(entity, FightProperty.FIGHT_PROP_ELEC_ADD_HURT));
float igelec = elec * 100;
CommandHandler.sendMessage(sender, "Electro DMG Bonus set to " + igelec + "%");
} catch (NumberFormatException ignored) {
diff --git a/src/main/java/emu/grasscutter/command/commands/TalentCommand.java b/src/main/java/emu/grasscutter/command/commands/TalentCommand.java
index 2ced6f1c3..21cf66249 100644
--- a/src/main/java/emu/grasscutter/command/commands/TalentCommand.java
+++ b/src/main/java/emu/grasscutter/command/commands/TalentCommand.java
@@ -2,6 +2,7 @@ package emu.grasscutter.command.commands;
import emu.grasscutter.command.Command;
import emu.grasscutter.command.CommandHandler;
+import emu.grasscutter.data.def.AvatarSkillDepotData;
import emu.grasscutter.game.GenshinPlayer;
import emu.grasscutter.game.avatar.GenshinAvatar;
import emu.grasscutter.game.entity.EntityAvatar;
@@ -21,8 +22,9 @@ public class TalentCommand implements CommandHandler {
return;
}
- if (args.size() < 0 || args.size() < 1){
+ if (args.size() < 1){
CommandHandler.sendMessage(sender, "To set talent level: /talent set ");
+ CommandHandler.sendMessage(sender, "Another way to set talent level: /talent ");
CommandHandler.sendMessage(sender, "To get talent ID: /talent getid");
return;
}
@@ -31,6 +33,7 @@ public class TalentCommand implements CommandHandler {
switch (cmdSwitch) {
default:
CommandHandler.sendMessage(sender, "To set talent level: /talent set ");
+ CommandHandler.sendMessage(sender, "Another way to set talent level: /talent ");
CommandHandler.sendMessage(sender, "To get talent ID: /talent getid");
return;
case "set":
@@ -90,6 +93,45 @@ public class TalentCommand implements CommandHandler {
return;
}
+ break;
+ case "n": case "e": case "q":
+ try {
+ EntityAvatar entity = sender.getTeamManager().getCurrentAvatarEntity();
+ GenshinAvatar avatar = entity.getAvatar();
+ AvatarSkillDepotData SkillDepot = avatar.getData().getSkillDepot();
+ int skillId;
+ switch (cmdSwitch) {
+ default:
+ skillId = SkillDepot.getSkills().get(0);
+ break;
+ case "e":
+ skillId = SkillDepot.getSkills().get(1);
+ break;
+ case "q":
+ skillId = SkillDepot.getEnergySkill();
+ break;
+ }
+ int nextLevel = Integer.parseInt(args.get(1));
+ int currentLevel = avatar.getSkillLevelMap().get(skillId);
+ if (args.size() < 1){
+ CommandHandler.sendMessage(sender, "To set talent level: /talent ");
+ return;
+ }
+ if (nextLevel > 16){
+ CommandHandler.sendMessage(sender, "Invalid talent level. Level should be lower than 16");
+ return;
+ }
+ // Upgrade skill
+ avatar.getSkillLevelMap().put(skillId, nextLevel);
+ avatar.save();
+ // Packet
+ sender.sendPacket(new PacketAvatarSkillChangeNotify(avatar, skillId, currentLevel, nextLevel));
+ sender.sendPacket(new PacketAvatarSkillUpgradeRsp(avatar, skillId, currentLevel, nextLevel));
+ CommandHandler.sendMessage(sender, "Set this talent to " + nextLevel + ".");
+ } catch (NumberFormatException ignored) {
+ CommandHandler.sendMessage(sender, "Invalid talent level.");
+ return;
+ }
break;
case "getid":
EntityAvatar entity = sender.getTeamManager().getCurrentAvatarEntity();
diff --git a/src/main/java/emu/grasscutter/command/commands/TelePortCommand.java b/src/main/java/emu/grasscutter/command/commands/TelePortCommand.java
new file mode 100644
index 000000000..84848afa5
--- /dev/null
+++ b/src/main/java/emu/grasscutter/command/commands/TelePortCommand.java
@@ -0,0 +1,70 @@
+package emu.grasscutter.command.commands;
+
+import emu.grasscutter.command.Command;
+import emu.grasscutter.command.CommandHandler;
+import emu.grasscutter.game.GenshinPlayer;
+import emu.grasscutter.utils.Position;
+
+import java.util.List;
+
+@Command(label = "teleport", usage = "teleport ", aliases = {"tp"},
+ description = "Change the player's position.", permission = "player.teleport")
+public class TelePortCommand implements CommandHandler {
+
+ @Override
+ public void execute(GenshinPlayer sender, List args) {
+ if (sender == null) {
+ CommandHandler.sendMessage(null, "Run this command in-game.");
+ return;
+ }
+
+ if (args.size() < 3){
+ CommandHandler.sendMessage(sender, "Usage: /tp [scene id]");
+ return;
+ }
+
+ try {
+ float x = 0f;
+ float y = 0f;
+ float z = 0f;
+ if (args.get(0).contains("~")) {
+ if (args.get(0).equals("~")) {
+ x = sender.getPos().getX();
+ } else {
+ x = Float.parseFloat(args.get(0).replace("~", "")) + sender.getPos().getX();
+ }
+ } else {
+ x = Float.parseFloat(args.get(0));
+ }
+ if (args.get(1).contains("~")) {
+ if (args.get(1).equals("~")) {
+ y = sender.getPos().getY();
+ } else {
+ y = Float.parseFloat(args.get(1).replace("~", "")) + sender.getPos().getY();
+ }
+ } else {
+ y = Float.parseFloat(args.get(1));
+ }
+ if (args.get(2).contains("~")) {
+ if (args.get(2).equals("~")) {
+ z = sender.getPos().getZ();
+ } else {
+ z = Float.parseFloat(args.get(2).replace("~", "")) + sender.getPos().getZ();
+ }
+ } else {
+ z = Float.parseFloat(args.get(2));
+ }
+ int sceneId = sender.getSceneId();
+ if (args.size() == 4){
+ sceneId = Integer.parseInt(args.get(3));
+ }
+ Position target = new Position(x, y, z);
+ boolean result = sender.getWorld().transferPlayerToScene(sender, sceneId, target);
+ if (!result) {
+ CommandHandler.sendMessage(sender, "Invalid position.");
+ }
+ } catch (NumberFormatException ignored) {
+ CommandHandler.sendMessage(sender, "Invalid position.");
+ }
+ }
+}
diff --git a/src/main/java/emu/grasscutter/data/GenshinData.java b/src/main/java/emu/grasscutter/data/GenshinData.java
index 7e14b1b46..3b3d351da 100644
--- a/src/main/java/emu/grasscutter/data/GenshinData.java
+++ b/src/main/java/emu/grasscutter/data/GenshinData.java
@@ -31,6 +31,7 @@ public class GenshinData {
private static final Int2ObjectMap avatarSkillDepotDataMap = new Int2ObjectOpenHashMap<>();
private static final Int2ObjectMap avatarSkillDataMap = new Int2ObjectOpenHashMap<>();
private static final Int2ObjectMap avatarCurveDataMap = new Int2ObjectLinkedOpenHashMap<>();
+ private static final Int2ObjectMap avatarFetterLevelDataMap = new Int2ObjectOpenHashMap<>();
private static final Int2ObjectMap avatarPromoteDataMap = new Int2ObjectOpenHashMap<>();
private static final Int2ObjectMap avatarTalentDataMap = new Int2ObjectOpenHashMap<>();
private static final Int2ObjectMap proudSkillDataMap = new Int2ObjectOpenHashMap<>();
@@ -57,6 +58,8 @@ public class GenshinData {
private static final Int2ObjectMap sceneDataMap = new Int2ObjectLinkedOpenHashMap<>();
private static final Int2ObjectMap fetterDataMap = new Int2ObjectOpenHashMap<>();
+ private static final Int2ObjectMap fetterCharacterCardDataMap = new Int2ObjectOpenHashMap<>();
+ private static final Int2ObjectMap rewardDataMap = new Int2ObjectOpenHashMap<>();
// Cache
private static Map> fetters = new HashMap<>();
@@ -114,6 +117,14 @@ public class GenshinData {
return playerLevelDataMap;
}
+ public static Int2ObjectMap getAvatarFetterLevelDataMap() {
+ return avatarFetterLevelDataMap;
+ }
+
+ public static Int2ObjectMap getFetterCharacterCardDataMap() {
+ return fetterCharacterCardDataMap;
+ }
+
public static Int2ObjectMap getAvatarLevelDataMap() {
return avatarLevelDataMap;
}
@@ -175,6 +186,11 @@ public class GenshinData {
AvatarLevelData levelData = avatarLevelDataMap.get(level);
return levelData != null ? levelData.getExp() : 0;
}
+
+ public static int getAvatarFetterLevelExpRequired(int level) {
+ AvatarFetterLevelData levelData = avatarFetterLevelDataMap.get(level);
+ return levelData != null ? levelData.getExp() : 0;
+ }
public static Int2ObjectMap getProudSkillDataMap() {
return proudSkillDataMap;
@@ -228,6 +244,10 @@ public class GenshinData {
return sceneDataMap;
}
+ public static Int2ObjectMap getRewardDataMap() {
+ return rewardDataMap;
+ }
+
public static Map> getFetterDataEntries() {
if (fetters.isEmpty()) {
fetterDataMap.forEach((k, v) -> {
diff --git a/src/main/java/emu/grasscutter/data/common/OpenCondData.java b/src/main/java/emu/grasscutter/data/common/OpenCondData.java
new file mode 100644
index 000000000..9e7b62b11
--- /dev/null
+++ b/src/main/java/emu/grasscutter/data/common/OpenCondData.java
@@ -0,0 +1,24 @@
+package emu.grasscutter.data.common;
+
+import java.util.List;
+
+public class OpenCondData {
+ private String CondType;
+ private List ParamList;
+
+ public String getCondType() {
+ return CondType;
+ }
+
+ public void setCondType(String condType) {
+ CondType = condType;
+ }
+
+ public List getParamList() {
+ return ParamList;
+ }
+
+ public void setParamList(List paramList) {
+ ParamList = paramList;
+ }
+}
diff --git a/src/main/java/emu/grasscutter/data/common/RewardItemData.java b/src/main/java/emu/grasscutter/data/common/RewardItemData.java
new file mode 100644
index 000000000..024b89f7f
--- /dev/null
+++ b/src/main/java/emu/grasscutter/data/common/RewardItemData.java
@@ -0,0 +1,22 @@
+package emu.grasscutter.data.common;
+
+public class RewardItemData {
+ private int ItemId;
+ private int ItemCount;
+
+ public int getItemId() {
+ return ItemId;
+ }
+
+ public void setItemId(int itemId) {
+ ItemId = itemId;
+ }
+
+ public int getItemCount() {
+ return ItemCount;
+ }
+
+ public void setItemCount(int itemCount) {
+ ItemCount = itemCount;
+ }
+}
diff --git a/src/main/java/emu/grasscutter/data/def/AvatarData.java b/src/main/java/emu/grasscutter/data/def/AvatarData.java
index 8097e04a7..3beb21eec 100644
--- a/src/main/java/emu/grasscutter/data/def/AvatarData.java
+++ b/src/main/java/emu/grasscutter/data/def/AvatarData.java
@@ -57,6 +57,8 @@ public class AvatarData extends GenshinResource {
private IntList abilities;
private List fetters;
+ private int nameCardRewardId;
+ private int nameCardId;
@Override
public int getId(){
@@ -199,12 +201,28 @@ public class AvatarData extends GenshinResource {
return fetters;
}
+ public int getNameCardRewardId() {
+ return nameCardRewardId;
+ }
+
+ public int getNameCardId() {
+ return nameCardId;
+ }
+
@Override
public void onLoad() {
this.skillDepot = GenshinData.getAvatarSkillDepotDataMap().get(this.SkillDepotId);
// Get fetters from GenshinData
this.fetters = GenshinData.getFetterDataEntries().get(this.Id);
+
+ if (GenshinData.getFetterCharacterCardDataMap().get(this.Id) != null) {
+ this.nameCardRewardId = GenshinData.getFetterCharacterCardDataMap().get(this.Id).getRewardId();
+ }
+
+ if (GenshinData.getRewardDataMap().get(this.nameCardRewardId) != null) {
+ this.nameCardId = GenshinData.getRewardDataMap().get(this.nameCardRewardId).getRewardItemList().get(0).getItemId();
+ }
int size = GenshinData.getAvatarCurveDataMap().size();
this.hpGrowthCurve = new float[size];
diff --git a/src/main/java/emu/grasscutter/data/def/AvatarFetterLevelData.java b/src/main/java/emu/grasscutter/data/def/AvatarFetterLevelData.java
new file mode 100644
index 000000000..8dd93eea5
--- /dev/null
+++ b/src/main/java/emu/grasscutter/data/def/AvatarFetterLevelData.java
@@ -0,0 +1,23 @@
+package emu.grasscutter.data.def;
+
+import emu.grasscutter.data.GenshinResource;
+import emu.grasscutter.data.ResourceType;
+
+@ResourceType(name = "AvatarFettersLevelExcelConfigData.json")
+public class AvatarFetterLevelData extends GenshinResource {
+ private int FetterLevel;
+ private int NeedExp;
+
+ @Override
+ public int getId() {
+ return this.FetterLevel;
+ }
+
+ public int getLevel() {
+ return FetterLevel;
+ }
+
+ public int getExp() {
+ return NeedExp;
+ }
+}
diff --git a/src/main/java/emu/grasscutter/data/def/FetterCharacterCardData.java b/src/main/java/emu/grasscutter/data/def/FetterCharacterCardData.java
new file mode 100644
index 000000000..2bf96ad8e
--- /dev/null
+++ b/src/main/java/emu/grasscutter/data/def/FetterCharacterCardData.java
@@ -0,0 +1,24 @@
+package emu.grasscutter.data.def;
+
+import emu.grasscutter.data.GenshinResource;
+import emu.grasscutter.data.ResourceType;
+import emu.grasscutter.data.ResourceType.LoadPriority;
+
+@ResourceType(name = "FetterCharacterCardExcelConfigData.json", loadPriority = LoadPriority.HIGHEST)
+public class FetterCharacterCardData extends GenshinResource {
+ private int AvatarId;
+ private int RewardId;
+
+ @Override
+ public int getId() {
+ return AvatarId;
+ }
+
+ public int getRewardId() {
+ return RewardId;
+ }
+
+ @Override
+ public void onLoad() {
+ }
+}
diff --git a/src/main/java/emu/grasscutter/data/def/FetterData.java b/src/main/java/emu/grasscutter/data/def/FetterData.java
index d17c940d1..dc88fd795 100644
--- a/src/main/java/emu/grasscutter/data/def/FetterData.java
+++ b/src/main/java/emu/grasscutter/data/def/FetterData.java
@@ -1,13 +1,17 @@
package emu.grasscutter.data.def;
+import java.util.List;
+
import emu.grasscutter.data.GenshinResource;
import emu.grasscutter.data.ResourceType;
import emu.grasscutter.data.ResourceType.LoadPriority;
+import emu.grasscutter.data.common.OpenCondData;
@ResourceType(name = {"FetterInfoExcelConfigData.json", "FettersExcelConfigData.json", "FetterStoryExcelConfigData.json"}, loadPriority = LoadPriority.HIGHEST)
public class FetterData extends GenshinResource {
private int AvatarId;
private int FetterId;
+ private List OpenCond;
@Override
public int getId() {
@@ -18,6 +22,10 @@ public class FetterData extends GenshinResource {
return AvatarId;
}
+ public List getOpenConds() {
+ return OpenCond;
+ }
+
@Override
public void onLoad() {
}
diff --git a/src/main/java/emu/grasscutter/data/def/RewardData.java b/src/main/java/emu/grasscutter/data/def/RewardData.java
new file mode 100644
index 000000000..46c39ac10
--- /dev/null
+++ b/src/main/java/emu/grasscutter/data/def/RewardData.java
@@ -0,0 +1,27 @@
+package emu.grasscutter.data.def;
+
+import java.util.List;
+
+import emu.grasscutter.data.GenshinResource;
+import emu.grasscutter.data.ResourceType;
+import emu.grasscutter.data.common.RewardItemData;
+
+@ResourceType(name = "RewardExcelConfigData.json")
+public class RewardData extends GenshinResource {
+ public int RewardId;
+ public List RewardItemList;
+
+ @Override
+ public int getId() {
+ return RewardId;
+ }
+
+ public List getRewardItemList() {
+ return RewardItemList;
+ }
+
+ @Override
+ public void onLoad() {
+
+ }
+}
diff --git a/src/main/java/emu/grasscutter/database/DatabaseManager.java b/src/main/java/emu/grasscutter/database/DatabaseManager.java
index 9d64f9258..6df2ca9f0 100644
--- a/src/main/java/emu/grasscutter/database/DatabaseManager.java
+++ b/src/main/java/emu/grasscutter/database/DatabaseManager.java
@@ -101,7 +101,7 @@ public final class DatabaseManager {
}
public static synchronized int getNextId(Class> c) {
- DatabaseCounter counter = getDatastore().find(DatabaseCounter.class).filter(Filters.eq("_id", c.getName())).first();
+ DatabaseCounter counter = getDatastore().find(DatabaseCounter.class).filter(Filters.eq("_id", c.getSimpleName())).first();
if (counter == null) {
counter = new DatabaseCounter(c.getSimpleName());
}
diff --git a/src/main/java/emu/grasscutter/game/GenshinPlayer.java b/src/main/java/emu/grasscutter/game/GenshinPlayer.java
index cdef5852e..3c68f04c2 100644
--- a/src/main/java/emu/grasscutter/game/GenshinPlayer.java
+++ b/src/main/java/emu/grasscutter/game/GenshinPlayer.java
@@ -1,7 +1,5 @@
package emu.grasscutter.game;
-import java.util.*;
-
import dev.morphia.annotations.*;
import emu.grasscutter.GenshinConstants;
import emu.grasscutter.Grasscutter;
@@ -23,7 +21,6 @@ import emu.grasscutter.game.props.ActionReason;
import emu.grasscutter.game.props.PlayerProperty;
import emu.grasscutter.net.packet.GenshinPacket;
import emu.grasscutter.net.proto.AbilityInvokeEntryOuterClass.AbilityInvokeEntry;
-import emu.grasscutter.net.proto.BirthdayOuterClass.Birthday;
import emu.grasscutter.net.proto.CombatInvokeEntryOuterClass.CombatInvokeEntry;
import emu.grasscutter.net.proto.HeadImageOuterClass.HeadImage;
import emu.grasscutter.net.proto.InteractTypeOuterClass.InteractType;
@@ -35,38 +32,18 @@ import emu.grasscutter.net.proto.SocialDetailOuterClass.SocialDetail;
import emu.grasscutter.net.proto.WorldPlayerLocationInfoOuterClass.WorldPlayerLocationInfo;
import emu.grasscutter.server.game.GameServer;
import emu.grasscutter.server.game.GameSession;
-import emu.grasscutter.server.packet.send.PacketAbilityInvocationsNotify;
-import emu.grasscutter.server.packet.send.PacketAvatarAddNotify;
-import emu.grasscutter.server.packet.send.PacketAvatarDataNotify;
-import emu.grasscutter.server.packet.send.PacketAvatarGainCostumeNotify;
-import emu.grasscutter.server.packet.send.PacketAvatarGainFlycloakNotify;
-import emu.grasscutter.server.packet.send.PacketClientAbilityInitFinishNotify;
-import emu.grasscutter.server.packet.send.PacketCombatInvocationsNotify;
-import emu.grasscutter.server.packet.send.PacketGadgetInteractRsp;
-import emu.grasscutter.server.packet.send.PacketItemAddHintNotify;
-import emu.grasscutter.server.packet.send.PacketOpenStateUpdateNotify;
-import emu.grasscutter.server.packet.send.PacketPlayerApplyEnterMpResultNotify;
-import emu.grasscutter.server.packet.send.PacketPlayerDataNotify;
-import emu.grasscutter.server.packet.send.PacketPlayerEnterSceneNotify;
-import emu.grasscutter.server.packet.send.PacketPlayerPropNotify;
-import emu.grasscutter.server.packet.send.PacketPlayerStoreNotify;
-import emu.grasscutter.server.packet.send.PacketPrivateChatNotify;
-import emu.grasscutter.server.packet.send.PacketScenePlayerLocationNotify;
-import emu.grasscutter.server.packet.send.PacketSetNameCardRsp;
-import emu.grasscutter.server.packet.send.PacketStoreWeightLimitNotify;
-import emu.grasscutter.server.packet.send.PacketUnlockNameCardNotify;
-import emu.grasscutter.server.packet.send.PacketWorldPlayerLocationNotify;
-import emu.grasscutter.server.packet.send.PacketWorldPlayerRTTNotify;
+import emu.grasscutter.server.packet.send.*;
import emu.grasscutter.utils.Position;
-
import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
+import java.util.*;
+
@Entity(value = "players", useDiscriminator = false)
public class GenshinPlayer {
@Id private int id;
@Indexed(options = @IndexOptions(unique = true)) private String accountId;
-
+
@Transient private Account account;
private String nickname;
private String signature;
@@ -75,12 +52,12 @@ public class GenshinPlayer {
private Position pos;
private Position rotation;
private PlayerBirthday birthday;
-
+
private Map properties;
private Set nameCardList;
private Set flyCloakList;
private Set costumeList;
-
+
@Transient private long nextGuid = 0;
@Transient private int peerId;
@Transient private World world;
@@ -89,32 +66,34 @@ public class GenshinPlayer {
@Transient private AvatarStorage avatars;
@Transient private Inventory inventory;
@Transient private FriendsList friendsList;
-
+
private TeamManager teamManager;
private PlayerGachaInfo gachaInfo;
private PlayerProfile playerProfile;
private MpSettingType mpSetting = MpSettingType.MpSettingEnterAfterApply;
private boolean showAvatar;
private ArrayList shownAvatars;
-
+ private Set rewardedLevels;
+
private int sceneId;
private int regionId;
private int mainCharacterId;
private boolean godmode;
-
+
@Transient private boolean paused;
@Transient private int enterSceneToken;
@Transient private SceneLoadState sceneState;
@Transient private boolean hasSentAvatarDataNotify;
@Transient private long nextSendPlayerLocTime = 0;
-
+
@Transient private final Int2ObjectMap coopRequests;
@Transient private final InvokeHandler combatInvokeHandler;
@Transient private final InvokeHandler abilityInvokeHandler;
@Transient private final InvokeHandler clientAbilityInitFinishHandler;
-
- @Deprecated @SuppressWarnings({ "rawtypes", "unchecked" }) // Morphia only!
- public GenshinPlayer() {
+
+ @Deprecated
+ @SuppressWarnings({"rawtypes", "unchecked"}) // Morphia only!
+ public GenshinPlayer() {
this.inventory = new Inventory(this);
this.avatars = new AvatarStorage(this);
this.friendsList = new FriendsList(this);
@@ -127,24 +106,25 @@ public class GenshinPlayer {
}
this.properties.put(prop.getId(), 0);
}
-
+
this.gachaInfo = new PlayerGachaInfo();
this.nameCardList = new HashSet<>();
this.flyCloakList = new HashSet<>();
this.costumeList = new HashSet<>();
-
+
this.setSceneId(3);
this.setRegionId(1);
this.sceneState = SceneLoadState.NONE;
-
+
this.coopRequests = new Int2ObjectOpenHashMap<>();
this.combatInvokeHandler = new InvokeHandler(PacketCombatInvocationsNotify.class);
this.abilityInvokeHandler = new InvokeHandler(PacketAbilityInvocationsNotify.class);
this.clientAbilityInitFinishHandler = new InvokeHandler(PacketClientAbilityInitFinishNotify.class);
this.birthday = new PlayerBirthday();
+ this.rewardedLevels = new HashSet<>();
}
-
+
// On player creation
public GenshinPlayer(GameSession session) {
this();
@@ -176,7 +156,7 @@ public class GenshinPlayer {
public void setUid(int id) {
this.id = id;
}
-
+
public long getNextGenshinGuid() {
long nextId = ++this.nextGuid;
return ((long) this.getUid() << 32) + nextId;
@@ -198,23 +178,23 @@ public class GenshinPlayer {
public void setSession(GameSession session) {
this.session = session;
}
-
+
public boolean isOnline() {
return this.getSession() != null && this.getSession().isActive();
}
-
+
public GameServer getServer() {
return this.getSession().getServer();
}
-
+
public synchronized World getWorld() {
return this.world;
}
-
+
public synchronized void setWorld(World world) {
this.world = world;
}
-
+
public GenshinScene getScene() {
return scene;
}
@@ -235,7 +215,7 @@ public class GenshinPlayer {
this.nickname = nickName;
this.updateProfile();
}
-
+
public int getHeadImage() {
return headImage;
}
@@ -257,71 +237,71 @@ public class GenshinPlayer {
public Position getPos() {
return pos;
}
-
+
public Position getRotation() {
return rotation;
}
-
+
public int getLevel() {
return this.getProperty(PlayerProperty.PROP_PLAYER_LEVEL);
}
-
+
public int getExp() {
return this.getProperty(PlayerProperty.PROP_PLAYER_EXP);
}
-
+
public int getWorldLevel() {
return this.getProperty(PlayerProperty.PROP_PLAYER_WORLD_LEVEL);
}
-
+
public int getPrimogems() {
return this.getProperty(PlayerProperty.PROP_PLAYER_HCOIN);
}
-
+
public void setPrimogems(int primogem) {
this.setProperty(PlayerProperty.PROP_PLAYER_HCOIN, primogem);
this.sendPacket(new PacketPlayerPropNotify(this, PlayerProperty.PROP_PLAYER_HCOIN));
}
-
+
public int getMora() {
return this.getProperty(PlayerProperty.PROP_PLAYER_SCOIN);
}
-
+
public void setMora(int mora) {
this.setProperty(PlayerProperty.PROP_PLAYER_SCOIN, mora);
this.sendPacket(new PacketPlayerPropNotify(this, PlayerProperty.PROP_PLAYER_SCOIN));
}
-
+
private int getExpRequired(int level) {
PlayerLevelData levelData = GenshinData.getPlayerLevelDataMap().get(level);
- return levelData != null ? levelData.getExp() : 0;
+ return levelData != null ? levelData.getExp() : 0;
}
-
+
private float getExpModifier() {
return Grasscutter.getConfig().getGameServerOptions().getGameRates().ADVENTURE_EXP_RATE;
}
-
+
// Affected by exp rate
public void earnExp(int exp) {
addExpDirectly((int) (exp * getExpModifier()));
}
-
+
// Directly give player exp
public void addExpDirectly(int gain) {
boolean hasLeveledUp = false;
int level = getLevel();
int exp = getExp();
int reqExp = getExpRequired(level);
-
+
exp += gain;
-
+
while (exp >= reqExp && reqExp > 0) {
exp -= reqExp;
level += 1;
reqExp = getExpRequired(level);
hasLeveledUp = true;
}
-
+
if (hasLeveledUp) {
// Set level property
this.setProperty(PlayerProperty.PROP_PLAYER_LEVEL, level);
@@ -330,18 +310,18 @@ public class GenshinPlayer {
// Update player with packet
this.sendPacket(new PacketPlayerPropNotify(this, PlayerProperty.PROP_PLAYER_LEVEL));
}
-
+
// Set exp
this.setProperty(PlayerProperty.PROP_PLAYER_EXP, exp);
-
+
// Update player with packet
this.sendPacket(new PacketPlayerPropNotify(this, PlayerProperty.PROP_PLAYER_EXP));
}
-
+
private void updateProfile() {
getProfile().syncWithCharacter(this);
}
-
+
public boolean isFirstLoginEnterScene() {
return !this.hasSentAvatarDataNotify;
}
@@ -364,11 +344,11 @@ public class GenshinPlayer {
public Map getProperties() {
return properties;
}
-
+
public void setProperty(PlayerProperty prop, int value) {
getProperties().put(prop.getId(), value);
}
-
+
public int getProperty(PlayerProperty prop) {
return getProperties().get(prop.getId());
}
@@ -376,11 +356,11 @@ public class GenshinPlayer {
public Set getFlyCloakList() {
return flyCloakList;
}
-
+
public Set getCostumeList() {
return costumeList;
}
-
+
public Set getNameCardList() {
return this.nameCardList;
}
@@ -388,7 +368,7 @@ public class GenshinPlayer {
public MpSettingType getMpSetting() {
return mpSetting;
}
-
+
public synchronized Int2ObjectMap getCoopRequests() {
return coopRequests;
}
@@ -396,7 +376,7 @@ public class GenshinPlayer {
public InvokeHandler getCombatInvokeHandler() {
return this.combatInvokeHandler;
}
-
+
public InvokeHandler getAbilityInvokeHandler() {
return this.abilityInvokeHandler;
}
@@ -412,11 +392,11 @@ public class GenshinPlayer {
public AvatarStorage getAvatars() {
return avatars;
}
-
+
public Inventory getInventory() {
return inventory;
}
-
+
public FriendsList getFriendsList() {
return this.friendsList;
}
@@ -469,7 +449,7 @@ public class GenshinPlayer {
public void setPaused(boolean newPauseState) {
boolean oldPauseState = this.paused;
this.paused = newPauseState;
-
+
if (newPauseState && !oldPauseState) {
this.onPause();
} else if (oldPauseState && !newPauseState) {
@@ -484,7 +464,7 @@ public class GenshinPlayer {
public void setSceneLoadState(SceneLoadState sceneState) {
this.sceneState = sceneState;
}
-
+
public boolean isInMultiplayer() {
return this.getWorld() != null && this.getWorld().isMultiplayer();
}
@@ -504,7 +484,7 @@ public class GenshinPlayer {
public void setRegionId(int regionId) {
this.regionId = regionId;
}
-
+
public boolean inGodmode() {
return godmode;
}
@@ -523,11 +503,11 @@ public class GenshinPlayer {
public void addAvatar(GenshinAvatar avatar) {
boolean result = getAvatars().addAvatar(avatar);
-
+
if (result) {
// Add starting weapon
getAvatars().addStartingWeapon(avatar);
-
+
// Try adding to team if possible
//List currentTeam = this.getTeamManager().getCurrentTeam();
boolean addedToTeam = false;
@@ -537,7 +517,7 @@ public class GenshinPlayer {
addedToTeam = currentTeam
}
*/
-
+
// Done
if (hasSentAvatarDataNotify()) {
// Recalc stats
@@ -549,55 +529,56 @@ public class GenshinPlayer {
// Failed adding avatar
}
}
-
+
public void addFlycloak(int flycloakId) {
this.getFlyCloakList().add(flycloakId);
this.sendPacket(new PacketAvatarGainFlycloakNotify(flycloakId));
}
-
+
public void addCostume(int costumeId) {
this.getCostumeList().add(costumeId);
this.sendPacket(new PacketAvatarGainCostumeNotify(costumeId));
}
-
+
public void addNameCard(int nameCardId) {
this.getNameCardList().add(nameCardId);
this.sendPacket(new PacketUnlockNameCardNotify(nameCardId));
}
-
+
public void setNameCard(int nameCardId) {
if (!this.getNameCardList().contains(nameCardId)) {
return;
}
-
+
this.setNameCardId(nameCardId);
-
+
this.sendPacket(new PacketSetNameCardRsp(nameCardId));
}
-
+
public void dropMessage(Object message) {
this.sendPacket(new PacketPrivateChatNotify(GenshinConstants.SERVER_CONSOLE_UID, getUid(), message.toString()));
}
/**
* Sends a message to another player.
- * @param sender The sender of the message.
+ *
+ * @param sender The sender of the message.
* @param message The message to send.
*/
public void sendMessage(GenshinPlayer sender, Object message) {
this.sendPacket(new PacketPrivateChatNotify(sender.getUid(), this.getUid(), message.toString()));
}
-
+
public void interactWith(int gadgetEntityId) {
GenshinEntity entity = getScene().getEntityById(gadgetEntityId);
-
+
if (entity == null) {
return;
}
-
+
// Delete
entity.getScene().removeEntity(entity);
-
+
// Handle
if (entity instanceof EntityItem) {
// Pick item
@@ -610,24 +591,24 @@ public class GenshinPlayer {
this.sendPacket(new PacketItemAddHintNotify(item, ActionReason.SubfieldDrop));
}
}
-
+
return;
}
-
+
public void onPause() {
-
+
}
-
+
public void onUnpause() {
-
+
}
-
+
public void sendPacket(GenshinPacket packet) {
if (this.hasSentAvatarDataNotify) {
this.getSession().send(packet);
}
}
-
+
public OnlinePlayerInfo getOnlinePlayerInfo() {
OnlinePlayerInfo.Builder onlineInfo = OnlinePlayerInfo.newBuilder()
.setUid(this.getUid())
@@ -637,17 +618,17 @@ public class GenshinPlayer {
.setNameCardId(this.getNameCardId())
.setSignature(this.getSignature())
.setAvatar(HeadImage.newBuilder().setAvatarId(this.getHeadImage()));
-
+
if (this.getWorld() != null) {
onlineInfo.setCurPlayerNumInWorld(this.getWorld().getPlayers().indexOf(this) + 1);
} else {
onlineInfo.setCurPlayerNumInWorld(1);
}
-
+
return onlineInfo.build();
}
- public PlayerBirthday getBirthday(){
+ public PlayerBirthday getBirthday() {
return this.birthday;
}
@@ -656,6 +637,18 @@ public class GenshinPlayer {
this.updateProfile();
}
+ public boolean hasBirthday() {
+ return this.birthday.getDay() > 0;
+ }
+
+ public Set getRewardedLevels() {
+ return rewardedLevels;
+ }
+
+ public void setRewardedLevels(Set rewardedLevels) {
+ this.rewardedLevels = rewardedLevels;
+ }
+
public SocialDetail.Builder getSocialDetail() {
SocialDetail.Builder social = SocialDetail.newBuilder()
.setUid(this.getUid())
@@ -671,22 +664,22 @@ public class GenshinPlayer {
.setFinishAchievementNum(0);
return social;
}
-
+
public WorldPlayerLocationInfo getWorldPlayerLocationInfo() {
return WorldPlayerLocationInfo.newBuilder()
- .setSceneId(this.getSceneId())
- .setPlayerLoc(this.getPlayerLocationInfo())
- .build();
+ .setSceneId(this.getSceneId())
+ .setPlayerLoc(this.getPlayerLocationInfo())
+ .build();
}
-
+
public PlayerLocationInfo getPlayerLocationInfo() {
return PlayerLocationInfo.newBuilder()
- .setUid(this.getUid())
- .setPos(this.getPos().toProto())
- .setRot(this.getRotation().toProto())
- .build();
+ .setUid(this.getUid())
+ .setPos(this.getPos().toProto())
+ .setRot(this.getRotation().toProto())
+ .build();
}
-
+
public synchronized void onTick() {
// Check ping
if (this.getLastPingTime() > System.currentTimeMillis() + 60000) {
@@ -706,7 +699,7 @@ public class GenshinPlayer {
if (this.getWorld() != null) {
// RTT notify - very important to send this often
this.sendPacket(new PacketWorldPlayerRTTNotify(this.getWorld()));
-
+
// Update player locations if in multiplayer every 5 seconds
long time = System.currentTimeMillis();
if (this.getWorld().isMultiplayer() && this.getScene() != null && time > nextSendPlayerLocTime) {
@@ -716,7 +709,7 @@ public class GenshinPlayer {
}
}
}
-
+
public void resetSendPlayerLocTime() {
this.nextSendPlayerLocTime = System.currentTimeMillis() + 5000;
}
@@ -725,7 +718,7 @@ public class GenshinPlayer {
private void onLoad() {
this.getTeamManager().setPlayer(this);
}
-
+
public void save() {
DatabaseHelper.savePlayer(this);
}
@@ -734,78 +727,80 @@ public class GenshinPlayer {
// Make sure these exist
if (this.getTeamManager() == null) {
this.teamManager = new TeamManager(this);
- } if (this.getProfile().getUid() == 0) {
+ }
+ if (this.getProfile().getUid() == 0) {
this.getProfile().syncWithCharacter(this);
}
-
+
// Check if player object exists in server
// TODO - optimize
GenshinPlayer exists = this.getServer().getPlayerByUid(getUid());
if (exists != null) {
exists.getSession().close();
}
-
+
// Load from db
this.getAvatars().loadFromDatabase();
this.getInventory().loadFromDatabase();
this.getAvatars().postLoad();
-
+
this.getFriendsList().loadFromDatabase();
-
+
// Create world
World world = new World(this);
world.addPlayer(this);
-
+
// Add to gameserver
if (getSession().isActive()) {
getServer().registerPlayer(this);
getProfile().setPlayer(this); // Set online
}
-
- // Multiplayer setting
+
+ // Multiplayer setting
this.setProperty(PlayerProperty.PROP_PLAYER_MP_SETTING_TYPE, this.getMpSetting().getNumber());
this.setProperty(PlayerProperty.PROP_IS_MP_MODE_AVAILABLE, 1);
-
+
// Packets
session.send(new PacketPlayerDataNotify(this)); // Player data
session.send(new PacketStoreWeightLimitNotify());
session.send(new PacketPlayerStoreNotify(this));
session.send(new PacketAvatarDataNotify(this));
-
+
session.send(new PacketPlayerEnterSceneNotify(this)); // Enter game world
+ session.send(new PacketPlayerLevelRewardUpdateNotify(rewardedLevels));
session.send(new PacketOpenStateUpdateNotify());
// First notify packets sent
this.setHasSentAvatarDataNotify(true);
}
-
+
public void onLogout() {
// Leave world
if (this.getWorld() != null) {
this.getWorld().removePlayer(this);
}
-
+
// Status stuff
this.getProfile().syncWithCharacter(this);
this.getProfile().setPlayer(null); // Set offline
-
+
this.getCoopRequests().clear();
-
+
// Save to db
this.save();
this.getTeamManager().saveAvatars();
this.getFriendsList().save();
}
-
+
public enum SceneLoadState {
- NONE (0), LOADING (1), INIT (2), LOADED (3);
-
+ NONE(0), LOADING(1), INIT(2), LOADED(3);
+
private final int value;
-
+
private SceneLoadState(int value) {
this.value = value;
}
-
+
public int getValue() {
return this.value;
}
diff --git a/src/main/java/emu/grasscutter/game/avatar/GenshinAvatar.java b/src/main/java/emu/grasscutter/game/avatar/GenshinAvatar.java
index b80b080d9..b6007e280 100644
--- a/src/main/java/emu/grasscutter/game/avatar/GenshinAvatar.java
+++ b/src/main/java/emu/grasscutter/game/avatar/GenshinAvatar.java
@@ -89,6 +89,12 @@ public class GenshinAvatar {
private int flyCloak;
private int costume;
private int bornTime;
+
+ private int fetterLevel = 1;
+ private int fetterExp;
+
+ private int nameCardRewardId;
+ private int nameCardId;
public GenshinAvatar() {
// Morhpia only!
@@ -107,6 +113,8 @@ public class GenshinAvatar {
public GenshinAvatar(AvatarData data) {
this();
this.avatarId = data.getId();
+ this.nameCardRewardId = data.getNameCardRewardId();
+ this.nameCardId = data.getNameCardId();
this.data = data;
this.bornTime = (int) (System.currentTimeMillis() / 1000);
this.flyCloak = 140001;
@@ -169,6 +177,14 @@ public class GenshinAvatar {
this.satiation = satiation;
}
+ public int getNameCardRewardId() {
+ return nameCardRewardId;
+ }
+
+ public void setNameCardRewardId(int nameCardRewardId) {
+ this.nameCardRewardId = nameCardRewardId;
+ }
+
public int getSatiationPenalty() {
return satiationPenalty;
}
@@ -281,6 +297,30 @@ public class GenshinAvatar {
return fetters;
}
+ public int getFetterLevel() {
+ return fetterLevel;
+ }
+
+ public void setFetterLevel(int fetterLevel) {
+ this.fetterLevel = fetterLevel;
+ }
+
+ public int getFetterExp() {
+ return fetterExp;
+ }
+
+ public void setFetterExp(int fetterExp) {
+ this.fetterExp = fetterExp;
+ }
+
+ public int getNameCardId() {
+ return nameCardId;
+ }
+
+ public void setNameCardId(int nameCardId) {
+ this.nameCardId = nameCardId;
+ }
+
public float getCurrentHp() {
return currentHp;
}
@@ -403,6 +443,8 @@ public class GenshinAvatar {
// Fetters
this.setFetterList(data.getFetters());
+ this.setNameCardRewardId(data.getNameCardRewardId());
+ this.setNameCardId(data.getNameCardId());
// Get hp percent, set to 100% if none
float hpPercent = this.getFightProperty(FightProperty.FIGHT_PROP_MAX_HP) <= 0 ? 1f : this.getFightProperty(FightProperty.FIGHT_PROP_CUR_HP) / this.getFightProperty(FightProperty.FIGHT_PROP_MAX_HP);
@@ -701,9 +743,14 @@ public class GenshinAvatar {
}
public AvatarInfo toProto() {
+ int fetterLevel = this.getFetterLevel();
AvatarFetterInfo.Builder avatarFetter = AvatarFetterInfo.newBuilder()
- .setExpLevel(10)
- .setExpNumber(6325); // Highest Level
+ .setExpLevel(fetterLevel);
+
+ if (fetterLevel != 10) {
+ avatarFetter.setExpNumber(this.getFetterExp());
+ }
+
if (this.getFetterList() != null) {
for (int i = 0; i < this.getFetterList().size(); i++) {
@@ -715,6 +762,12 @@ public class GenshinAvatar {
}
}
+ int cardId = this.getNameCardId();
+
+ if (this.getPlayer().getNameCardList().contains(cardId)) {
+ avatarFetter.addRewardedFetterLevelList(10);
+ }
+
AvatarInfo.Builder avatarInfo = AvatarInfo.newBuilder()
.setAvatarId(this.getAvatarId())
.setGuid(this.getGuid())
diff --git a/src/main/java/emu/grasscutter/game/inventory/GenshinItem.java b/src/main/java/emu/grasscutter/game/inventory/GenshinItem.java
index a395825da..89c0e7169 100644
--- a/src/main/java/emu/grasscutter/game/inventory/GenshinItem.java
+++ b/src/main/java/emu/grasscutter/game/inventory/GenshinItem.java
@@ -90,7 +90,7 @@ public class GenshinItem {
// Equip data
if (getItemType() == ItemType.ITEM_WEAPON) {
- this.level = 1;
+ this.level = this.count > 1 ? this.count : 1;
this.affixes = new ArrayList<>(2);
if (getItemData().getSkillAffix() != null) {
for (int skillAffix : getItemData().getSkillAffix()) {
diff --git a/src/main/java/emu/grasscutter/game/managers/InventoryManager.java b/src/main/java/emu/grasscutter/game/managers/InventoryManager.java
index 532155db1..4ea9e6bc1 100644
--- a/src/main/java/emu/grasscutter/game/managers/InventoryManager.java
+++ b/src/main/java/emu/grasscutter/game/managers/InventoryManager.java
@@ -711,6 +711,31 @@ public class InventoryManager {
player.sendPacket(new PacketAvatarUpgradeRsp(avatar, oldLevel, oldPropMap));
}
+ public void upgradeAvatarFetterLevel(GenshinPlayer player, GenshinAvatar avatar, int expGain) {
+ // May work. Not test.
+ int maxLevel = 10; // Keep it until I think of a more "elegant" way
+ int level = avatar.getFetterLevel();
+ int exp = avatar.getFetterExp();
+ int reqExp = GenshinData.getAvatarFetterLevelExpRequired(level);
+
+ while (expGain > 0 && reqExp > 0 && level < maxLevel) {
+ int toGain = Math.min(expGain, reqExp - exp);
+ exp += toGain;
+ expGain -= toGain;
+ if (exp >= reqExp) {
+ exp = 0;
+ level += 1;
+ reqExp = GenshinData.getAvatarFetterLevelExpRequired(level);
+ }
+ }
+
+ avatar.setFetterLevel(level);
+ avatar.setFetterExp(exp);
+ avatar.save();
+
+ player.sendPacket(new PacketAvatarPropNotify(avatar));
+ }
+
public void upgradeAvatarSkill(GenshinPlayer player, long guid, int skillId) {
// Sanity checks
GenshinAvatar avatar = player.getAvatars().getAvatarByGuid(guid);
diff --git a/src/main/java/emu/grasscutter/game/player/PlayerBirthday.java b/src/main/java/emu/grasscutter/game/player/PlayerBirthday.java
index 733f58ee5..13a8e7c58 100644
--- a/src/main/java/emu/grasscutter/game/player/PlayerBirthday.java
+++ b/src/main/java/emu/grasscutter/game/player/PlayerBirthday.java
@@ -1,7 +1,9 @@
package emu.grasscutter.game.player;
+import dev.morphia.annotations.Entity;
import emu.grasscutter.net.proto.BirthdayOuterClass.Birthday;
+@Entity
public class PlayerBirthday {
private int day;
private int month;
diff --git a/src/main/java/emu/grasscutter/net/proto/PlayerLevelRewardUpdateNotifyOuterClass.java b/src/main/java/emu/grasscutter/net/proto/PlayerLevelRewardUpdateNotifyOuterClass.java
new file mode 100644
index 000000000..9dcad0ee3
--- /dev/null
+++ b/src/main/java/emu/grasscutter/net/proto/PlayerLevelRewardUpdateNotifyOuterClass.java
@@ -0,0 +1,666 @@
+// Generated by the protocol buffer compiler. DO NOT EDIT!
+// source: PlayerLevelRewardUpdateNotify.proto
+
+package emu.grasscutter.net.proto;
+
+public final class PlayerLevelRewardUpdateNotifyOuterClass {
+ private PlayerLevelRewardUpdateNotifyOuterClass() {}
+ public static void registerAllExtensions(
+ com.google.protobuf.ExtensionRegistryLite registry) {
+ }
+
+ public static void registerAllExtensions(
+ com.google.protobuf.ExtensionRegistry registry) {
+ registerAllExtensions(
+ (com.google.protobuf.ExtensionRegistryLite) registry);
+ }
+ public interface PlayerLevelRewardUpdateNotifyOrBuilder extends
+ // @@protoc_insertion_point(interface_extends:PlayerLevelRewardUpdateNotify)
+ com.google.protobuf.MessageOrBuilder {
+
+ /**
+ * repeated uint32 levelList = 1;
+ * @return A list containing the levelList.
+ */
+ java.util.List getLevelListList();
+ /**
+ * repeated uint32 levelList = 1;
+ * @return The count of levelList.
+ */
+ int getLevelListCount();
+ /**
+ * repeated uint32 levelList = 1;
+ * @param index The index of the element to return.
+ * @return The levelList at the given index.
+ */
+ int getLevelList(int index);
+ }
+ /**
+ * Protobuf type {@code PlayerLevelRewardUpdateNotify}
+ */
+ public static final class PlayerLevelRewardUpdateNotify extends
+ com.google.protobuf.GeneratedMessageV3 implements
+ // @@protoc_insertion_point(message_implements:PlayerLevelRewardUpdateNotify)
+ PlayerLevelRewardUpdateNotifyOrBuilder {
+ private static final long serialVersionUID = 0L;
+ // Use PlayerLevelRewardUpdateNotify.newBuilder() to construct.
+ private PlayerLevelRewardUpdateNotify(com.google.protobuf.GeneratedMessageV3.Builder> builder) {
+ super(builder);
+ }
+ private PlayerLevelRewardUpdateNotify() {
+ levelList_ = emptyIntList();
+ }
+
+ @java.lang.Override
+ @SuppressWarnings({"unused"})
+ protected java.lang.Object newInstance(
+ UnusedPrivateParameter unused) {
+ return new PlayerLevelRewardUpdateNotify();
+ }
+
+ @java.lang.Override
+ public final com.google.protobuf.UnknownFieldSet
+ getUnknownFields() {
+ return this.unknownFields;
+ }
+ private PlayerLevelRewardUpdateNotify(
+ com.google.protobuf.CodedInputStream input,
+ com.google.protobuf.ExtensionRegistryLite extensionRegistry)
+ throws com.google.protobuf.InvalidProtocolBufferException {
+ this();
+ if (extensionRegistry == null) {
+ throw new java.lang.NullPointerException();
+ }
+ int mutable_bitField0_ = 0;
+ com.google.protobuf.UnknownFieldSet.Builder unknownFields =
+ com.google.protobuf.UnknownFieldSet.newBuilder();
+ try {
+ boolean done = false;
+ while (!done) {
+ int tag = input.readTag();
+ switch (tag) {
+ case 0:
+ done = true;
+ break;
+ case 8: {
+ if (!((mutable_bitField0_ & 0x00000001) != 0)) {
+ levelList_ = newIntList();
+ mutable_bitField0_ |= 0x00000001;
+ }
+ levelList_.addInt(input.readUInt32());
+ break;
+ }
+ case 10: {
+ int length = input.readRawVarint32();
+ int limit = input.pushLimit(length);
+ if (!((mutable_bitField0_ & 0x00000001) != 0) && input.getBytesUntilLimit() > 0) {
+ levelList_ = newIntList();
+ mutable_bitField0_ |= 0x00000001;
+ }
+ while (input.getBytesUntilLimit() > 0) {
+ levelList_.addInt(input.readUInt32());
+ }
+ input.popLimit(limit);
+ break;
+ }
+ default: {
+ if (!parseUnknownField(
+ input, unknownFields, extensionRegistry, tag)) {
+ done = true;
+ }
+ break;
+ }
+ }
+ }
+ } catch (com.google.protobuf.InvalidProtocolBufferException e) {
+ throw e.setUnfinishedMessage(this);
+ } catch (com.google.protobuf.UninitializedMessageException e) {
+ throw e.asInvalidProtocolBufferException().setUnfinishedMessage(this);
+ } catch (java.io.IOException e) {
+ throw new com.google.protobuf.InvalidProtocolBufferException(
+ e).setUnfinishedMessage(this);
+ } finally {
+ if (((mutable_bitField0_ & 0x00000001) != 0)) {
+ levelList_.makeImmutable(); // C
+ }
+ this.unknownFields = unknownFields.build();
+ makeExtensionsImmutable();
+ }
+ }
+ public static final com.google.protobuf.Descriptors.Descriptor
+ getDescriptor() {
+ return emu.grasscutter.net.proto.PlayerLevelRewardUpdateNotifyOuterClass.internal_static_PlayerLevelRewardUpdateNotify_descriptor;
+ }
+
+ @java.lang.Override
+ protected com.google.protobuf.GeneratedMessageV3.FieldAccessorTable
+ internalGetFieldAccessorTable() {
+ return emu.grasscutter.net.proto.PlayerLevelRewardUpdateNotifyOuterClass.internal_static_PlayerLevelRewardUpdateNotify_fieldAccessorTable
+ .ensureFieldAccessorsInitialized(
+ emu.grasscutter.net.proto.PlayerLevelRewardUpdateNotifyOuterClass.PlayerLevelRewardUpdateNotify.class, emu.grasscutter.net.proto.PlayerLevelRewardUpdateNotifyOuterClass.PlayerLevelRewardUpdateNotify.Builder.class);
+ }
+
+ public static final int LEVELLIST_FIELD_NUMBER = 1;
+ private com.google.protobuf.Internal.IntList levelList_;
+ /**
+ * repeated uint32 levelList = 1;
+ * @return A list containing the levelList.
+ */
+ @java.lang.Override
+ public java.util.List
+ getLevelListList() {
+ return levelList_;
+ }
+ /**
+ * repeated uint32 levelList = 1;
+ * @return The count of levelList.
+ */
+ public int getLevelListCount() {
+ return levelList_.size();
+ }
+ /**
+ * repeated uint32 levelList = 1;
+ * @param index The index of the element to return.
+ * @return The levelList at the given index.
+ */
+ public int getLevelList(int index) {
+ return levelList_.getInt(index);
+ }
+ private int levelListMemoizedSerializedSize = -1;
+
+ private byte memoizedIsInitialized = -1;
+ @java.lang.Override
+ public final boolean isInitialized() {
+ byte isInitialized = memoizedIsInitialized;
+ if (isInitialized == 1) return true;
+ if (isInitialized == 0) return false;
+
+ memoizedIsInitialized = 1;
+ return true;
+ }
+
+ @java.lang.Override
+ public void writeTo(com.google.protobuf.CodedOutputStream output)
+ throws java.io.IOException {
+ getSerializedSize();
+ if (getLevelListList().size() > 0) {
+ output.writeUInt32NoTag(10);
+ output.writeUInt32NoTag(levelListMemoizedSerializedSize);
+ }
+ for (int i = 0; i < levelList_.size(); i++) {
+ output.writeUInt32NoTag(levelList_.getInt(i));
+ }
+ unknownFields.writeTo(output);
+ }
+
+ @java.lang.Override
+ public int getSerializedSize() {
+ int size = memoizedSize;
+ if (size != -1) return size;
+
+ size = 0;
+ {
+ int dataSize = 0;
+ for (int i = 0; i < levelList_.size(); i++) {
+ dataSize += com.google.protobuf.CodedOutputStream
+ .computeUInt32SizeNoTag(levelList_.getInt(i));
+ }
+ size += dataSize;
+ if (!getLevelListList().isEmpty()) {
+ size += 1;
+ size += com.google.protobuf.CodedOutputStream
+ .computeInt32SizeNoTag(dataSize);
+ }
+ levelListMemoizedSerializedSize = dataSize;
+ }
+ size += unknownFields.getSerializedSize();
+ memoizedSize = size;
+ return size;
+ }
+
+ @java.lang.Override
+ public boolean equals(final java.lang.Object obj) {
+ if (obj == this) {
+ return true;
+ }
+ if (!(obj instanceof emu.grasscutter.net.proto.PlayerLevelRewardUpdateNotifyOuterClass.PlayerLevelRewardUpdateNotify)) {
+ return super.equals(obj);
+ }
+ emu.grasscutter.net.proto.PlayerLevelRewardUpdateNotifyOuterClass.PlayerLevelRewardUpdateNotify other = (emu.grasscutter.net.proto.PlayerLevelRewardUpdateNotifyOuterClass.PlayerLevelRewardUpdateNotify) obj;
+
+ if (!getLevelListList()
+ .equals(other.getLevelListList())) return false;
+ if (!unknownFields.equals(other.unknownFields)) return false;
+ return true;
+ }
+
+ @java.lang.Override
+ public int hashCode() {
+ if (memoizedHashCode != 0) {
+ return memoizedHashCode;
+ }
+ int hash = 41;
+ hash = (19 * hash) + getDescriptor().hashCode();
+ if (getLevelListCount() > 0) {
+ hash = (37 * hash) + LEVELLIST_FIELD_NUMBER;
+ hash = (53 * hash) + getLevelListList().hashCode();
+ }
+ hash = (29 * hash) + unknownFields.hashCode();
+ memoizedHashCode = hash;
+ return hash;
+ }
+
+ public static emu.grasscutter.net.proto.PlayerLevelRewardUpdateNotifyOuterClass.PlayerLevelRewardUpdateNotify parseFrom(
+ java.nio.ByteBuffer data)
+ throws com.google.protobuf.InvalidProtocolBufferException {
+ return PARSER.parseFrom(data);
+ }
+ public static emu.grasscutter.net.proto.PlayerLevelRewardUpdateNotifyOuterClass.PlayerLevelRewardUpdateNotify parseFrom(
+ java.nio.ByteBuffer data,
+ com.google.protobuf.ExtensionRegistryLite extensionRegistry)
+ throws com.google.protobuf.InvalidProtocolBufferException {
+ return PARSER.parseFrom(data, extensionRegistry);
+ }
+ public static emu.grasscutter.net.proto.PlayerLevelRewardUpdateNotifyOuterClass.PlayerLevelRewardUpdateNotify parseFrom(
+ com.google.protobuf.ByteString data)
+ throws com.google.protobuf.InvalidProtocolBufferException {
+ return PARSER.parseFrom(data);
+ }
+ public static emu.grasscutter.net.proto.PlayerLevelRewardUpdateNotifyOuterClass.PlayerLevelRewardUpdateNotify parseFrom(
+ com.google.protobuf.ByteString data,
+ com.google.protobuf.ExtensionRegistryLite extensionRegistry)
+ throws com.google.protobuf.InvalidProtocolBufferException {
+ return PARSER.parseFrom(data, extensionRegistry);
+ }
+ public static emu.grasscutter.net.proto.PlayerLevelRewardUpdateNotifyOuterClass.PlayerLevelRewardUpdateNotify parseFrom(byte[] data)
+ throws com.google.protobuf.InvalidProtocolBufferException {
+ return PARSER.parseFrom(data);
+ }
+ public static emu.grasscutter.net.proto.PlayerLevelRewardUpdateNotifyOuterClass.PlayerLevelRewardUpdateNotify parseFrom(
+ byte[] data,
+ com.google.protobuf.ExtensionRegistryLite extensionRegistry)
+ throws com.google.protobuf.InvalidProtocolBufferException {
+ return PARSER.parseFrom(data, extensionRegistry);
+ }
+ public static emu.grasscutter.net.proto.PlayerLevelRewardUpdateNotifyOuterClass.PlayerLevelRewardUpdateNotify parseFrom(java.io.InputStream input)
+ throws java.io.IOException {
+ return com.google.protobuf.GeneratedMessageV3
+ .parseWithIOException(PARSER, input);
+ }
+ public static emu.grasscutter.net.proto.PlayerLevelRewardUpdateNotifyOuterClass.PlayerLevelRewardUpdateNotify parseFrom(
+ java.io.InputStream input,
+ com.google.protobuf.ExtensionRegistryLite extensionRegistry)
+ throws java.io.IOException {
+ return com.google.protobuf.GeneratedMessageV3
+ .parseWithIOException(PARSER, input, extensionRegistry);
+ }
+ public static emu.grasscutter.net.proto.PlayerLevelRewardUpdateNotifyOuterClass.PlayerLevelRewardUpdateNotify parseDelimitedFrom(java.io.InputStream input)
+ throws java.io.IOException {
+ return com.google.protobuf.GeneratedMessageV3
+ .parseDelimitedWithIOException(PARSER, input);
+ }
+ public static emu.grasscutter.net.proto.PlayerLevelRewardUpdateNotifyOuterClass.PlayerLevelRewardUpdateNotify parseDelimitedFrom(
+ java.io.InputStream input,
+ com.google.protobuf.ExtensionRegistryLite extensionRegistry)
+ throws java.io.IOException {
+ return com.google.protobuf.GeneratedMessageV3
+ .parseDelimitedWithIOException(PARSER, input, extensionRegistry);
+ }
+ public static emu.grasscutter.net.proto.PlayerLevelRewardUpdateNotifyOuterClass.PlayerLevelRewardUpdateNotify parseFrom(
+ com.google.protobuf.CodedInputStream input)
+ throws java.io.IOException {
+ return com.google.protobuf.GeneratedMessageV3
+ .parseWithIOException(PARSER, input);
+ }
+ public static emu.grasscutter.net.proto.PlayerLevelRewardUpdateNotifyOuterClass.PlayerLevelRewardUpdateNotify parseFrom(
+ com.google.protobuf.CodedInputStream input,
+ com.google.protobuf.ExtensionRegistryLite extensionRegistry)
+ throws java.io.IOException {
+ return com.google.protobuf.GeneratedMessageV3
+ .parseWithIOException(PARSER, input, extensionRegistry);
+ }
+
+ @java.lang.Override
+ public Builder newBuilderForType() { return newBuilder(); }
+ public static Builder newBuilder() {
+ return DEFAULT_INSTANCE.toBuilder();
+ }
+ public static Builder newBuilder(emu.grasscutter.net.proto.PlayerLevelRewardUpdateNotifyOuterClass.PlayerLevelRewardUpdateNotify prototype) {
+ return DEFAULT_INSTANCE.toBuilder().mergeFrom(prototype);
+ }
+ @java.lang.Override
+ public Builder toBuilder() {
+ return this == DEFAULT_INSTANCE
+ ? new Builder() : new Builder().mergeFrom(this);
+ }
+
+ @java.lang.Override
+ protected Builder newBuilderForType(
+ com.google.protobuf.GeneratedMessageV3.BuilderParent parent) {
+ Builder builder = new Builder(parent);
+ return builder;
+ }
+ /**
+ * Protobuf type {@code PlayerLevelRewardUpdateNotify}
+ */
+ public static final class Builder extends
+ com.google.protobuf.GeneratedMessageV3.Builder implements
+ // @@protoc_insertion_point(builder_implements:PlayerLevelRewardUpdateNotify)
+ emu.grasscutter.net.proto.PlayerLevelRewardUpdateNotifyOuterClass.PlayerLevelRewardUpdateNotifyOrBuilder {
+ public static final com.google.protobuf.Descriptors.Descriptor
+ getDescriptor() {
+ return emu.grasscutter.net.proto.PlayerLevelRewardUpdateNotifyOuterClass.internal_static_PlayerLevelRewardUpdateNotify_descriptor;
+ }
+
+ @java.lang.Override
+ protected com.google.protobuf.GeneratedMessageV3.FieldAccessorTable
+ internalGetFieldAccessorTable() {
+ return emu.grasscutter.net.proto.PlayerLevelRewardUpdateNotifyOuterClass.internal_static_PlayerLevelRewardUpdateNotify_fieldAccessorTable
+ .ensureFieldAccessorsInitialized(
+ emu.grasscutter.net.proto.PlayerLevelRewardUpdateNotifyOuterClass.PlayerLevelRewardUpdateNotify.class, emu.grasscutter.net.proto.PlayerLevelRewardUpdateNotifyOuterClass.PlayerLevelRewardUpdateNotify.Builder.class);
+ }
+
+ // Construct using emu.grasscutter.net.proto.PlayerLevelRewardUpdateNotifyOuterClass.PlayerLevelRewardUpdateNotify.newBuilder()
+ private Builder() {
+ maybeForceBuilderInitialization();
+ }
+
+ private Builder(
+ com.google.protobuf.GeneratedMessageV3.BuilderParent parent) {
+ super(parent);
+ maybeForceBuilderInitialization();
+ }
+ private void maybeForceBuilderInitialization() {
+ if (com.google.protobuf.GeneratedMessageV3
+ .alwaysUseFieldBuilders) {
+ }
+ }
+ @java.lang.Override
+ public Builder clear() {
+ super.clear();
+ levelList_ = emptyIntList();
+ bitField0_ = (bitField0_ & ~0x00000001);
+ return this;
+ }
+
+ @java.lang.Override
+ public com.google.protobuf.Descriptors.Descriptor
+ getDescriptorForType() {
+ return emu.grasscutter.net.proto.PlayerLevelRewardUpdateNotifyOuterClass.internal_static_PlayerLevelRewardUpdateNotify_descriptor;
+ }
+
+ @java.lang.Override
+ public emu.grasscutter.net.proto.PlayerLevelRewardUpdateNotifyOuterClass.PlayerLevelRewardUpdateNotify getDefaultInstanceForType() {
+ return emu.grasscutter.net.proto.PlayerLevelRewardUpdateNotifyOuterClass.PlayerLevelRewardUpdateNotify.getDefaultInstance();
+ }
+
+ @java.lang.Override
+ public emu.grasscutter.net.proto.PlayerLevelRewardUpdateNotifyOuterClass.PlayerLevelRewardUpdateNotify build() {
+ emu.grasscutter.net.proto.PlayerLevelRewardUpdateNotifyOuterClass.PlayerLevelRewardUpdateNotify result = buildPartial();
+ if (!result.isInitialized()) {
+ throw newUninitializedMessageException(result);
+ }
+ return result;
+ }
+
+ @java.lang.Override
+ public emu.grasscutter.net.proto.PlayerLevelRewardUpdateNotifyOuterClass.PlayerLevelRewardUpdateNotify buildPartial() {
+ emu.grasscutter.net.proto.PlayerLevelRewardUpdateNotifyOuterClass.PlayerLevelRewardUpdateNotify result = new emu.grasscutter.net.proto.PlayerLevelRewardUpdateNotifyOuterClass.PlayerLevelRewardUpdateNotify(this);
+ int from_bitField0_ = bitField0_;
+ if (((bitField0_ & 0x00000001) != 0)) {
+ levelList_.makeImmutable();
+ bitField0_ = (bitField0_ & ~0x00000001);
+ }
+ result.levelList_ = levelList_;
+ onBuilt();
+ return result;
+ }
+
+ @java.lang.Override
+ public Builder clone() {
+ return super.clone();
+ }
+ @java.lang.Override
+ public Builder setField(
+ com.google.protobuf.Descriptors.FieldDescriptor field,
+ java.lang.Object value) {
+ return super.setField(field, value);
+ }
+ @java.lang.Override
+ public Builder clearField(
+ com.google.protobuf.Descriptors.FieldDescriptor field) {
+ return super.clearField(field);
+ }
+ @java.lang.Override
+ public Builder clearOneof(
+ com.google.protobuf.Descriptors.OneofDescriptor oneof) {
+ return super.clearOneof(oneof);
+ }
+ @java.lang.Override
+ public Builder setRepeatedField(
+ com.google.protobuf.Descriptors.FieldDescriptor field,
+ int index, java.lang.Object value) {
+ return super.setRepeatedField(field, index, value);
+ }
+ @java.lang.Override
+ public Builder addRepeatedField(
+ com.google.protobuf.Descriptors.FieldDescriptor field,
+ java.lang.Object value) {
+ return super.addRepeatedField(field, value);
+ }
+ @java.lang.Override
+ public Builder mergeFrom(com.google.protobuf.Message other) {
+ if (other instanceof emu.grasscutter.net.proto.PlayerLevelRewardUpdateNotifyOuterClass.PlayerLevelRewardUpdateNotify) {
+ return mergeFrom((emu.grasscutter.net.proto.PlayerLevelRewardUpdateNotifyOuterClass.PlayerLevelRewardUpdateNotify)other);
+ } else {
+ super.mergeFrom(other);
+ return this;
+ }
+ }
+
+ public Builder mergeFrom(emu.grasscutter.net.proto.PlayerLevelRewardUpdateNotifyOuterClass.PlayerLevelRewardUpdateNotify other) {
+ if (other == emu.grasscutter.net.proto.PlayerLevelRewardUpdateNotifyOuterClass.PlayerLevelRewardUpdateNotify.getDefaultInstance()) return this;
+ if (!other.levelList_.isEmpty()) {
+ if (levelList_.isEmpty()) {
+ levelList_ = other.levelList_;
+ bitField0_ = (bitField0_ & ~0x00000001);
+ } else {
+ ensureLevelListIsMutable();
+ levelList_.addAll(other.levelList_);
+ }
+ onChanged();
+ }
+ this.mergeUnknownFields(other.unknownFields);
+ onChanged();
+ return this;
+ }
+
+ @java.lang.Override
+ public final boolean isInitialized() {
+ return true;
+ }
+
+ @java.lang.Override
+ public Builder mergeFrom(
+ com.google.protobuf.CodedInputStream input,
+ com.google.protobuf.ExtensionRegistryLite extensionRegistry)
+ throws java.io.IOException {
+ emu.grasscutter.net.proto.PlayerLevelRewardUpdateNotifyOuterClass.PlayerLevelRewardUpdateNotify parsedMessage = null;
+ try {
+ parsedMessage = PARSER.parsePartialFrom(input, extensionRegistry);
+ } catch (com.google.protobuf.InvalidProtocolBufferException e) {
+ parsedMessage = (emu.grasscutter.net.proto.PlayerLevelRewardUpdateNotifyOuterClass.PlayerLevelRewardUpdateNotify) e.getUnfinishedMessage();
+ throw e.unwrapIOException();
+ } finally {
+ if (parsedMessage != null) {
+ mergeFrom(parsedMessage);
+ }
+ }
+ return this;
+ }
+ private int bitField0_;
+
+ private com.google.protobuf.Internal.IntList levelList_ = emptyIntList();
+ private void ensureLevelListIsMutable() {
+ if (!((bitField0_ & 0x00000001) != 0)) {
+ levelList_ = mutableCopy(levelList_);
+ bitField0_ |= 0x00000001;
+ }
+ }
+ /**
+ * repeated uint32 levelList = 1;
+ * @return A list containing the levelList.
+ */
+ public java.util.List
+ getLevelListList() {
+ return ((bitField0_ & 0x00000001) != 0) ?
+ java.util.Collections.unmodifiableList(levelList_) : levelList_;
+ }
+ /**
+ * repeated uint32 levelList = 1;
+ * @return The count of levelList.
+ */
+ public int getLevelListCount() {
+ return levelList_.size();
+ }
+ /**
+ * repeated uint32 levelList = 1;
+ * @param index The index of the element to return.
+ * @return The levelList at the given index.
+ */
+ public int getLevelList(int index) {
+ return levelList_.getInt(index);
+ }
+ /**
+ * repeated uint32 levelList = 1;
+ * @param index The index to set the value at.
+ * @param value The levelList to set.
+ * @return This builder for chaining.
+ */
+ public Builder setLevelList(
+ int index, int value) {
+ ensureLevelListIsMutable();
+ levelList_.setInt(index, value);
+ onChanged();
+ return this;
+ }
+ /**
+ * repeated uint32 levelList = 1;
+ * @param value The levelList to add.
+ * @return This builder for chaining.
+ */
+ public Builder addLevelList(int value) {
+ ensureLevelListIsMutable();
+ levelList_.addInt(value);
+ onChanged();
+ return this;
+ }
+ /**
+ * repeated uint32 levelList = 1;
+ * @param values The levelList to add.
+ * @return This builder for chaining.
+ */
+ public Builder addAllLevelList(
+ java.lang.Iterable extends java.lang.Integer> values) {
+ ensureLevelListIsMutable();
+ com.google.protobuf.AbstractMessageLite.Builder.addAll(
+ values, levelList_);
+ onChanged();
+ return this;
+ }
+ /**
+ * repeated uint32 levelList = 1;
+ * @return This builder for chaining.
+ */
+ public Builder clearLevelList() {
+ levelList_ = emptyIntList();
+ bitField0_ = (bitField0_ & ~0x00000001);
+ onChanged();
+ return this;
+ }
+ @java.lang.Override
+ public final Builder setUnknownFields(
+ final com.google.protobuf.UnknownFieldSet unknownFields) {
+ return super.setUnknownFields(unknownFields);
+ }
+
+ @java.lang.Override
+ public final Builder mergeUnknownFields(
+ final com.google.protobuf.UnknownFieldSet unknownFields) {
+ return super.mergeUnknownFields(unknownFields);
+ }
+
+
+ // @@protoc_insertion_point(builder_scope:PlayerLevelRewardUpdateNotify)
+ }
+
+ // @@protoc_insertion_point(class_scope:PlayerLevelRewardUpdateNotify)
+ private static final emu.grasscutter.net.proto.PlayerLevelRewardUpdateNotifyOuterClass.PlayerLevelRewardUpdateNotify DEFAULT_INSTANCE;
+ static {
+ DEFAULT_INSTANCE = new emu.grasscutter.net.proto.PlayerLevelRewardUpdateNotifyOuterClass.PlayerLevelRewardUpdateNotify();
+ }
+
+ public static emu.grasscutter.net.proto.PlayerLevelRewardUpdateNotifyOuterClass.PlayerLevelRewardUpdateNotify getDefaultInstance() {
+ return DEFAULT_INSTANCE;
+ }
+
+ private static final com.google.protobuf.Parser
+ PARSER = new com.google.protobuf.AbstractParser() {
+ @java.lang.Override
+ public PlayerLevelRewardUpdateNotify parsePartialFrom(
+ com.google.protobuf.CodedInputStream input,
+ com.google.protobuf.ExtensionRegistryLite extensionRegistry)
+ throws com.google.protobuf.InvalidProtocolBufferException {
+ return new PlayerLevelRewardUpdateNotify(input, extensionRegistry);
+ }
+ };
+
+ public static com.google.protobuf.Parser parser() {
+ return PARSER;
+ }
+
+ @java.lang.Override
+ public com.google.protobuf.Parser getParserForType() {
+ return PARSER;
+ }
+
+ @java.lang.Override
+ public emu.grasscutter.net.proto.PlayerLevelRewardUpdateNotifyOuterClass.PlayerLevelRewardUpdateNotify getDefaultInstanceForType() {
+ return DEFAULT_INSTANCE;
+ }
+
+ }
+
+ private static final com.google.protobuf.Descriptors.Descriptor
+ internal_static_PlayerLevelRewardUpdateNotify_descriptor;
+ private static final
+ com.google.protobuf.GeneratedMessageV3.FieldAccessorTable
+ internal_static_PlayerLevelRewardUpdateNotify_fieldAccessorTable;
+
+ public static com.google.protobuf.Descriptors.FileDescriptor
+ getDescriptor() {
+ return descriptor;
+ }
+ private static com.google.protobuf.Descriptors.FileDescriptor
+ descriptor;
+ static {
+ java.lang.String[] descriptorData = {
+ "\n#PlayerLevelRewardUpdateNotify.proto\"2\n" +
+ "\035PlayerLevelRewardUpdateNotify\022\021\n\tlevelL" +
+ "ist\030\001 \003(\rB\033\n\031emu.grasscutter.net.protob\006" +
+ "proto3"
+ };
+ descriptor = com.google.protobuf.Descriptors.FileDescriptor
+ .internalBuildGeneratedFileFrom(descriptorData,
+ new com.google.protobuf.Descriptors.FileDescriptor[] {
+ });
+ internal_static_PlayerLevelRewardUpdateNotify_descriptor =
+ getDescriptor().getMessageTypes().get(0);
+ internal_static_PlayerLevelRewardUpdateNotify_fieldAccessorTable = new
+ com.google.protobuf.GeneratedMessageV3.FieldAccessorTable(
+ internal_static_PlayerLevelRewardUpdateNotify_descriptor,
+ new java.lang.String[] { "LevelList", });
+ }
+
+ // @@protoc_insertion_point(outer_class_scope)
+}
diff --git a/src/main/java/emu/grasscutter/net/proto/SetPlayerBirthdayReqOuterClass.java b/src/main/java/emu/grasscutter/net/proto/SetPlayerBirthdayReqOuterClass.java
index d49e0f983..7f174dd45 100644
--- a/src/main/java/emu/grasscutter/net/proto/SetPlayerBirthdayReqOuterClass.java
+++ b/src/main/java/emu/grasscutter/net/proto/SetPlayerBirthdayReqOuterClass.java
@@ -1,5 +1,5 @@
// Generated by the protocol buffer compiler. DO NOT EDIT!
-// source: proto/SetPlayerBirthdayReq.proto
+// source: SetPlayerBirthdayReq.proto
package emu.grasscutter.net.proto;
@@ -19,19 +19,19 @@ public final class SetPlayerBirthdayReqOuterClass {
com.google.protobuf.MessageOrBuilder {
/**
- * .Birthday birth = 1;
- * @return Whether the birth field is set.
+ * .Birthday birthday = 1;
+ * @return Whether the birthday field is set.
*/
- boolean hasBirth();
+ boolean hasBirthday();
/**
- * .Birthday birth = 1;
- * @return The birth.
+ * .Birthday birthday = 1;
+ * @return The birthday.
*/
- emu.grasscutter.net.proto.BirthdayOuterClass.Birthday getBirth();
+ emu.grasscutter.net.proto.BirthdayOuterClass.Birthday getBirthday();
/**
- * .Birthday birth = 1;
+ * .Birthday birthday = 1;
*/
- emu.grasscutter.net.proto.BirthdayOuterClass.BirthdayOrBuilder getBirthOrBuilder();
+ emu.grasscutter.net.proto.BirthdayOuterClass.BirthdayOrBuilder getBirthdayOrBuilder();
}
/**
* Protobuf type {@code SetPlayerBirthdayReq}
@@ -80,13 +80,13 @@ public final class SetPlayerBirthdayReqOuterClass {
break;
case 10: {
emu.grasscutter.net.proto.BirthdayOuterClass.Birthday.Builder subBuilder = null;
- if (birth_ != null) {
- subBuilder = birth_.toBuilder();
+ if (birthday_ != null) {
+ subBuilder = birthday_.toBuilder();
}
- birth_ = input.readMessage(emu.grasscutter.net.proto.BirthdayOuterClass.Birthday.parser(), extensionRegistry);
+ birthday_ = input.readMessage(emu.grasscutter.net.proto.BirthdayOuterClass.Birthday.parser(), extensionRegistry);
if (subBuilder != null) {
- subBuilder.mergeFrom(birth_);
- birth_ = subBuilder.buildPartial();
+ subBuilder.mergeFrom(birthday_);
+ birthday_ = subBuilder.buildPartial();
}
break;
@@ -125,30 +125,30 @@ public final class SetPlayerBirthdayReqOuterClass {
emu.grasscutter.net.proto.SetPlayerBirthdayReqOuterClass.SetPlayerBirthdayReq.class, emu.grasscutter.net.proto.SetPlayerBirthdayReqOuterClass.SetPlayerBirthdayReq.Builder.class);
}
- public static final int BIRTH_FIELD_NUMBER = 1;
- private emu.grasscutter.net.proto.BirthdayOuterClass.Birthday birth_;
+ public static final int BIRTHDAY_FIELD_NUMBER = 1;
+ private emu.grasscutter.net.proto.BirthdayOuterClass.Birthday birthday_;
/**
- * .Birthday birth = 1;
- * @return Whether the birth field is set.
+ * .Birthday birthday = 1;
+ * @return Whether the birthday field is set.
*/
@java.lang.Override
- public boolean hasBirth() {
- return birth_ != null;
+ public boolean hasBirthday() {
+ return birthday_ != null;
}
/**
- * .Birthday birth = 1;
- * @return The birth.
+ * .Birthday birthday = 1;
+ * @return The birthday.
*/
@java.lang.Override
- public emu.grasscutter.net.proto.BirthdayOuterClass.Birthday getBirth() {
- return birth_ == null ? emu.grasscutter.net.proto.BirthdayOuterClass.Birthday.getDefaultInstance() : birth_;
+ public emu.grasscutter.net.proto.BirthdayOuterClass.Birthday getBirthday() {
+ return birthday_ == null ? emu.grasscutter.net.proto.BirthdayOuterClass.Birthday.getDefaultInstance() : birthday_;
}
/**
- * .Birthday birth = 1;
+ * .Birthday birthday = 1;
*/
@java.lang.Override
- public emu.grasscutter.net.proto.BirthdayOuterClass.BirthdayOrBuilder getBirthOrBuilder() {
- return getBirth();
+ public emu.grasscutter.net.proto.BirthdayOuterClass.BirthdayOrBuilder getBirthdayOrBuilder() {
+ return getBirthday();
}
private byte memoizedIsInitialized = -1;
@@ -165,8 +165,8 @@ public final class SetPlayerBirthdayReqOuterClass {
@java.lang.Override
public void writeTo(com.google.protobuf.CodedOutputStream output)
throws java.io.IOException {
- if (birth_ != null) {
- output.writeMessage(1, getBirth());
+ if (birthday_ != null) {
+ output.writeMessage(1, getBirthday());
}
unknownFields.writeTo(output);
}
@@ -177,9 +177,9 @@ public final class SetPlayerBirthdayReqOuterClass {
if (size != -1) return size;
size = 0;
- if (birth_ != null) {
+ if (birthday_ != null) {
size += com.google.protobuf.CodedOutputStream
- .computeMessageSize(1, getBirth());
+ .computeMessageSize(1, getBirthday());
}
size += unknownFields.getSerializedSize();
memoizedSize = size;
@@ -196,10 +196,10 @@ public final class SetPlayerBirthdayReqOuterClass {
}
emu.grasscutter.net.proto.SetPlayerBirthdayReqOuterClass.SetPlayerBirthdayReq other = (emu.grasscutter.net.proto.SetPlayerBirthdayReqOuterClass.SetPlayerBirthdayReq) obj;
- if (hasBirth() != other.hasBirth()) return false;
- if (hasBirth()) {
- if (!getBirth()
- .equals(other.getBirth())) return false;
+ if (hasBirthday() != other.hasBirthday()) return false;
+ if (hasBirthday()) {
+ if (!getBirthday()
+ .equals(other.getBirthday())) return false;
}
if (!unknownFields.equals(other.unknownFields)) return false;
return true;
@@ -212,9 +212,9 @@ public final class SetPlayerBirthdayReqOuterClass {
}
int hash = 41;
hash = (19 * hash) + getDescriptor().hashCode();
- if (hasBirth()) {
- hash = (37 * hash) + BIRTH_FIELD_NUMBER;
- hash = (53 * hash) + getBirth().hashCode();
+ if (hasBirthday()) {
+ hash = (37 * hash) + BIRTHDAY_FIELD_NUMBER;
+ hash = (53 * hash) + getBirthday().hashCode();
}
hash = (29 * hash) + unknownFields.hashCode();
memoizedHashCode = hash;
@@ -349,11 +349,11 @@ public final class SetPlayerBirthdayReqOuterClass {
@java.lang.Override
public Builder clear() {
super.clear();
- if (birthBuilder_ == null) {
- birth_ = null;
+ if (birthdayBuilder_ == null) {
+ birthday_ = null;
} else {
- birth_ = null;
- birthBuilder_ = null;
+ birthday_ = null;
+ birthdayBuilder_ = null;
}
return this;
}
@@ -381,10 +381,10 @@ public final class SetPlayerBirthdayReqOuterClass {
@java.lang.Override
public emu.grasscutter.net.proto.SetPlayerBirthdayReqOuterClass.SetPlayerBirthdayReq buildPartial() {
emu.grasscutter.net.proto.SetPlayerBirthdayReqOuterClass.SetPlayerBirthdayReq result = new emu.grasscutter.net.proto.SetPlayerBirthdayReqOuterClass.SetPlayerBirthdayReq(this);
- if (birthBuilder_ == null) {
- result.birth_ = birth_;
+ if (birthdayBuilder_ == null) {
+ result.birthday_ = birthday_;
} else {
- result.birth_ = birthBuilder_.build();
+ result.birthday_ = birthdayBuilder_.build();
}
onBuilt();
return result;
@@ -434,8 +434,8 @@ public final class SetPlayerBirthdayReqOuterClass {
public Builder mergeFrom(emu.grasscutter.net.proto.SetPlayerBirthdayReqOuterClass.SetPlayerBirthdayReq other) {
if (other == emu.grasscutter.net.proto.SetPlayerBirthdayReqOuterClass.SetPlayerBirthdayReq.getDefaultInstance()) return this;
- if (other.hasBirth()) {
- mergeBirth(other.getBirth());
+ if (other.hasBirthday()) {
+ mergeBirthday(other.getBirthday());
}
this.mergeUnknownFields(other.unknownFields);
onChanged();
@@ -466,123 +466,123 @@ public final class SetPlayerBirthdayReqOuterClass {
return this;
}
- private emu.grasscutter.net.proto.BirthdayOuterClass.Birthday birth_;
+ private emu.grasscutter.net.proto.BirthdayOuterClass.Birthday birthday_;
private com.google.protobuf.SingleFieldBuilderV3<
- emu.grasscutter.net.proto.BirthdayOuterClass.Birthday, emu.grasscutter.net.proto.BirthdayOuterClass.Birthday.Builder, emu.grasscutter.net.proto.BirthdayOuterClass.BirthdayOrBuilder> birthBuilder_;
+ emu.grasscutter.net.proto.BirthdayOuterClass.Birthday, emu.grasscutter.net.proto.BirthdayOuterClass.Birthday.Builder, emu.grasscutter.net.proto.BirthdayOuterClass.BirthdayOrBuilder> birthdayBuilder_;
/**
- * .Birthday birth = 1;
- * @return Whether the birth field is set.
+ * .Birthday birthday = 1;
+ * @return Whether the birthday field is set.
*/
- public boolean hasBirth() {
- return birthBuilder_ != null || birth_ != null;
+ public boolean hasBirthday() {
+ return birthdayBuilder_ != null || birthday_ != null;
}
/**
- * .Birthday birth = 1;
- * @return The birth.
+ * .Birthday birthday = 1;
+ * @return The birthday.
*/
- public emu.grasscutter.net.proto.BirthdayOuterClass.Birthday getBirth() {
- if (birthBuilder_ == null) {
- return birth_ == null ? emu.grasscutter.net.proto.BirthdayOuterClass.Birthday.getDefaultInstance() : birth_;
+ public emu.grasscutter.net.proto.BirthdayOuterClass.Birthday getBirthday() {
+ if (birthdayBuilder_ == null) {
+ return birthday_ == null ? emu.grasscutter.net.proto.BirthdayOuterClass.Birthday.getDefaultInstance() : birthday_;
} else {
- return birthBuilder_.getMessage();
+ return birthdayBuilder_.getMessage();
}
}
/**
- * .Birthday birth = 1;
+ * .Birthday birthday = 1;
*/
- public Builder setBirth(emu.grasscutter.net.proto.BirthdayOuterClass.Birthday value) {
- if (birthBuilder_ == null) {
+ public Builder setBirthday(emu.grasscutter.net.proto.BirthdayOuterClass.Birthday value) {
+ if (birthdayBuilder_ == null) {
if (value == null) {
throw new NullPointerException();
}
- birth_ = value;
+ birthday_ = value;
onChanged();
} else {
- birthBuilder_.setMessage(value);
+ birthdayBuilder_.setMessage(value);
}
return this;
}
/**
- * .Birthday birth = 1;
+ * .Birthday birthday = 1;
*/
- public Builder setBirth(
+ public Builder setBirthday(
emu.grasscutter.net.proto.BirthdayOuterClass.Birthday.Builder builderForValue) {
- if (birthBuilder_ == null) {
- birth_ = builderForValue.build();
+ if (birthdayBuilder_ == null) {
+ birthday_ = builderForValue.build();
onChanged();
} else {
- birthBuilder_.setMessage(builderForValue.build());
+ birthdayBuilder_.setMessage(builderForValue.build());
}
return this;
}
/**
- * .Birthday birth = 1;
+ * .Birthday birthday = 1;
*/
- public Builder mergeBirth(emu.grasscutter.net.proto.BirthdayOuterClass.Birthday value) {
- if (birthBuilder_ == null) {
- if (birth_ != null) {
- birth_ =
- emu.grasscutter.net.proto.BirthdayOuterClass.Birthday.newBuilder(birth_).mergeFrom(value).buildPartial();
+ public Builder mergeBirthday(emu.grasscutter.net.proto.BirthdayOuterClass.Birthday value) {
+ if (birthdayBuilder_ == null) {
+ if (birthday_ != null) {
+ birthday_ =
+ emu.grasscutter.net.proto.BirthdayOuterClass.Birthday.newBuilder(birthday_).mergeFrom(value).buildPartial();
} else {
- birth_ = value;
+ birthday_ = value;
}
onChanged();
} else {
- birthBuilder_.mergeFrom(value);
+ birthdayBuilder_.mergeFrom(value);
}
return this;
}
/**
- * .Birthday birth = 1;
+ * .Birthday birthday = 1;
*/
- public Builder clearBirth() {
- if (birthBuilder_ == null) {
- birth_ = null;
+ public Builder clearBirthday() {
+ if (birthdayBuilder_ == null) {
+ birthday_ = null;
onChanged();
} else {
- birth_ = null;
- birthBuilder_ = null;
+ birthday_ = null;
+ birthdayBuilder_ = null;
}
return this;
}
/**
- * .Birthday birth = 1;
+ * .Birthday birthday = 1;
*/
- public emu.grasscutter.net.proto.BirthdayOuterClass.Birthday.Builder getBirthBuilder() {
+ public emu.grasscutter.net.proto.BirthdayOuterClass.Birthday.Builder getBirthdayBuilder() {
onChanged();
- return getBirthFieldBuilder().getBuilder();
+ return getBirthdayFieldBuilder().getBuilder();
}
/**
- * .Birthday birth = 1;
+ * .Birthday birthday = 1;
*/
- public emu.grasscutter.net.proto.BirthdayOuterClass.BirthdayOrBuilder getBirthOrBuilder() {
- if (birthBuilder_ != null) {
- return birthBuilder_.getMessageOrBuilder();
+ public emu.grasscutter.net.proto.BirthdayOuterClass.BirthdayOrBuilder getBirthdayOrBuilder() {
+ if (birthdayBuilder_ != null) {
+ return birthdayBuilder_.getMessageOrBuilder();
} else {
- return birth_ == null ?
- emu.grasscutter.net.proto.BirthdayOuterClass.Birthday.getDefaultInstance() : birth_;
+ return birthday_ == null ?
+ emu.grasscutter.net.proto.BirthdayOuterClass.Birthday.getDefaultInstance() : birthday_;
}
}
/**
- * .Birthday birth = 1;
+ * .Birthday birthday = 1;
*/
private com.google.protobuf.SingleFieldBuilderV3<
emu.grasscutter.net.proto.BirthdayOuterClass.Birthday, emu.grasscutter.net.proto.BirthdayOuterClass.Birthday.Builder, emu.grasscutter.net.proto.BirthdayOuterClass.BirthdayOrBuilder>
- getBirthFieldBuilder() {
- if (birthBuilder_ == null) {
- birthBuilder_ = new com.google.protobuf.SingleFieldBuilderV3<
+ getBirthdayFieldBuilder() {
+ if (birthdayBuilder_ == null) {
+ birthdayBuilder_ = new com.google.protobuf.SingleFieldBuilderV3<
emu.grasscutter.net.proto.BirthdayOuterClass.Birthday, emu.grasscutter.net.proto.BirthdayOuterClass.Birthday.Builder, emu.grasscutter.net.proto.BirthdayOuterClass.BirthdayOrBuilder>(
- getBirth(),
+ getBirthday(),
getParentForChildren(),
isClean());
- birth_ = null;
+ birthday_ = null;
}
- return birthBuilder_;
+ return birthdayBuilder_;
}
@java.lang.Override
public final Builder setUnknownFields(
@@ -651,10 +651,10 @@ public final class SetPlayerBirthdayReqOuterClass {
descriptor;
static {
java.lang.String[] descriptorData = {
- "\n proto/SetPlayerBirthdayReq.proto\032\024prot" +
- "o/Birthday.proto\"0\n\024SetPlayerBirthdayReq" +
- "\022\030\n\005birth\030\001 \001(\0132\t.BirthdayB\033\n\031emu.grassc" +
- "utter.net.protob\006proto3"
+ "\n\032SetPlayerBirthdayReq.proto\032\016Birthday.p" +
+ "roto\"3\n\024SetPlayerBirthdayReq\022\033\n\010birthday" +
+ "\030\001 \001(\0132\t.BirthdayB\033\n\031emu.grasscutter.net" +
+ ".protob\006proto3"
};
descriptor = com.google.protobuf.Descriptors.FileDescriptor
.internalBuildGeneratedFileFrom(descriptorData,
@@ -666,7 +666,7 @@ public final class SetPlayerBirthdayReqOuterClass {
internal_static_SetPlayerBirthdayReq_fieldAccessorTable = new
com.google.protobuf.GeneratedMessageV3.FieldAccessorTable(
internal_static_SetPlayerBirthdayReq_descriptor,
- new java.lang.String[] { "Birth", });
+ new java.lang.String[] { "Birthday", });
emu.grasscutter.net.proto.BirthdayOuterClass.getDescriptor();
}
diff --git a/src/main/java/emu/grasscutter/net/proto/SetPlayerBirthdayRspOuterClass.java b/src/main/java/emu/grasscutter/net/proto/SetPlayerBirthdayRspOuterClass.java
index acb24b8b2..e77af9ae9 100644
--- a/src/main/java/emu/grasscutter/net/proto/SetPlayerBirthdayRspOuterClass.java
+++ b/src/main/java/emu/grasscutter/net/proto/SetPlayerBirthdayRspOuterClass.java
@@ -1,5 +1,5 @@
// Generated by the protocol buffer compiler. DO NOT EDIT!
-// source: proto/SetPlayerBirthdayRsp.proto
+// source: SetPlayerBirthdayRsp.proto
package emu.grasscutter.net.proto;
@@ -19,19 +19,25 @@ public final class SetPlayerBirthdayRspOuterClass {
com.google.protobuf.MessageOrBuilder {
/**
- * .Birthday birth = 1;
- * @return Whether the birth field is set.
+ * int32 retcode = 1;
+ * @return The retcode.
*/
- boolean hasBirth();
+ int getRetcode();
+
/**
- * .Birthday birth = 1;
- * @return The birth.
+ * .Birthday birthday = 2;
+ * @return Whether the birthday field is set.
*/
- emu.grasscutter.net.proto.BirthdayOuterClass.Birthday getBirth();
+ boolean hasBirthday();
/**
- * .Birthday birth = 1;
+ * .Birthday birthday = 2;
+ * @return The birthday.
*/
- emu.grasscutter.net.proto.BirthdayOuterClass.BirthdayOrBuilder getBirthOrBuilder();
+ emu.grasscutter.net.proto.BirthdayOuterClass.Birthday getBirthday();
+ /**
+ * .Birthday birthday = 2;
+ */
+ emu.grasscutter.net.proto.BirthdayOuterClass.BirthdayOrBuilder getBirthdayOrBuilder();
}
/**
* Protobuf type {@code SetPlayerBirthdayRsp}
@@ -78,15 +84,20 @@ public final class SetPlayerBirthdayRspOuterClass {
case 0:
done = true;
break;
- case 10: {
+ case 8: {
+
+ retcode_ = input.readInt32();
+ break;
+ }
+ case 18: {
emu.grasscutter.net.proto.BirthdayOuterClass.Birthday.Builder subBuilder = null;
- if (birth_ != null) {
- subBuilder = birth_.toBuilder();
+ if (birthday_ != null) {
+ subBuilder = birthday_.toBuilder();
}
- birth_ = input.readMessage(emu.grasscutter.net.proto.BirthdayOuterClass.Birthday.parser(), extensionRegistry);
+ birthday_ = input.readMessage(emu.grasscutter.net.proto.BirthdayOuterClass.Birthday.parser(), extensionRegistry);
if (subBuilder != null) {
- subBuilder.mergeFrom(birth_);
- birth_ = subBuilder.buildPartial();
+ subBuilder.mergeFrom(birthday_);
+ birthday_ = subBuilder.buildPartial();
}
break;
@@ -125,30 +136,41 @@ public final class SetPlayerBirthdayRspOuterClass {
emu.grasscutter.net.proto.SetPlayerBirthdayRspOuterClass.SetPlayerBirthdayRsp.class, emu.grasscutter.net.proto.SetPlayerBirthdayRspOuterClass.SetPlayerBirthdayRsp.Builder.class);
}
- public static final int BIRTH_FIELD_NUMBER = 1;
- private emu.grasscutter.net.proto.BirthdayOuterClass.Birthday birth_;
+ public static final int RETCODE_FIELD_NUMBER = 1;
+ private int retcode_;
/**
- * .Birthday birth = 1;
- * @return Whether the birth field is set.
+ * int32 retcode = 1;
+ * @return The retcode.
*/
@java.lang.Override
- public boolean hasBirth() {
- return birth_ != null;
+ public int getRetcode() {
+ return retcode_;
+ }
+
+ public static final int BIRTHDAY_FIELD_NUMBER = 2;
+ private emu.grasscutter.net.proto.BirthdayOuterClass.Birthday birthday_;
+ /**
+ * .Birthday birthday = 2;
+ * @return Whether the birthday field is set.
+ */
+ @java.lang.Override
+ public boolean hasBirthday() {
+ return birthday_ != null;
}
/**
- * .Birthday birth = 1;
- * @return The birth.
+ * .Birthday birthday = 2;
+ * @return The birthday.
*/
@java.lang.Override
- public emu.grasscutter.net.proto.BirthdayOuterClass.Birthday getBirth() {
- return birth_ == null ? emu.grasscutter.net.proto.BirthdayOuterClass.Birthday.getDefaultInstance() : birth_;
+ public emu.grasscutter.net.proto.BirthdayOuterClass.Birthday getBirthday() {
+ return birthday_ == null ? emu.grasscutter.net.proto.BirthdayOuterClass.Birthday.getDefaultInstance() : birthday_;
}
/**
- * .Birthday birth = 1;
+ * .Birthday birthday = 2;
*/
@java.lang.Override
- public emu.grasscutter.net.proto.BirthdayOuterClass.BirthdayOrBuilder getBirthOrBuilder() {
- return getBirth();
+ public emu.grasscutter.net.proto.BirthdayOuterClass.BirthdayOrBuilder getBirthdayOrBuilder() {
+ return getBirthday();
}
private byte memoizedIsInitialized = -1;
@@ -165,8 +187,11 @@ public final class SetPlayerBirthdayRspOuterClass {
@java.lang.Override
public void writeTo(com.google.protobuf.CodedOutputStream output)
throws java.io.IOException {
- if (birth_ != null) {
- output.writeMessage(1, getBirth());
+ if (retcode_ != 0) {
+ output.writeInt32(1, retcode_);
+ }
+ if (birthday_ != null) {
+ output.writeMessage(2, getBirthday());
}
unknownFields.writeTo(output);
}
@@ -177,9 +202,13 @@ public final class SetPlayerBirthdayRspOuterClass {
if (size != -1) return size;
size = 0;
- if (birth_ != null) {
+ if (retcode_ != 0) {
size += com.google.protobuf.CodedOutputStream
- .computeMessageSize(1, getBirth());
+ .computeInt32Size(1, retcode_);
+ }
+ if (birthday_ != null) {
+ size += com.google.protobuf.CodedOutputStream
+ .computeMessageSize(2, getBirthday());
}
size += unknownFields.getSerializedSize();
memoizedSize = size;
@@ -196,10 +225,12 @@ public final class SetPlayerBirthdayRspOuterClass {
}
emu.grasscutter.net.proto.SetPlayerBirthdayRspOuterClass.SetPlayerBirthdayRsp other = (emu.grasscutter.net.proto.SetPlayerBirthdayRspOuterClass.SetPlayerBirthdayRsp) obj;
- if (hasBirth() != other.hasBirth()) return false;
- if (hasBirth()) {
- if (!getBirth()
- .equals(other.getBirth())) return false;
+ if (getRetcode()
+ != other.getRetcode()) return false;
+ if (hasBirthday() != other.hasBirthday()) return false;
+ if (hasBirthday()) {
+ if (!getBirthday()
+ .equals(other.getBirthday())) return false;
}
if (!unknownFields.equals(other.unknownFields)) return false;
return true;
@@ -212,9 +243,11 @@ public final class SetPlayerBirthdayRspOuterClass {
}
int hash = 41;
hash = (19 * hash) + getDescriptor().hashCode();
- if (hasBirth()) {
- hash = (37 * hash) + BIRTH_FIELD_NUMBER;
- hash = (53 * hash) + getBirth().hashCode();
+ hash = (37 * hash) + RETCODE_FIELD_NUMBER;
+ hash = (53 * hash) + getRetcode();
+ if (hasBirthday()) {
+ hash = (37 * hash) + BIRTHDAY_FIELD_NUMBER;
+ hash = (53 * hash) + getBirthday().hashCode();
}
hash = (29 * hash) + unknownFields.hashCode();
memoizedHashCode = hash;
@@ -349,11 +382,13 @@ public final class SetPlayerBirthdayRspOuterClass {
@java.lang.Override
public Builder clear() {
super.clear();
- if (birthBuilder_ == null) {
- birth_ = null;
+ retcode_ = 0;
+
+ if (birthdayBuilder_ == null) {
+ birthday_ = null;
} else {
- birth_ = null;
- birthBuilder_ = null;
+ birthday_ = null;
+ birthdayBuilder_ = null;
}
return this;
}
@@ -381,10 +416,11 @@ public final class SetPlayerBirthdayRspOuterClass {
@java.lang.Override
public emu.grasscutter.net.proto.SetPlayerBirthdayRspOuterClass.SetPlayerBirthdayRsp buildPartial() {
emu.grasscutter.net.proto.SetPlayerBirthdayRspOuterClass.SetPlayerBirthdayRsp result = new emu.grasscutter.net.proto.SetPlayerBirthdayRspOuterClass.SetPlayerBirthdayRsp(this);
- if (birthBuilder_ == null) {
- result.birth_ = birth_;
+ result.retcode_ = retcode_;
+ if (birthdayBuilder_ == null) {
+ result.birthday_ = birthday_;
} else {
- result.birth_ = birthBuilder_.build();
+ result.birthday_ = birthdayBuilder_.build();
}
onBuilt();
return result;
@@ -434,8 +470,11 @@ public final class SetPlayerBirthdayRspOuterClass {
public Builder mergeFrom(emu.grasscutter.net.proto.SetPlayerBirthdayRspOuterClass.SetPlayerBirthdayRsp other) {
if (other == emu.grasscutter.net.proto.SetPlayerBirthdayRspOuterClass.SetPlayerBirthdayRsp.getDefaultInstance()) return this;
- if (other.hasBirth()) {
- mergeBirth(other.getBirth());
+ if (other.getRetcode() != 0) {
+ setRetcode(other.getRetcode());
+ }
+ if (other.hasBirthday()) {
+ mergeBirthday(other.getBirthday());
}
this.mergeUnknownFields(other.unknownFields);
onChanged();
@@ -466,123 +505,154 @@ public final class SetPlayerBirthdayRspOuterClass {
return this;
}
- private emu.grasscutter.net.proto.BirthdayOuterClass.Birthday birth_;
- private com.google.protobuf.SingleFieldBuilderV3<
- emu.grasscutter.net.proto.BirthdayOuterClass.Birthday, emu.grasscutter.net.proto.BirthdayOuterClass.Birthday.Builder, emu.grasscutter.net.proto.BirthdayOuterClass.BirthdayOrBuilder> birthBuilder_;
+ private int retcode_ ;
/**
- * .Birthday birth = 1;
- * @return Whether the birth field is set.
+ * int32 retcode = 1;
+ * @return The retcode.
*/
- public boolean hasBirth() {
- return birthBuilder_ != null || birth_ != null;
+ @java.lang.Override
+ public int getRetcode() {
+ return retcode_;
}
/**
- * .Birthday birth = 1;
- * @return The birth.
+ * int32 retcode = 1;
+ * @param value The retcode to set.
+ * @return This builder for chaining.
*/
- public emu.grasscutter.net.proto.BirthdayOuterClass.Birthday getBirth() {
- if (birthBuilder_ == null) {
- return birth_ == null ? emu.grasscutter.net.proto.BirthdayOuterClass.Birthday.getDefaultInstance() : birth_;
+ public Builder setRetcode(int value) {
+
+ retcode_ = value;
+ onChanged();
+ return this;
+ }
+ /**
+ * int32 retcode = 1;
+ * @return This builder for chaining.
+ */
+ public Builder clearRetcode() {
+
+ retcode_ = 0;
+ onChanged();
+ return this;
+ }
+
+ private emu.grasscutter.net.proto.BirthdayOuterClass.Birthday birthday_;
+ private com.google.protobuf.SingleFieldBuilderV3<
+ emu.grasscutter.net.proto.BirthdayOuterClass.Birthday, emu.grasscutter.net.proto.BirthdayOuterClass.Birthday.Builder, emu.grasscutter.net.proto.BirthdayOuterClass.BirthdayOrBuilder> birthdayBuilder_;
+ /**
+ * .Birthday birthday = 2;
+ * @return Whether the birthday field is set.
+ */
+ public boolean hasBirthday() {
+ return birthdayBuilder_ != null || birthday_ != null;
+ }
+ /**
+ * .Birthday birthday = 2;
+ * @return The birthday.
+ */
+ public emu.grasscutter.net.proto.BirthdayOuterClass.Birthday getBirthday() {
+ if (birthdayBuilder_ == null) {
+ return birthday_ == null ? emu.grasscutter.net.proto.BirthdayOuterClass.Birthday.getDefaultInstance() : birthday_;
} else {
- return birthBuilder_.getMessage();
+ return birthdayBuilder_.getMessage();
}
}
/**
- * .Birthday birth = 1;
+ * .Birthday birthday = 2;
*/
- public Builder setBirth(emu.grasscutter.net.proto.BirthdayOuterClass.Birthday value) {
- if (birthBuilder_ == null) {
+ public Builder setBirthday(emu.grasscutter.net.proto.BirthdayOuterClass.Birthday value) {
+ if (birthdayBuilder_ == null) {
if (value == null) {
throw new NullPointerException();
}
- birth_ = value;
+ birthday_ = value;
onChanged();
} else {
- birthBuilder_.setMessage(value);
+ birthdayBuilder_.setMessage(value);
}
return this;
}
/**
- * .Birthday birth = 1;
+ * .Birthday birthday = 2;
*/
- public Builder setBirth(
+ public Builder setBirthday(
emu.grasscutter.net.proto.BirthdayOuterClass.Birthday.Builder builderForValue) {
- if (birthBuilder_ == null) {
- birth_ = builderForValue.build();
+ if (birthdayBuilder_ == null) {
+ birthday_ = builderForValue.build();
onChanged();
} else {
- birthBuilder_.setMessage(builderForValue.build());
+ birthdayBuilder_.setMessage(builderForValue.build());
}
return this;
}
/**
- * .Birthday birth = 1;
+ * .Birthday birthday = 2;
*/
- public Builder mergeBirth(emu.grasscutter.net.proto.BirthdayOuterClass.Birthday value) {
- if (birthBuilder_ == null) {
- if (birth_ != null) {
- birth_ =
- emu.grasscutter.net.proto.BirthdayOuterClass.Birthday.newBuilder(birth_).mergeFrom(value).buildPartial();
+ public Builder mergeBirthday(emu.grasscutter.net.proto.BirthdayOuterClass.Birthday value) {
+ if (birthdayBuilder_ == null) {
+ if (birthday_ != null) {
+ birthday_ =
+ emu.grasscutter.net.proto.BirthdayOuterClass.Birthday.newBuilder(birthday_).mergeFrom(value).buildPartial();
} else {
- birth_ = value;
+ birthday_ = value;
}
onChanged();
} else {
- birthBuilder_.mergeFrom(value);
+ birthdayBuilder_.mergeFrom(value);
}
return this;
}
/**
- * .Birthday birth = 1;
+ * .Birthday birthday = 2;
*/
- public Builder clearBirth() {
- if (birthBuilder_ == null) {
- birth_ = null;
+ public Builder clearBirthday() {
+ if (birthdayBuilder_ == null) {
+ birthday_ = null;
onChanged();
} else {
- birth_ = null;
- birthBuilder_ = null;
+ birthday_ = null;
+ birthdayBuilder_ = null;
}
return this;
}
/**
- * .Birthday birth = 1;
+ * .Birthday birthday = 2;
*/
- public emu.grasscutter.net.proto.BirthdayOuterClass.Birthday.Builder getBirthBuilder() {
+ public emu.grasscutter.net.proto.BirthdayOuterClass.Birthday.Builder getBirthdayBuilder() {
onChanged();
- return getBirthFieldBuilder().getBuilder();
+ return getBirthdayFieldBuilder().getBuilder();
}
/**
- * .Birthday birth = 1;
+ * .Birthday birthday = 2;
*/
- public emu.grasscutter.net.proto.BirthdayOuterClass.BirthdayOrBuilder getBirthOrBuilder() {
- if (birthBuilder_ != null) {
- return birthBuilder_.getMessageOrBuilder();
+ public emu.grasscutter.net.proto.BirthdayOuterClass.BirthdayOrBuilder getBirthdayOrBuilder() {
+ if (birthdayBuilder_ != null) {
+ return birthdayBuilder_.getMessageOrBuilder();
} else {
- return birth_ == null ?
- emu.grasscutter.net.proto.BirthdayOuterClass.Birthday.getDefaultInstance() : birth_;
+ return birthday_ == null ?
+ emu.grasscutter.net.proto.BirthdayOuterClass.Birthday.getDefaultInstance() : birthday_;
}
}
/**
- * .Birthday birth = 1;
+ * .Birthday birthday = 2;
*/
private com.google.protobuf.SingleFieldBuilderV3<
emu.grasscutter.net.proto.BirthdayOuterClass.Birthday, emu.grasscutter.net.proto.BirthdayOuterClass.Birthday.Builder, emu.grasscutter.net.proto.BirthdayOuterClass.BirthdayOrBuilder>
- getBirthFieldBuilder() {
- if (birthBuilder_ == null) {
- birthBuilder_ = new com.google.protobuf.SingleFieldBuilderV3<
+ getBirthdayFieldBuilder() {
+ if (birthdayBuilder_ == null) {
+ birthdayBuilder_ = new com.google.protobuf.SingleFieldBuilderV3<
emu.grasscutter.net.proto.BirthdayOuterClass.Birthday, emu.grasscutter.net.proto.BirthdayOuterClass.Birthday.Builder, emu.grasscutter.net.proto.BirthdayOuterClass.BirthdayOrBuilder>(
- getBirth(),
+ getBirthday(),
getParentForChildren(),
isClean());
- birth_ = null;
+ birthday_ = null;
}
- return birthBuilder_;
+ return birthdayBuilder_;
}
@java.lang.Override
public final Builder setUnknownFields(
@@ -651,10 +721,10 @@ public final class SetPlayerBirthdayRspOuterClass {
descriptor;
static {
java.lang.String[] descriptorData = {
- "\n proto/SetPlayerBirthdayRsp.proto\032\024prot" +
- "o/Birthday.proto\"0\n\024SetPlayerBirthdayRsp" +
- "\022\030\n\005birth\030\001 \001(\0132\t.BirthdayB\033\n\031emu.grassc" +
- "utter.net.protob\006proto3"
+ "\n\032SetPlayerBirthdayRsp.proto\032\016Birthday.p" +
+ "roto\"D\n\024SetPlayerBirthdayRsp\022\017\n\007retcode\030" +
+ "\001 \001(\005\022\033\n\010birthday\030\002 \001(\0132\t.BirthdayB\033\n\031em" +
+ "u.grasscutter.net.protob\006proto3"
};
descriptor = com.google.protobuf.Descriptors.FileDescriptor
.internalBuildGeneratedFileFrom(descriptorData,
@@ -666,7 +736,7 @@ public final class SetPlayerBirthdayRspOuterClass {
internal_static_SetPlayerBirthdayRsp_fieldAccessorTable = new
com.google.protobuf.GeneratedMessageV3.FieldAccessorTable(
internal_static_SetPlayerBirthdayRsp_descriptor,
- new java.lang.String[] { "Birth", });
+ new java.lang.String[] { "Retcode", "Birthday", });
emu.grasscutter.net.proto.BirthdayOuterClass.getDescriptor();
}
diff --git a/src/main/java/emu/grasscutter/net/proto/TakePlayerLevelRewardReqOuterClass.java b/src/main/java/emu/grasscutter/net/proto/TakePlayerLevelRewardReqOuterClass.java
new file mode 100644
index 000000000..ceae2bae5
--- /dev/null
+++ b/src/main/java/emu/grasscutter/net/proto/TakePlayerLevelRewardReqOuterClass.java
@@ -0,0 +1,538 @@
+// Generated by the protocol buffer compiler. DO NOT EDIT!
+// source: TakePlayerLevelRewardReq.proto
+
+package emu.grasscutter.net.proto;
+
+public final class TakePlayerLevelRewardReqOuterClass {
+ private TakePlayerLevelRewardReqOuterClass() {}
+ public static void registerAllExtensions(
+ com.google.protobuf.ExtensionRegistryLite registry) {
+ }
+
+ public static void registerAllExtensions(
+ com.google.protobuf.ExtensionRegistry registry) {
+ registerAllExtensions(
+ (com.google.protobuf.ExtensionRegistryLite) registry);
+ }
+ public interface TakePlayerLevelRewardReqOrBuilder extends
+ // @@protoc_insertion_point(interface_extends:TakePlayerLevelRewardReq)
+ com.google.protobuf.MessageOrBuilder {
+
+ /**
+ * uint32 level = 1;
+ * @return The level.
+ */
+ int getLevel();
+ }
+ /**
+ * Protobuf type {@code TakePlayerLevelRewardReq}
+ */
+ public static final class TakePlayerLevelRewardReq extends
+ com.google.protobuf.GeneratedMessageV3 implements
+ // @@protoc_insertion_point(message_implements:TakePlayerLevelRewardReq)
+ TakePlayerLevelRewardReqOrBuilder {
+ private static final long serialVersionUID = 0L;
+ // Use TakePlayerLevelRewardReq.newBuilder() to construct.
+ private TakePlayerLevelRewardReq(com.google.protobuf.GeneratedMessageV3.Builder> builder) {
+ super(builder);
+ }
+ private TakePlayerLevelRewardReq() {
+ }
+
+ @java.lang.Override
+ @SuppressWarnings({"unused"})
+ protected java.lang.Object newInstance(
+ UnusedPrivateParameter unused) {
+ return new TakePlayerLevelRewardReq();
+ }
+
+ @java.lang.Override
+ public final com.google.protobuf.UnknownFieldSet
+ getUnknownFields() {
+ return this.unknownFields;
+ }
+ private TakePlayerLevelRewardReq(
+ com.google.protobuf.CodedInputStream input,
+ com.google.protobuf.ExtensionRegistryLite extensionRegistry)
+ throws com.google.protobuf.InvalidProtocolBufferException {
+ this();
+ if (extensionRegistry == null) {
+ throw new java.lang.NullPointerException();
+ }
+ com.google.protobuf.UnknownFieldSet.Builder unknownFields =
+ com.google.protobuf.UnknownFieldSet.newBuilder();
+ try {
+ boolean done = false;
+ while (!done) {
+ int tag = input.readTag();
+ switch (tag) {
+ case 0:
+ done = true;
+ break;
+ case 8: {
+
+ level_ = input.readUInt32();
+ break;
+ }
+ default: {
+ if (!parseUnknownField(
+ input, unknownFields, extensionRegistry, tag)) {
+ done = true;
+ }
+ break;
+ }
+ }
+ }
+ } catch (com.google.protobuf.InvalidProtocolBufferException e) {
+ throw e.setUnfinishedMessage(this);
+ } catch (com.google.protobuf.UninitializedMessageException e) {
+ throw e.asInvalidProtocolBufferException().setUnfinishedMessage(this);
+ } catch (java.io.IOException e) {
+ throw new com.google.protobuf.InvalidProtocolBufferException(
+ e).setUnfinishedMessage(this);
+ } finally {
+ this.unknownFields = unknownFields.build();
+ makeExtensionsImmutable();
+ }
+ }
+ public static final com.google.protobuf.Descriptors.Descriptor
+ getDescriptor() {
+ return emu.grasscutter.net.proto.TakePlayerLevelRewardReqOuterClass.internal_static_TakePlayerLevelRewardReq_descriptor;
+ }
+
+ @java.lang.Override
+ protected com.google.protobuf.GeneratedMessageV3.FieldAccessorTable
+ internalGetFieldAccessorTable() {
+ return emu.grasscutter.net.proto.TakePlayerLevelRewardReqOuterClass.internal_static_TakePlayerLevelRewardReq_fieldAccessorTable
+ .ensureFieldAccessorsInitialized(
+ emu.grasscutter.net.proto.TakePlayerLevelRewardReqOuterClass.TakePlayerLevelRewardReq.class, emu.grasscutter.net.proto.TakePlayerLevelRewardReqOuterClass.TakePlayerLevelRewardReq.Builder.class);
+ }
+
+ public static final int LEVEL_FIELD_NUMBER = 1;
+ private int level_;
+ /**
+ * uint32 level = 1;
+ * @return The level.
+ */
+ @java.lang.Override
+ public int getLevel() {
+ return level_;
+ }
+
+ private byte memoizedIsInitialized = -1;
+ @java.lang.Override
+ public final boolean isInitialized() {
+ byte isInitialized = memoizedIsInitialized;
+ if (isInitialized == 1) return true;
+ if (isInitialized == 0) return false;
+
+ memoizedIsInitialized = 1;
+ return true;
+ }
+
+ @java.lang.Override
+ public void writeTo(com.google.protobuf.CodedOutputStream output)
+ throws java.io.IOException {
+ if (level_ != 0) {
+ output.writeUInt32(1, level_);
+ }
+ unknownFields.writeTo(output);
+ }
+
+ @java.lang.Override
+ public int getSerializedSize() {
+ int size = memoizedSize;
+ if (size != -1) return size;
+
+ size = 0;
+ if (level_ != 0) {
+ size += com.google.protobuf.CodedOutputStream
+ .computeUInt32Size(1, level_);
+ }
+ size += unknownFields.getSerializedSize();
+ memoizedSize = size;
+ return size;
+ }
+
+ @java.lang.Override
+ public boolean equals(final java.lang.Object obj) {
+ if (obj == this) {
+ return true;
+ }
+ if (!(obj instanceof emu.grasscutter.net.proto.TakePlayerLevelRewardReqOuterClass.TakePlayerLevelRewardReq)) {
+ return super.equals(obj);
+ }
+ emu.grasscutter.net.proto.TakePlayerLevelRewardReqOuterClass.TakePlayerLevelRewardReq other = (emu.grasscutter.net.proto.TakePlayerLevelRewardReqOuterClass.TakePlayerLevelRewardReq) obj;
+
+ if (getLevel()
+ != other.getLevel()) return false;
+ if (!unknownFields.equals(other.unknownFields)) return false;
+ return true;
+ }
+
+ @java.lang.Override
+ public int hashCode() {
+ if (memoizedHashCode != 0) {
+ return memoizedHashCode;
+ }
+ int hash = 41;
+ hash = (19 * hash) + getDescriptor().hashCode();
+ hash = (37 * hash) + LEVEL_FIELD_NUMBER;
+ hash = (53 * hash) + getLevel();
+ hash = (29 * hash) + unknownFields.hashCode();
+ memoizedHashCode = hash;
+ return hash;
+ }
+
+ public static emu.grasscutter.net.proto.TakePlayerLevelRewardReqOuterClass.TakePlayerLevelRewardReq parseFrom(
+ java.nio.ByteBuffer data)
+ throws com.google.protobuf.InvalidProtocolBufferException {
+ return PARSER.parseFrom(data);
+ }
+ public static emu.grasscutter.net.proto.TakePlayerLevelRewardReqOuterClass.TakePlayerLevelRewardReq parseFrom(
+ java.nio.ByteBuffer data,
+ com.google.protobuf.ExtensionRegistryLite extensionRegistry)
+ throws com.google.protobuf.InvalidProtocolBufferException {
+ return PARSER.parseFrom(data, extensionRegistry);
+ }
+ public static emu.grasscutter.net.proto.TakePlayerLevelRewardReqOuterClass.TakePlayerLevelRewardReq parseFrom(
+ com.google.protobuf.ByteString data)
+ throws com.google.protobuf.InvalidProtocolBufferException {
+ return PARSER.parseFrom(data);
+ }
+ public static emu.grasscutter.net.proto.TakePlayerLevelRewardReqOuterClass.TakePlayerLevelRewardReq parseFrom(
+ com.google.protobuf.ByteString data,
+ com.google.protobuf.ExtensionRegistryLite extensionRegistry)
+ throws com.google.protobuf.InvalidProtocolBufferException {
+ return PARSER.parseFrom(data, extensionRegistry);
+ }
+ public static emu.grasscutter.net.proto.TakePlayerLevelRewardReqOuterClass.TakePlayerLevelRewardReq parseFrom(byte[] data)
+ throws com.google.protobuf.InvalidProtocolBufferException {
+ return PARSER.parseFrom(data);
+ }
+ public static emu.grasscutter.net.proto.TakePlayerLevelRewardReqOuterClass.TakePlayerLevelRewardReq parseFrom(
+ byte[] data,
+ com.google.protobuf.ExtensionRegistryLite extensionRegistry)
+ throws com.google.protobuf.InvalidProtocolBufferException {
+ return PARSER.parseFrom(data, extensionRegistry);
+ }
+ public static emu.grasscutter.net.proto.TakePlayerLevelRewardReqOuterClass.TakePlayerLevelRewardReq parseFrom(java.io.InputStream input)
+ throws java.io.IOException {
+ return com.google.protobuf.GeneratedMessageV3
+ .parseWithIOException(PARSER, input);
+ }
+ public static emu.grasscutter.net.proto.TakePlayerLevelRewardReqOuterClass.TakePlayerLevelRewardReq parseFrom(
+ java.io.InputStream input,
+ com.google.protobuf.ExtensionRegistryLite extensionRegistry)
+ throws java.io.IOException {
+ return com.google.protobuf.GeneratedMessageV3
+ .parseWithIOException(PARSER, input, extensionRegistry);
+ }
+ public static emu.grasscutter.net.proto.TakePlayerLevelRewardReqOuterClass.TakePlayerLevelRewardReq parseDelimitedFrom(java.io.InputStream input)
+ throws java.io.IOException {
+ return com.google.protobuf.GeneratedMessageV3
+ .parseDelimitedWithIOException(PARSER, input);
+ }
+ public static emu.grasscutter.net.proto.TakePlayerLevelRewardReqOuterClass.TakePlayerLevelRewardReq parseDelimitedFrom(
+ java.io.InputStream input,
+ com.google.protobuf.ExtensionRegistryLite extensionRegistry)
+ throws java.io.IOException {
+ return com.google.protobuf.GeneratedMessageV3
+ .parseDelimitedWithIOException(PARSER, input, extensionRegistry);
+ }
+ public static emu.grasscutter.net.proto.TakePlayerLevelRewardReqOuterClass.TakePlayerLevelRewardReq parseFrom(
+ com.google.protobuf.CodedInputStream input)
+ throws java.io.IOException {
+ return com.google.protobuf.GeneratedMessageV3
+ .parseWithIOException(PARSER, input);
+ }
+ public static emu.grasscutter.net.proto.TakePlayerLevelRewardReqOuterClass.TakePlayerLevelRewardReq parseFrom(
+ com.google.protobuf.CodedInputStream input,
+ com.google.protobuf.ExtensionRegistryLite extensionRegistry)
+ throws java.io.IOException {
+ return com.google.protobuf.GeneratedMessageV3
+ .parseWithIOException(PARSER, input, extensionRegistry);
+ }
+
+ @java.lang.Override
+ public Builder newBuilderForType() { return newBuilder(); }
+ public static Builder newBuilder() {
+ return DEFAULT_INSTANCE.toBuilder();
+ }
+ public static Builder newBuilder(emu.grasscutter.net.proto.TakePlayerLevelRewardReqOuterClass.TakePlayerLevelRewardReq prototype) {
+ return DEFAULT_INSTANCE.toBuilder().mergeFrom(prototype);
+ }
+ @java.lang.Override
+ public Builder toBuilder() {
+ return this == DEFAULT_INSTANCE
+ ? new Builder() : new Builder().mergeFrom(this);
+ }
+
+ @java.lang.Override
+ protected Builder newBuilderForType(
+ com.google.protobuf.GeneratedMessageV3.BuilderParent parent) {
+ Builder builder = new Builder(parent);
+ return builder;
+ }
+ /**
+ * Protobuf type {@code TakePlayerLevelRewardReq}
+ */
+ public static final class Builder extends
+ com.google.protobuf.GeneratedMessageV3.Builder implements
+ // @@protoc_insertion_point(builder_implements:TakePlayerLevelRewardReq)
+ emu.grasscutter.net.proto.TakePlayerLevelRewardReqOuterClass.TakePlayerLevelRewardReqOrBuilder {
+ public static final com.google.protobuf.Descriptors.Descriptor
+ getDescriptor() {
+ return emu.grasscutter.net.proto.TakePlayerLevelRewardReqOuterClass.internal_static_TakePlayerLevelRewardReq_descriptor;
+ }
+
+ @java.lang.Override
+ protected com.google.protobuf.GeneratedMessageV3.FieldAccessorTable
+ internalGetFieldAccessorTable() {
+ return emu.grasscutter.net.proto.TakePlayerLevelRewardReqOuterClass.internal_static_TakePlayerLevelRewardReq_fieldAccessorTable
+ .ensureFieldAccessorsInitialized(
+ emu.grasscutter.net.proto.TakePlayerLevelRewardReqOuterClass.TakePlayerLevelRewardReq.class, emu.grasscutter.net.proto.TakePlayerLevelRewardReqOuterClass.TakePlayerLevelRewardReq.Builder.class);
+ }
+
+ // Construct using emu.grasscutter.net.proto.TakePlayerLevelRewardReqOuterClass.TakePlayerLevelRewardReq.newBuilder()
+ private Builder() {
+ maybeForceBuilderInitialization();
+ }
+
+ private Builder(
+ com.google.protobuf.GeneratedMessageV3.BuilderParent parent) {
+ super(parent);
+ maybeForceBuilderInitialization();
+ }
+ private void maybeForceBuilderInitialization() {
+ if (com.google.protobuf.GeneratedMessageV3
+ .alwaysUseFieldBuilders) {
+ }
+ }
+ @java.lang.Override
+ public Builder clear() {
+ super.clear();
+ level_ = 0;
+
+ return this;
+ }
+
+ @java.lang.Override
+ public com.google.protobuf.Descriptors.Descriptor
+ getDescriptorForType() {
+ return emu.grasscutter.net.proto.TakePlayerLevelRewardReqOuterClass.internal_static_TakePlayerLevelRewardReq_descriptor;
+ }
+
+ @java.lang.Override
+ public emu.grasscutter.net.proto.TakePlayerLevelRewardReqOuterClass.TakePlayerLevelRewardReq getDefaultInstanceForType() {
+ return emu.grasscutter.net.proto.TakePlayerLevelRewardReqOuterClass.TakePlayerLevelRewardReq.getDefaultInstance();
+ }
+
+ @java.lang.Override
+ public emu.grasscutter.net.proto.TakePlayerLevelRewardReqOuterClass.TakePlayerLevelRewardReq build() {
+ emu.grasscutter.net.proto.TakePlayerLevelRewardReqOuterClass.TakePlayerLevelRewardReq result = buildPartial();
+ if (!result.isInitialized()) {
+ throw newUninitializedMessageException(result);
+ }
+ return result;
+ }
+
+ @java.lang.Override
+ public emu.grasscutter.net.proto.TakePlayerLevelRewardReqOuterClass.TakePlayerLevelRewardReq buildPartial() {
+ emu.grasscutter.net.proto.TakePlayerLevelRewardReqOuterClass.TakePlayerLevelRewardReq result = new emu.grasscutter.net.proto.TakePlayerLevelRewardReqOuterClass.TakePlayerLevelRewardReq(this);
+ result.level_ = level_;
+ onBuilt();
+ return result;
+ }
+
+ @java.lang.Override
+ public Builder clone() {
+ return super.clone();
+ }
+ @java.lang.Override
+ public Builder setField(
+ com.google.protobuf.Descriptors.FieldDescriptor field,
+ java.lang.Object value) {
+ return super.setField(field, value);
+ }
+ @java.lang.Override
+ public Builder clearField(
+ com.google.protobuf.Descriptors.FieldDescriptor field) {
+ return super.clearField(field);
+ }
+ @java.lang.Override
+ public Builder clearOneof(
+ com.google.protobuf.Descriptors.OneofDescriptor oneof) {
+ return super.clearOneof(oneof);
+ }
+ @java.lang.Override
+ public Builder setRepeatedField(
+ com.google.protobuf.Descriptors.FieldDescriptor field,
+ int index, java.lang.Object value) {
+ return super.setRepeatedField(field, index, value);
+ }
+ @java.lang.Override
+ public Builder addRepeatedField(
+ com.google.protobuf.Descriptors.FieldDescriptor field,
+ java.lang.Object value) {
+ return super.addRepeatedField(field, value);
+ }
+ @java.lang.Override
+ public Builder mergeFrom(com.google.protobuf.Message other) {
+ if (other instanceof emu.grasscutter.net.proto.TakePlayerLevelRewardReqOuterClass.TakePlayerLevelRewardReq) {
+ return mergeFrom((emu.grasscutter.net.proto.TakePlayerLevelRewardReqOuterClass.TakePlayerLevelRewardReq)other);
+ } else {
+ super.mergeFrom(other);
+ return this;
+ }
+ }
+
+ public Builder mergeFrom(emu.grasscutter.net.proto.TakePlayerLevelRewardReqOuterClass.TakePlayerLevelRewardReq other) {
+ if (other == emu.grasscutter.net.proto.TakePlayerLevelRewardReqOuterClass.TakePlayerLevelRewardReq.getDefaultInstance()) return this;
+ if (other.getLevel() != 0) {
+ setLevel(other.getLevel());
+ }
+ this.mergeUnknownFields(other.unknownFields);
+ onChanged();
+ return this;
+ }
+
+ @java.lang.Override
+ public final boolean isInitialized() {
+ return true;
+ }
+
+ @java.lang.Override
+ public Builder mergeFrom(
+ com.google.protobuf.CodedInputStream input,
+ com.google.protobuf.ExtensionRegistryLite extensionRegistry)
+ throws java.io.IOException {
+ emu.grasscutter.net.proto.TakePlayerLevelRewardReqOuterClass.TakePlayerLevelRewardReq parsedMessage = null;
+ try {
+ parsedMessage = PARSER.parsePartialFrom(input, extensionRegistry);
+ } catch (com.google.protobuf.InvalidProtocolBufferException e) {
+ parsedMessage = (emu.grasscutter.net.proto.TakePlayerLevelRewardReqOuterClass.TakePlayerLevelRewardReq) e.getUnfinishedMessage();
+ throw e.unwrapIOException();
+ } finally {
+ if (parsedMessage != null) {
+ mergeFrom(parsedMessage);
+ }
+ }
+ return this;
+ }
+
+ private int level_ ;
+ /**
+ * uint32 level = 1;
+ * @return The level.
+ */
+ @java.lang.Override
+ public int getLevel() {
+ return level_;
+ }
+ /**
+ * uint32 level = 1;
+ * @param value The level to set.
+ * @return This builder for chaining.
+ */
+ public Builder setLevel(int value) {
+
+ level_ = value;
+ onChanged();
+ return this;
+ }
+ /**
+ * uint32 level = 1;
+ * @return This builder for chaining.
+ */
+ public Builder clearLevel() {
+
+ level_ = 0;
+ onChanged();
+ return this;
+ }
+ @java.lang.Override
+ public final Builder setUnknownFields(
+ final com.google.protobuf.UnknownFieldSet unknownFields) {
+ return super.setUnknownFields(unknownFields);
+ }
+
+ @java.lang.Override
+ public final Builder mergeUnknownFields(
+ final com.google.protobuf.UnknownFieldSet unknownFields) {
+ return super.mergeUnknownFields(unknownFields);
+ }
+
+
+ // @@protoc_insertion_point(builder_scope:TakePlayerLevelRewardReq)
+ }
+
+ // @@protoc_insertion_point(class_scope:TakePlayerLevelRewardReq)
+ private static final emu.grasscutter.net.proto.TakePlayerLevelRewardReqOuterClass.TakePlayerLevelRewardReq DEFAULT_INSTANCE;
+ static {
+ DEFAULT_INSTANCE = new emu.grasscutter.net.proto.TakePlayerLevelRewardReqOuterClass.TakePlayerLevelRewardReq();
+ }
+
+ public static emu.grasscutter.net.proto.TakePlayerLevelRewardReqOuterClass.TakePlayerLevelRewardReq getDefaultInstance() {
+ return DEFAULT_INSTANCE;
+ }
+
+ private static final com.google.protobuf.Parser
+ PARSER = new com.google.protobuf.AbstractParser() {
+ @java.lang.Override
+ public TakePlayerLevelRewardReq parsePartialFrom(
+ com.google.protobuf.CodedInputStream input,
+ com.google.protobuf.ExtensionRegistryLite extensionRegistry)
+ throws com.google.protobuf.InvalidProtocolBufferException {
+ return new TakePlayerLevelRewardReq(input, extensionRegistry);
+ }
+ };
+
+ public static com.google.protobuf.Parser parser() {
+ return PARSER;
+ }
+
+ @java.lang.Override
+ public com.google.protobuf.Parser getParserForType() {
+ return PARSER;
+ }
+
+ @java.lang.Override
+ public emu.grasscutter.net.proto.TakePlayerLevelRewardReqOuterClass.TakePlayerLevelRewardReq getDefaultInstanceForType() {
+ return DEFAULT_INSTANCE;
+ }
+
+ }
+
+ private static final com.google.protobuf.Descriptors.Descriptor
+ internal_static_TakePlayerLevelRewardReq_descriptor;
+ private static final
+ com.google.protobuf.GeneratedMessageV3.FieldAccessorTable
+ internal_static_TakePlayerLevelRewardReq_fieldAccessorTable;
+
+ public static com.google.protobuf.Descriptors.FileDescriptor
+ getDescriptor() {
+ return descriptor;
+ }
+ private static com.google.protobuf.Descriptors.FileDescriptor
+ descriptor;
+ static {
+ java.lang.String[] descriptorData = {
+ "\n\036TakePlayerLevelRewardReq.proto\")\n\030Take" +
+ "PlayerLevelRewardReq\022\r\n\005level\030\001 \001(\rB\033\n\031e" +
+ "mu.grasscutter.net.protob\006proto3"
+ };
+ descriptor = com.google.protobuf.Descriptors.FileDescriptor
+ .internalBuildGeneratedFileFrom(descriptorData,
+ new com.google.protobuf.Descriptors.FileDescriptor[] {
+ });
+ internal_static_TakePlayerLevelRewardReq_descriptor =
+ getDescriptor().getMessageTypes().get(0);
+ internal_static_TakePlayerLevelRewardReq_fieldAccessorTable = new
+ com.google.protobuf.GeneratedMessageV3.FieldAccessorTable(
+ internal_static_TakePlayerLevelRewardReq_descriptor,
+ new java.lang.String[] { "Level", });
+ }
+
+ // @@protoc_insertion_point(outer_class_scope)
+}
diff --git a/src/main/java/emu/grasscutter/net/proto/TakePlayerLevelRewardRspOuterClass.java b/src/main/java/emu/grasscutter/net/proto/TakePlayerLevelRewardRspOuterClass.java
new file mode 100644
index 000000000..bd18103b9
--- /dev/null
+++ b/src/main/java/emu/grasscutter/net/proto/TakePlayerLevelRewardRspOuterClass.java
@@ -0,0 +1,679 @@
+// Generated by the protocol buffer compiler. DO NOT EDIT!
+// source: TakePlayerLevelRewardRsp.proto
+
+package emu.grasscutter.net.proto;
+
+public final class TakePlayerLevelRewardRspOuterClass {
+ private TakePlayerLevelRewardRspOuterClass() {}
+ public static void registerAllExtensions(
+ com.google.protobuf.ExtensionRegistryLite registry) {
+ }
+
+ public static void registerAllExtensions(
+ com.google.protobuf.ExtensionRegistry registry) {
+ registerAllExtensions(
+ (com.google.protobuf.ExtensionRegistryLite) registry);
+ }
+ public interface TakePlayerLevelRewardRspOrBuilder extends
+ // @@protoc_insertion_point(interface_extends:TakePlayerLevelRewardRsp)
+ com.google.protobuf.MessageOrBuilder {
+
+ /**
+ * int32 retcode = 1;
+ * @return The retcode.
+ */
+ int getRetcode();
+
+ /**
+ * uint32 level = 2;
+ * @return The level.
+ */
+ int getLevel();
+
+ /**
+ * uint32 rewardId = 3;
+ * @return The rewardId.
+ */
+ int getRewardId();
+ }
+ /**
+ * Protobuf type {@code TakePlayerLevelRewardRsp}
+ */
+ public static final class TakePlayerLevelRewardRsp extends
+ com.google.protobuf.GeneratedMessageV3 implements
+ // @@protoc_insertion_point(message_implements:TakePlayerLevelRewardRsp)
+ TakePlayerLevelRewardRspOrBuilder {
+ private static final long serialVersionUID = 0L;
+ // Use TakePlayerLevelRewardRsp.newBuilder() to construct.
+ private TakePlayerLevelRewardRsp(com.google.protobuf.GeneratedMessageV3.Builder> builder) {
+ super(builder);
+ }
+ private TakePlayerLevelRewardRsp() {
+ }
+
+ @java.lang.Override
+ @SuppressWarnings({"unused"})
+ protected java.lang.Object newInstance(
+ UnusedPrivateParameter unused) {
+ return new TakePlayerLevelRewardRsp();
+ }
+
+ @java.lang.Override
+ public final com.google.protobuf.UnknownFieldSet
+ getUnknownFields() {
+ return this.unknownFields;
+ }
+ private TakePlayerLevelRewardRsp(
+ com.google.protobuf.CodedInputStream input,
+ com.google.protobuf.ExtensionRegistryLite extensionRegistry)
+ throws com.google.protobuf.InvalidProtocolBufferException {
+ this();
+ if (extensionRegistry == null) {
+ throw new java.lang.NullPointerException();
+ }
+ com.google.protobuf.UnknownFieldSet.Builder unknownFields =
+ com.google.protobuf.UnknownFieldSet.newBuilder();
+ try {
+ boolean done = false;
+ while (!done) {
+ int tag = input.readTag();
+ switch (tag) {
+ case 0:
+ done = true;
+ break;
+ case 8: {
+
+ retcode_ = input.readInt32();
+ break;
+ }
+ case 16: {
+
+ level_ = input.readUInt32();
+ break;
+ }
+ case 24: {
+
+ rewardId_ = input.readUInt32();
+ break;
+ }
+ default: {
+ if (!parseUnknownField(
+ input, unknownFields, extensionRegistry, tag)) {
+ done = true;
+ }
+ break;
+ }
+ }
+ }
+ } catch (com.google.protobuf.InvalidProtocolBufferException e) {
+ throw e.setUnfinishedMessage(this);
+ } catch (com.google.protobuf.UninitializedMessageException e) {
+ throw e.asInvalidProtocolBufferException().setUnfinishedMessage(this);
+ } catch (java.io.IOException e) {
+ throw new com.google.protobuf.InvalidProtocolBufferException(
+ e).setUnfinishedMessage(this);
+ } finally {
+ this.unknownFields = unknownFields.build();
+ makeExtensionsImmutable();
+ }
+ }
+ public static final com.google.protobuf.Descriptors.Descriptor
+ getDescriptor() {
+ return emu.grasscutter.net.proto.TakePlayerLevelRewardRspOuterClass.internal_static_TakePlayerLevelRewardRsp_descriptor;
+ }
+
+ @java.lang.Override
+ protected com.google.protobuf.GeneratedMessageV3.FieldAccessorTable
+ internalGetFieldAccessorTable() {
+ return emu.grasscutter.net.proto.TakePlayerLevelRewardRspOuterClass.internal_static_TakePlayerLevelRewardRsp_fieldAccessorTable
+ .ensureFieldAccessorsInitialized(
+ emu.grasscutter.net.proto.TakePlayerLevelRewardRspOuterClass.TakePlayerLevelRewardRsp.class, emu.grasscutter.net.proto.TakePlayerLevelRewardRspOuterClass.TakePlayerLevelRewardRsp.Builder.class);
+ }
+
+ public static final int RETCODE_FIELD_NUMBER = 1;
+ private int retcode_;
+ /**
+ * int32 retcode = 1;
+ * @return The retcode.
+ */
+ @java.lang.Override
+ public int getRetcode() {
+ return retcode_;
+ }
+
+ public static final int LEVEL_FIELD_NUMBER = 2;
+ private int level_;
+ /**
+ * uint32 level = 2;
+ * @return The level.
+ */
+ @java.lang.Override
+ public int getLevel() {
+ return level_;
+ }
+
+ public static final int REWARDID_FIELD_NUMBER = 3;
+ private int rewardId_;
+ /**
+ * uint32 rewardId = 3;
+ * @return The rewardId.
+ */
+ @java.lang.Override
+ public int getRewardId() {
+ return rewardId_;
+ }
+
+ private byte memoizedIsInitialized = -1;
+ @java.lang.Override
+ public final boolean isInitialized() {
+ byte isInitialized = memoizedIsInitialized;
+ if (isInitialized == 1) return true;
+ if (isInitialized == 0) return false;
+
+ memoizedIsInitialized = 1;
+ return true;
+ }
+
+ @java.lang.Override
+ public void writeTo(com.google.protobuf.CodedOutputStream output)
+ throws java.io.IOException {
+ if (retcode_ != 0) {
+ output.writeInt32(1, retcode_);
+ }
+ if (level_ != 0) {
+ output.writeUInt32(2, level_);
+ }
+ if (rewardId_ != 0) {
+ output.writeUInt32(3, rewardId_);
+ }
+ unknownFields.writeTo(output);
+ }
+
+ @java.lang.Override
+ public int getSerializedSize() {
+ int size = memoizedSize;
+ if (size != -1) return size;
+
+ size = 0;
+ if (retcode_ != 0) {
+ size += com.google.protobuf.CodedOutputStream
+ .computeInt32Size(1, retcode_);
+ }
+ if (level_ != 0) {
+ size += com.google.protobuf.CodedOutputStream
+ .computeUInt32Size(2, level_);
+ }
+ if (rewardId_ != 0) {
+ size += com.google.protobuf.CodedOutputStream
+ .computeUInt32Size(3, rewardId_);
+ }
+ size += unknownFields.getSerializedSize();
+ memoizedSize = size;
+ return size;
+ }
+
+ @java.lang.Override
+ public boolean equals(final java.lang.Object obj) {
+ if (obj == this) {
+ return true;
+ }
+ if (!(obj instanceof emu.grasscutter.net.proto.TakePlayerLevelRewardRspOuterClass.TakePlayerLevelRewardRsp)) {
+ return super.equals(obj);
+ }
+ emu.grasscutter.net.proto.TakePlayerLevelRewardRspOuterClass.TakePlayerLevelRewardRsp other = (emu.grasscutter.net.proto.TakePlayerLevelRewardRspOuterClass.TakePlayerLevelRewardRsp) obj;
+
+ if (getRetcode()
+ != other.getRetcode()) return false;
+ if (getLevel()
+ != other.getLevel()) return false;
+ if (getRewardId()
+ != other.getRewardId()) return false;
+ if (!unknownFields.equals(other.unknownFields)) return false;
+ return true;
+ }
+
+ @java.lang.Override
+ public int hashCode() {
+ if (memoizedHashCode != 0) {
+ return memoizedHashCode;
+ }
+ int hash = 41;
+ hash = (19 * hash) + getDescriptor().hashCode();
+ hash = (37 * hash) + RETCODE_FIELD_NUMBER;
+ hash = (53 * hash) + getRetcode();
+ hash = (37 * hash) + LEVEL_FIELD_NUMBER;
+ hash = (53 * hash) + getLevel();
+ hash = (37 * hash) + REWARDID_FIELD_NUMBER;
+ hash = (53 * hash) + getRewardId();
+ hash = (29 * hash) + unknownFields.hashCode();
+ memoizedHashCode = hash;
+ return hash;
+ }
+
+ public static emu.grasscutter.net.proto.TakePlayerLevelRewardRspOuterClass.TakePlayerLevelRewardRsp parseFrom(
+ java.nio.ByteBuffer data)
+ throws com.google.protobuf.InvalidProtocolBufferException {
+ return PARSER.parseFrom(data);
+ }
+ public static emu.grasscutter.net.proto.TakePlayerLevelRewardRspOuterClass.TakePlayerLevelRewardRsp parseFrom(
+ java.nio.ByteBuffer data,
+ com.google.protobuf.ExtensionRegistryLite extensionRegistry)
+ throws com.google.protobuf.InvalidProtocolBufferException {
+ return PARSER.parseFrom(data, extensionRegistry);
+ }
+ public static emu.grasscutter.net.proto.TakePlayerLevelRewardRspOuterClass.TakePlayerLevelRewardRsp parseFrom(
+ com.google.protobuf.ByteString data)
+ throws com.google.protobuf.InvalidProtocolBufferException {
+ return PARSER.parseFrom(data);
+ }
+ public static emu.grasscutter.net.proto.TakePlayerLevelRewardRspOuterClass.TakePlayerLevelRewardRsp parseFrom(
+ com.google.protobuf.ByteString data,
+ com.google.protobuf.ExtensionRegistryLite extensionRegistry)
+ throws com.google.protobuf.InvalidProtocolBufferException {
+ return PARSER.parseFrom(data, extensionRegistry);
+ }
+ public static emu.grasscutter.net.proto.TakePlayerLevelRewardRspOuterClass.TakePlayerLevelRewardRsp parseFrom(byte[] data)
+ throws com.google.protobuf.InvalidProtocolBufferException {
+ return PARSER.parseFrom(data);
+ }
+ public static emu.grasscutter.net.proto.TakePlayerLevelRewardRspOuterClass.TakePlayerLevelRewardRsp parseFrom(
+ byte[] data,
+ com.google.protobuf.ExtensionRegistryLite extensionRegistry)
+ throws com.google.protobuf.InvalidProtocolBufferException {
+ return PARSER.parseFrom(data, extensionRegistry);
+ }
+ public static emu.grasscutter.net.proto.TakePlayerLevelRewardRspOuterClass.TakePlayerLevelRewardRsp parseFrom(java.io.InputStream input)
+ throws java.io.IOException {
+ return com.google.protobuf.GeneratedMessageV3
+ .parseWithIOException(PARSER, input);
+ }
+ public static emu.grasscutter.net.proto.TakePlayerLevelRewardRspOuterClass.TakePlayerLevelRewardRsp parseFrom(
+ java.io.InputStream input,
+ com.google.protobuf.ExtensionRegistryLite extensionRegistry)
+ throws java.io.IOException {
+ return com.google.protobuf.GeneratedMessageV3
+ .parseWithIOException(PARSER, input, extensionRegistry);
+ }
+ public static emu.grasscutter.net.proto.TakePlayerLevelRewardRspOuterClass.TakePlayerLevelRewardRsp parseDelimitedFrom(java.io.InputStream input)
+ throws java.io.IOException {
+ return com.google.protobuf.GeneratedMessageV3
+ .parseDelimitedWithIOException(PARSER, input);
+ }
+ public static emu.grasscutter.net.proto.TakePlayerLevelRewardRspOuterClass.TakePlayerLevelRewardRsp parseDelimitedFrom(
+ java.io.InputStream input,
+ com.google.protobuf.ExtensionRegistryLite extensionRegistry)
+ throws java.io.IOException {
+ return com.google.protobuf.GeneratedMessageV3
+ .parseDelimitedWithIOException(PARSER, input, extensionRegistry);
+ }
+ public static emu.grasscutter.net.proto.TakePlayerLevelRewardRspOuterClass.TakePlayerLevelRewardRsp parseFrom(
+ com.google.protobuf.CodedInputStream input)
+ throws java.io.IOException {
+ return com.google.protobuf.GeneratedMessageV3
+ .parseWithIOException(PARSER, input);
+ }
+ public static emu.grasscutter.net.proto.TakePlayerLevelRewardRspOuterClass.TakePlayerLevelRewardRsp parseFrom(
+ com.google.protobuf.CodedInputStream input,
+ com.google.protobuf.ExtensionRegistryLite extensionRegistry)
+ throws java.io.IOException {
+ return com.google.protobuf.GeneratedMessageV3
+ .parseWithIOException(PARSER, input, extensionRegistry);
+ }
+
+ @java.lang.Override
+ public Builder newBuilderForType() { return newBuilder(); }
+ public static Builder newBuilder() {
+ return DEFAULT_INSTANCE.toBuilder();
+ }
+ public static Builder newBuilder(emu.grasscutter.net.proto.TakePlayerLevelRewardRspOuterClass.TakePlayerLevelRewardRsp prototype) {
+ return DEFAULT_INSTANCE.toBuilder().mergeFrom(prototype);
+ }
+ @java.lang.Override
+ public Builder toBuilder() {
+ return this == DEFAULT_INSTANCE
+ ? new Builder() : new Builder().mergeFrom(this);
+ }
+
+ @java.lang.Override
+ protected Builder newBuilderForType(
+ com.google.protobuf.GeneratedMessageV3.BuilderParent parent) {
+ Builder builder = new Builder(parent);
+ return builder;
+ }
+ /**
+ * Protobuf type {@code TakePlayerLevelRewardRsp}
+ */
+ public static final class Builder extends
+ com.google.protobuf.GeneratedMessageV3.Builder implements
+ // @@protoc_insertion_point(builder_implements:TakePlayerLevelRewardRsp)
+ emu.grasscutter.net.proto.TakePlayerLevelRewardRspOuterClass.TakePlayerLevelRewardRspOrBuilder {
+ public static final com.google.protobuf.Descriptors.Descriptor
+ getDescriptor() {
+ return emu.grasscutter.net.proto.TakePlayerLevelRewardRspOuterClass.internal_static_TakePlayerLevelRewardRsp_descriptor;
+ }
+
+ @java.lang.Override
+ protected com.google.protobuf.GeneratedMessageV3.FieldAccessorTable
+ internalGetFieldAccessorTable() {
+ return emu.grasscutter.net.proto.TakePlayerLevelRewardRspOuterClass.internal_static_TakePlayerLevelRewardRsp_fieldAccessorTable
+ .ensureFieldAccessorsInitialized(
+ emu.grasscutter.net.proto.TakePlayerLevelRewardRspOuterClass.TakePlayerLevelRewardRsp.class, emu.grasscutter.net.proto.TakePlayerLevelRewardRspOuterClass.TakePlayerLevelRewardRsp.Builder.class);
+ }
+
+ // Construct using emu.grasscutter.net.proto.TakePlayerLevelRewardRspOuterClass.TakePlayerLevelRewardRsp.newBuilder()
+ private Builder() {
+ maybeForceBuilderInitialization();
+ }
+
+ private Builder(
+ com.google.protobuf.GeneratedMessageV3.BuilderParent parent) {
+ super(parent);
+ maybeForceBuilderInitialization();
+ }
+ private void maybeForceBuilderInitialization() {
+ if (com.google.protobuf.GeneratedMessageV3
+ .alwaysUseFieldBuilders) {
+ }
+ }
+ @java.lang.Override
+ public Builder clear() {
+ super.clear();
+ retcode_ = 0;
+
+ level_ = 0;
+
+ rewardId_ = 0;
+
+ return this;
+ }
+
+ @java.lang.Override
+ public com.google.protobuf.Descriptors.Descriptor
+ getDescriptorForType() {
+ return emu.grasscutter.net.proto.TakePlayerLevelRewardRspOuterClass.internal_static_TakePlayerLevelRewardRsp_descriptor;
+ }
+
+ @java.lang.Override
+ public emu.grasscutter.net.proto.TakePlayerLevelRewardRspOuterClass.TakePlayerLevelRewardRsp getDefaultInstanceForType() {
+ return emu.grasscutter.net.proto.TakePlayerLevelRewardRspOuterClass.TakePlayerLevelRewardRsp.getDefaultInstance();
+ }
+
+ @java.lang.Override
+ public emu.grasscutter.net.proto.TakePlayerLevelRewardRspOuterClass.TakePlayerLevelRewardRsp build() {
+ emu.grasscutter.net.proto.TakePlayerLevelRewardRspOuterClass.TakePlayerLevelRewardRsp result = buildPartial();
+ if (!result.isInitialized()) {
+ throw newUninitializedMessageException(result);
+ }
+ return result;
+ }
+
+ @java.lang.Override
+ public emu.grasscutter.net.proto.TakePlayerLevelRewardRspOuterClass.TakePlayerLevelRewardRsp buildPartial() {
+ emu.grasscutter.net.proto.TakePlayerLevelRewardRspOuterClass.TakePlayerLevelRewardRsp result = new emu.grasscutter.net.proto.TakePlayerLevelRewardRspOuterClass.TakePlayerLevelRewardRsp(this);
+ result.retcode_ = retcode_;
+ result.level_ = level_;
+ result.rewardId_ = rewardId_;
+ onBuilt();
+ return result;
+ }
+
+ @java.lang.Override
+ public Builder clone() {
+ return super.clone();
+ }
+ @java.lang.Override
+ public Builder setField(
+ com.google.protobuf.Descriptors.FieldDescriptor field,
+ java.lang.Object value) {
+ return super.setField(field, value);
+ }
+ @java.lang.Override
+ public Builder clearField(
+ com.google.protobuf.Descriptors.FieldDescriptor field) {
+ return super.clearField(field);
+ }
+ @java.lang.Override
+ public Builder clearOneof(
+ com.google.protobuf.Descriptors.OneofDescriptor oneof) {
+ return super.clearOneof(oneof);
+ }
+ @java.lang.Override
+ public Builder setRepeatedField(
+ com.google.protobuf.Descriptors.FieldDescriptor field,
+ int index, java.lang.Object value) {
+ return super.setRepeatedField(field, index, value);
+ }
+ @java.lang.Override
+ public Builder addRepeatedField(
+ com.google.protobuf.Descriptors.FieldDescriptor field,
+ java.lang.Object value) {
+ return super.addRepeatedField(field, value);
+ }
+ @java.lang.Override
+ public Builder mergeFrom(com.google.protobuf.Message other) {
+ if (other instanceof emu.grasscutter.net.proto.TakePlayerLevelRewardRspOuterClass.TakePlayerLevelRewardRsp) {
+ return mergeFrom((emu.grasscutter.net.proto.TakePlayerLevelRewardRspOuterClass.TakePlayerLevelRewardRsp)other);
+ } else {
+ super.mergeFrom(other);
+ return this;
+ }
+ }
+
+ public Builder mergeFrom(emu.grasscutter.net.proto.TakePlayerLevelRewardRspOuterClass.TakePlayerLevelRewardRsp other) {
+ if (other == emu.grasscutter.net.proto.TakePlayerLevelRewardRspOuterClass.TakePlayerLevelRewardRsp.getDefaultInstance()) return this;
+ if (other.getRetcode() != 0) {
+ setRetcode(other.getRetcode());
+ }
+ if (other.getLevel() != 0) {
+ setLevel(other.getLevel());
+ }
+ if (other.getRewardId() != 0) {
+ setRewardId(other.getRewardId());
+ }
+ this.mergeUnknownFields(other.unknownFields);
+ onChanged();
+ return this;
+ }
+
+ @java.lang.Override
+ public final boolean isInitialized() {
+ return true;
+ }
+
+ @java.lang.Override
+ public Builder mergeFrom(
+ com.google.protobuf.CodedInputStream input,
+ com.google.protobuf.ExtensionRegistryLite extensionRegistry)
+ throws java.io.IOException {
+ emu.grasscutter.net.proto.TakePlayerLevelRewardRspOuterClass.TakePlayerLevelRewardRsp parsedMessage = null;
+ try {
+ parsedMessage = PARSER.parsePartialFrom(input, extensionRegistry);
+ } catch (com.google.protobuf.InvalidProtocolBufferException e) {
+ parsedMessage = (emu.grasscutter.net.proto.TakePlayerLevelRewardRspOuterClass.TakePlayerLevelRewardRsp) e.getUnfinishedMessage();
+ throw e.unwrapIOException();
+ } finally {
+ if (parsedMessage != null) {
+ mergeFrom(parsedMessage);
+ }
+ }
+ return this;
+ }
+
+ private int retcode_ ;
+ /**
+ * int32 retcode = 1;
+ * @return The retcode.
+ */
+ @java.lang.Override
+ public int getRetcode() {
+ return retcode_;
+ }
+ /**
+ * int32 retcode = 1;
+ * @param value The retcode to set.
+ * @return This builder for chaining.
+ */
+ public Builder setRetcode(int value) {
+
+ retcode_ = value;
+ onChanged();
+ return this;
+ }
+ /**
+ * int32 retcode = 1;
+ * @return This builder for chaining.
+ */
+ public Builder clearRetcode() {
+
+ retcode_ = 0;
+ onChanged();
+ return this;
+ }
+
+ private int level_ ;
+ /**
+ * uint32 level = 2;
+ * @return The level.
+ */
+ @java.lang.Override
+ public int getLevel() {
+ return level_;
+ }
+ /**
+ * uint32 level = 2;
+ * @param value The level to set.
+ * @return This builder for chaining.
+ */
+ public Builder setLevel(int value) {
+
+ level_ = value;
+ onChanged();
+ return this;
+ }
+ /**
+ * uint32 level = 2;
+ * @return This builder for chaining.
+ */
+ public Builder clearLevel() {
+
+ level_ = 0;
+ onChanged();
+ return this;
+ }
+
+ private int rewardId_ ;
+ /**
+ * uint32 rewardId = 3;
+ * @return The rewardId.
+ */
+ @java.lang.Override
+ public int getRewardId() {
+ return rewardId_;
+ }
+ /**
+ * uint32 rewardId = 3;
+ * @param value The rewardId to set.
+ * @return This builder for chaining.
+ */
+ public Builder setRewardId(int value) {
+
+ rewardId_ = value;
+ onChanged();
+ return this;
+ }
+ /**
+ * uint32 rewardId = 3;
+ * @return This builder for chaining.
+ */
+ public Builder clearRewardId() {
+
+ rewardId_ = 0;
+ onChanged();
+ return this;
+ }
+ @java.lang.Override
+ public final Builder setUnknownFields(
+ final com.google.protobuf.UnknownFieldSet unknownFields) {
+ return super.setUnknownFields(unknownFields);
+ }
+
+ @java.lang.Override
+ public final Builder mergeUnknownFields(
+ final com.google.protobuf.UnknownFieldSet unknownFields) {
+ return super.mergeUnknownFields(unknownFields);
+ }
+
+
+ // @@protoc_insertion_point(builder_scope:TakePlayerLevelRewardRsp)
+ }
+
+ // @@protoc_insertion_point(class_scope:TakePlayerLevelRewardRsp)
+ private static final emu.grasscutter.net.proto.TakePlayerLevelRewardRspOuterClass.TakePlayerLevelRewardRsp DEFAULT_INSTANCE;
+ static {
+ DEFAULT_INSTANCE = new emu.grasscutter.net.proto.TakePlayerLevelRewardRspOuterClass.TakePlayerLevelRewardRsp();
+ }
+
+ public static emu.grasscutter.net.proto.TakePlayerLevelRewardRspOuterClass.TakePlayerLevelRewardRsp getDefaultInstance() {
+ return DEFAULT_INSTANCE;
+ }
+
+ private static final com.google.protobuf.Parser
+ PARSER = new com.google.protobuf.AbstractParser() {
+ @java.lang.Override
+ public TakePlayerLevelRewardRsp parsePartialFrom(
+ com.google.protobuf.CodedInputStream input,
+ com.google.protobuf.ExtensionRegistryLite extensionRegistry)
+ throws com.google.protobuf.InvalidProtocolBufferException {
+ return new TakePlayerLevelRewardRsp(input, extensionRegistry);
+ }
+ };
+
+ public static com.google.protobuf.Parser parser() {
+ return PARSER;
+ }
+
+ @java.lang.Override
+ public com.google.protobuf.Parser getParserForType() {
+ return PARSER;
+ }
+
+ @java.lang.Override
+ public emu.grasscutter.net.proto.TakePlayerLevelRewardRspOuterClass.TakePlayerLevelRewardRsp getDefaultInstanceForType() {
+ return DEFAULT_INSTANCE;
+ }
+
+ }
+
+ private static final com.google.protobuf.Descriptors.Descriptor
+ internal_static_TakePlayerLevelRewardRsp_descriptor;
+ private static final
+ com.google.protobuf.GeneratedMessageV3.FieldAccessorTable
+ internal_static_TakePlayerLevelRewardRsp_fieldAccessorTable;
+
+ public static com.google.protobuf.Descriptors.FileDescriptor
+ getDescriptor() {
+ return descriptor;
+ }
+ private static com.google.protobuf.Descriptors.FileDescriptor
+ descriptor;
+ static {
+ java.lang.String[] descriptorData = {
+ "\n\036TakePlayerLevelRewardRsp.proto\"L\n\030Take" +
+ "PlayerLevelRewardRsp\022\017\n\007retcode\030\001 \001(\005\022\r\n" +
+ "\005level\030\002 \001(\r\022\020\n\010rewardId\030\003 \001(\rB\033\n\031emu.gr" +
+ "asscutter.net.protob\006proto3"
+ };
+ descriptor = com.google.protobuf.Descriptors.FileDescriptor
+ .internalBuildGeneratedFileFrom(descriptorData,
+ new com.google.protobuf.Descriptors.FileDescriptor[] {
+ });
+ internal_static_TakePlayerLevelRewardRsp_descriptor =
+ getDescriptor().getMessageTypes().get(0);
+ internal_static_TakePlayerLevelRewardRsp_fieldAccessorTable = new
+ com.google.protobuf.GeneratedMessageV3.FieldAccessorTable(
+ internal_static_TakePlayerLevelRewardRsp_descriptor,
+ new java.lang.String[] { "Retcode", "Level", "RewardId", });
+ }
+
+ // @@protoc_insertion_point(outer_class_scope)
+}
diff --git a/src/main/java/emu/grasscutter/netty/MihoyoKcpServer.java b/src/main/java/emu/grasscutter/netty/MihoyoKcpServer.java
index c3a9297b2..41e8ad412 100644
--- a/src/main/java/emu/grasscutter/netty/MihoyoKcpServer.java
+++ b/src/main/java/emu/grasscutter/netty/MihoyoKcpServer.java
@@ -64,9 +64,8 @@ public class MihoyoKcpServer extends Thread {
// Wait until the server socket is closed.
f.channel().closeFuture().sync();
- } catch (Exception e) {
- // TODO Auto-generated catch block
- e.printStackTrace();
+ } catch (Exception exception) {
+ Grasscutter.getLogger().error("Unable to start game server.", exception);
} finally {
// Close
finish();
diff --git a/src/main/java/emu/grasscutter/server/dispatch/DispatchServer.java b/src/main/java/emu/grasscutter/server/dispatch/DispatchServer.java
index ba280fb3e..5cb06d5e1 100644
--- a/src/main/java/emu/grasscutter/server/dispatch/DispatchServer.java
+++ b/src/main/java/emu/grasscutter/server/dispatch/DispatchServer.java
@@ -26,6 +26,7 @@ import emu.grasscutter.utils.Utils;
import javax.net.ssl.KeyManagerFactory;
import javax.net.ssl.SSLContext;
import java.io.*;
+import java.net.BindException;
import java.net.InetSocketAddress;
import java.net.URI;
import java.net.URLDecoder;
@@ -39,47 +40,48 @@ public final class DispatchServer {
private final InetSocketAddress address;
private final Gson gson;
private final String defaultServerName = "os_usa";
-
+
public String regionListBase64;
public HashMap regions;
public DispatchServer() {
this.regions = new HashMap();
- this.address = new InetSocketAddress(Grasscutter.getConfig().getDispatchOptions().Ip, Grasscutter.getConfig().getDispatchOptions().Port);
+ this.address = new InetSocketAddress(Grasscutter.getConfig().getDispatchOptions().Ip,
+ Grasscutter.getConfig().getDispatchOptions().Port);
this.gson = new GsonBuilder().create();
-
+
this.loadQueries();
this.initRegion();
}
-
+
public InetSocketAddress getAddress() {
return address;
}
-
+
public Gson getGsonFactory() {
return gson;
}
public QueryCurrRegionHttpRsp getCurrRegion() {
// Needs to be fixed by having the game servers connect to the dispatch server.
- if(Grasscutter.getConfig().RunMode.equalsIgnoreCase("HYBRID")) {
+ if (Grasscutter.getConfig().RunMode.equalsIgnoreCase("HYBRID")) {
return regions.get(defaultServerName).parsedRegionQuery;
}
Grasscutter.getLogger().warn("[Dispatch] Unsupported run mode for getCurrRegion()");
return null;
}
-
+
public void loadQueries() {
File file;
-
+
file = new File(Grasscutter.getConfig().DATA_FOLDER + "query_region_list.txt");
if (file.exists()) {
query_region_list = new String(FileUtils.read(file));
} else {
Grasscutter.getLogger().warn("[Dispatch] query_region_list not found! Using default region list.");
}
-
+
file = new File(Grasscutter.getConfig().DATA_FOLDER + "query_cur_region.txt");
if (file.exists()) {
query_cur_region = new String(FileUtils.read(file));
@@ -92,40 +94,58 @@ public final class DispatchServer {
try {
byte[] decoded = Base64.getDecoder().decode(query_region_list);
QueryRegionListHttpRsp rl = QueryRegionListHttpRsp.parseFrom(decoded);
-
+
byte[] decoded2 = Base64.getDecoder().decode(query_cur_region);
QueryCurrRegionHttpRsp regionQuery = QueryCurrRegionHttpRsp.parseFrom(decoded2);
List servers = new ArrayList();
List usedNames = new ArrayList(); // List to check for potential naming conflicts
- if(Grasscutter.getConfig().RunMode.equalsIgnoreCase("HYBRID")) { // Automatically add the game server if in hybrid mode
+ if (Grasscutter.getConfig().RunMode.equalsIgnoreCase("HYBRID")) { // Automatically add the game server if in
+ // hybrid mode
RegionSimpleInfo server = RegionSimpleInfo.newBuilder()
.setName("os_usa")
.setTitle(Grasscutter.getConfig().getGameServerOptions().Name)
.setType("DEV_PUBLIC")
- .setDispatchUrl("http" + (Grasscutter.getConfig().getDispatchOptions().FrontHTTPS ? "s" : "") + "://" + (Grasscutter.getConfig().getDispatchOptions().PublicIp.isEmpty() ? Grasscutter.getConfig().getDispatchOptions().Ip : Grasscutter.getConfig().getDispatchOptions().PublicIp) + ":" + (Grasscutter.getConfig().getDispatchOptions().PublicPort != 0 ? Grasscutter.getConfig().getDispatchOptions().PublicPort : Grasscutter.getConfig().getDispatchOptions().Port) + "/query_cur_region_" + defaultServerName)
+ .setDispatchUrl(
+ "http" + (Grasscutter.getConfig().getDispatchOptions().FrontHTTPS ? "s" : "") + "://"
+ + (Grasscutter.getConfig().getDispatchOptions().PublicIp.isEmpty()
+ ? Grasscutter.getConfig().getDispatchOptions().Ip
+ : Grasscutter.getConfig().getDispatchOptions().PublicIp)
+ + ":"
+ + (Grasscutter.getConfig().getDispatchOptions().PublicPort != 0
+ ? Grasscutter.getConfig().getDispatchOptions().PublicPort
+ : Grasscutter.getConfig().getDispatchOptions().Port)
+ + "/query_cur_region_" + defaultServerName)
.build();
usedNames.add(defaultServerName);
servers.add(server);
RegionInfo serverRegion = regionQuery.getRegionInfo().toBuilder()
- .setIp((Grasscutter.getConfig().getGameServerOptions().PublicIp.isEmpty() ? Grasscutter.getConfig().getGameServerOptions().Ip : Grasscutter.getConfig().getGameServerOptions().PublicIp))
- .setPort(Grasscutter.getConfig().getGameServerOptions().PublicPort != 0 ? Grasscutter.getConfig().getGameServerOptions().PublicPort : Grasscutter.getConfig().getGameServerOptions().Port)
- .setSecretKey(ByteString.copyFrom(FileUtils.read(Grasscutter.getConfig().KEY_FOLDER + "dispatchSeed.bin")))
+ .setIp((Grasscutter.getConfig().getGameServerOptions().PublicIp.isEmpty()
+ ? Grasscutter.getConfig().getGameServerOptions().Ip
+ : Grasscutter.getConfig().getGameServerOptions().PublicIp))
+ .setPort(Grasscutter.getConfig().getGameServerOptions().PublicPort != 0
+ ? Grasscutter.getConfig().getGameServerOptions().PublicPort
+ : Grasscutter.getConfig().getGameServerOptions().Port)
+ .setSecretKey(ByteString
+ .copyFrom(FileUtils.read(Grasscutter.getConfig().KEY_FOLDER + "dispatchSeed.bin")))
.build();
QueryCurrRegionHttpRsp parsedRegionQuery = regionQuery.toBuilder().setRegionInfo(serverRegion).build();
- regions.put(defaultServerName, new RegionData(parsedRegionQuery, Base64.getEncoder().encodeToString(parsedRegionQuery.toByteString().toByteArray())));
+ regions.put(defaultServerName, new RegionData(parsedRegionQuery,
+ Base64.getEncoder().encodeToString(parsedRegionQuery.toByteString().toByteArray())));
} else {
- if(Grasscutter.getConfig().getDispatchOptions().getGameServers().length == 0) {
- Grasscutter.getLogger().error("[Dispatch] There are no game servers available. Exiting due to unplayable state.");
+ if (Grasscutter.getConfig().getDispatchOptions().getGameServers().length == 0) {
+ Grasscutter.getLogger()
+ .error("[Dispatch] There are no game servers available. Exiting due to unplayable state.");
System.exit(1);
}
}
- for (Config.DispatchServerOptions.RegionInfo regionInfo : Grasscutter.getConfig().getDispatchOptions().getGameServers()) {
- if(usedNames.contains(regionInfo.Name)) {
+ for (Config.DispatchServerOptions.RegionInfo regionInfo : Grasscutter.getConfig().getDispatchOptions()
+ .getGameServers()) {
+ if (usedNames.contains(regionInfo.Name)) {
Grasscutter.getLogger().error("Region name already in use.");
continue;
}
@@ -133,7 +153,12 @@ public final class DispatchServer {
.setName(regionInfo.Name)
.setTitle(regionInfo.Title)
.setType("DEV_PUBLIC")
- .setDispatchUrl("http" + (Grasscutter.getConfig().getDispatchOptions().FrontHTTPS ? "s" : "") + "://" + (Grasscutter.getConfig().getDispatchOptions().PublicIp.isEmpty() ? Grasscutter.getConfig().getDispatchOptions().Ip : Grasscutter.getConfig().getDispatchOptions().PublicIp) + ":" + getAddress().getPort() + "/query_cur_region_" + regionInfo.Name)
+ .setDispatchUrl(
+ "http" + (Grasscutter.getConfig().getDispatchOptions().FrontHTTPS ? "s" : "") + "://"
+ + (Grasscutter.getConfig().getDispatchOptions().PublicIp.isEmpty()
+ ? Grasscutter.getConfig().getDispatchOptions().Ip
+ : Grasscutter.getConfig().getDispatchOptions().PublicIp)
+ + ":" + getAddress().getPort() + "/query_cur_region_" + regionInfo.Name)
.build();
usedNames.add(regionInfo.Name);
servers.add(server);
@@ -141,19 +166,21 @@ public final class DispatchServer {
RegionInfo serverRegion = regionQuery.getRegionInfo().toBuilder()
.setIp(regionInfo.Ip)
.setPort(regionInfo.Port)
- .setSecretKey(ByteString.copyFrom(FileUtils.read(Grasscutter.getConfig().KEY_FOLDER + "dispatchSeed.bin")))
+ .setSecretKey(ByteString
+ .copyFrom(FileUtils.read(Grasscutter.getConfig().KEY_FOLDER + "dispatchSeed.bin")))
.build();
QueryCurrRegionHttpRsp parsedRegionQuery = regionQuery.toBuilder().setRegionInfo(serverRegion).build();
- regions.put(regionInfo.Name, new RegionData(parsedRegionQuery, Base64.getEncoder().encodeToString(parsedRegionQuery.toByteString().toByteArray())));
+ regions.put(regionInfo.Name, new RegionData(parsedRegionQuery,
+ Base64.getEncoder().encodeToString(parsedRegionQuery.toByteString().toByteArray())));
}
QueryRegionListHttpRsp regionList = QueryRegionListHttpRsp.newBuilder()
- .addAllServers(servers)
- .setClientSecretKey(rl.getClientSecretKey())
- .setClientCustomConfigEncrypted(rl.getClientCustomConfigEncrypted())
- .setEnableLoginPc(true)
- .build();
+ .addAllServers(servers)
+ .setClientSecretKey(rl.getClientSecretKey())
+ .setClientCustomConfigEncrypted(rl.getClientCustomConfigEncrypted())
+ .setEnableLoginPc(true)
+ .build();
this.regionListBase64 = Base64.getEncoder().encodeToString(regionList.toByteString().toByteArray());
} catch (Exception e) {
@@ -161,52 +188,92 @@ public final class DispatchServer {
}
}
+ private HttpServer safelyCreateServer(InetSocketAddress address) {
+ try {
+ return HttpServer.create(address, 0);
+ } catch (BindException ignored) {
+ Grasscutter.getLogger().error("Unable to bind to port: " + getAddress().getPort() + " (HTTP)");
+ } catch (Exception exception) {
+ Grasscutter.getLogger().error("Unable to start HTTP server.", exception);
+ }
+ return null;
+ }
+
public void start() throws Exception {
HttpServer server;
if (Grasscutter.getConfig().getDispatchOptions().UseSSL) {
- HttpsServer httpsServer;
- httpsServer = HttpsServer.create(getAddress(), 0);
+ HttpsServer httpsServer = HttpsServer.create(getAddress(), 0);
SSLContext sslContext = SSLContext.getInstance("TLS");
try (FileInputStream fis = new FileInputStream(Grasscutter.getConfig().getDispatchOptions().KeystorePath)) {
char[] keystorePassword = Grasscutter.getConfig().getDispatchOptions().KeystorePassword.toCharArray();
- KeyStore ks = KeyStore.getInstance("PKCS12");
- ks.load(fis, keystorePassword);
- KeyManagerFactory kmf = KeyManagerFactory.getInstance("SunX509");
- kmf.init(ks, keystorePassword);
-
- sslContext.init(kmf.getKeyManagers(), null, null);
-
+ KeyManagerFactory _kmf;
+ try {
+ KeyStore ks = KeyStore.getInstance("PKCS12");
+ ks.load(fis, keystorePassword);
+ KeyManagerFactory kmf = KeyManagerFactory.getInstance("SunX509");
+ _kmf = kmf;
+ kmf.init(ks, keystorePassword);
+ } catch (Exception originalEx) {
+ try {
+ // try to initialize kmf with the default password
+ char[] defaultPassword = "123456".toCharArray();
+
+ Grasscutter.getLogger()
+ .warn("[Dispatch] Unable to load keystore. Trying default keystore password...");
+ KeyStore ks = KeyStore.getInstance("PKCS12");
+ ks.load(fis, defaultPassword);
+ KeyManagerFactory kmf = KeyManagerFactory.getInstance("SunX509");
+ kmf.init(ks, defaultPassword);
+ _kmf = kmf;
+
+ Grasscutter.getLogger().warn(
+ "[Dispatch] The default keystore password was loaded successfully. Please consider setting the password in config.json.");
+ } catch (Exception ignored) {
+ Grasscutter.getLogger().warn("[Dispatch] Error while loading keystore!");
+
+ // don't care about the exception for the "123456" default password attempt
+ originalEx.printStackTrace();
+ throw originalEx;
+ }
+ }
+
+ sslContext.init(_kmf.getKeyManagers(), null, null);
+
httpsServer.setHttpsConfigurator(new HttpsConfigurator(sslContext));
server = httpsServer;
+ } catch (BindException ignored) {
+ Grasscutter.getLogger().error("Unable to bind to port: " + getAddress().getPort() + " (HTTPS)");
+ server = this.safelyCreateServer(this.getAddress());
} catch (Exception e) {
Grasscutter.getLogger().warn("[Dispatch] No SSL cert found! Falling back to HTTP server.");
Grasscutter.getConfig().getDispatchOptions().UseSSL = false;
- server = HttpServer.create(getAddress(), 0);
+ server = this.safelyCreateServer(this.getAddress());
}
} else {
- server = HttpServer.create(getAddress(), 0);
+ server = this.safelyCreateServer(this.getAddress());
}
+ if (server == null)
+ throw new NullPointerException("An HTTP server was not created.");
+
server.createContext("/", t -> responseHTML(t, "Hello"));
-
+
// Dispatch
server.createContext("/query_region_list", t -> {
- // Log incoming request.
- Grasscutter.getLogger().info(String.format("[Dispatch] Client %s request: query_region_list", t.getRemoteAddress()));
-
- // Invoke event.
- QueryAllRegionsEvent event = new QueryAllRegionsEvent(this.regionListBase64); event.call();
- // Respond with event result.
- responseHTML(t, event.getRegionList());
+ // Log
+ Grasscutter.getLogger()
+ .info(String.format("[Dispatch] Client %s request: query_region_list", t.getRemoteAddress()));
+
+ responseHTML(t, regionListBase64);
});
for (String regionName : regions.keySet()) {
server.createContext("/query_cur_region_" + regionName, t -> {
String regionCurrentBase64 = regions.get(regionName).Base64;
- // Log incoming request.
- Grasscutter.getLogger().info(String.format("Client %s request: query_cur_region_%s", t.getRemoteAddress(), regionName));
-
- // Create a response from the request query parameters.
+ // Log
+ Grasscutter.getLogger().info(
+ String.format("Client %s request: query_cur_region_%s", t.getRemoteAddress(), regionName));
+ // Create a response form the request query parameters
URI uri = t.getRequestURI();
String response = "CAESGE5vdCBGb3VuZCB2ZXJzaW9uIGNvbmZpZw==";
if (uri.getQuery() != null && uri.getQuery().length() > 0) {
@@ -227,7 +294,8 @@ public final class DispatchServer {
try {
String body = Utils.toString(t.getRequestBody());
requestData = getGsonFactory().fromJson(body, LoginAccountRequestJson.class);
- } catch (Exception ignored) { }
+ } catch (Exception ignored) {
+ }
// Create response json
if (requestData == null) {
@@ -235,16 +303,19 @@ public final class DispatchServer {
}
LoginResultJson responseData = new LoginResultJson();
- Grasscutter.getLogger().info(String.format("[Dispatch] Client %s is trying to log in", t.getRemoteAddress()));
-
+ Grasscutter.getLogger()
+ .info(String.format("[Dispatch] Client %s is trying to log in", t.getRemoteAddress()));
+
// Login
Account account = DatabaseHelper.getAccountByName(requestData.account);
-
+
// Check if account exists, else create a new one.
if (account == null) {
- // Account doesnt exist, so we can either auto create it if the config value is set
+ // Account doesnt exist, so we can either auto create it if the config value is
+ // set
if (Grasscutter.getConfig().getDispatchOptions().AutomaticallyCreateAccounts) {
- // This account has been created AUTOMATICALLY. There will be no permissions added.
+ // This account has been created AUTOMATICALLY. There will be no permissions
+ // added.
account = DatabaseHelper.createAccountWithId(requestData.account, 0);
if (account != null) {
@@ -253,19 +324,23 @@ public final class DispatchServer {
responseData.data.account.token = account.generateSessionKey();
responseData.data.account.email = account.getEmail();
- Grasscutter.getLogger().info(String.format("[Dispatch] Client %s failed to log in: Account %s created", t.getRemoteAddress(), responseData.data.account.uid));
+ Grasscutter.getLogger()
+ .info(String.format("[Dispatch] Client %s failed to log in: Account %s created",
+ t.getRemoteAddress(), responseData.data.account.uid));
} else {
responseData.retcode = -201;
responseData.message = "Username not found, create failed.";
- Grasscutter.getLogger().info(String.format("[Dispatch] Client %s failed to log in: Account create failed", t.getRemoteAddress()));
+ Grasscutter.getLogger().info(String.format(
+ "[Dispatch] Client %s failed to log in: Account create failed", t.getRemoteAddress()));
}
} else {
responseData.retcode = -201;
responseData.message = "Username not found.";
- Grasscutter.getLogger().info(String.format("[Dispatch] Client %s failed to log in: Account no found", t.getRemoteAddress()));
- }
+ Grasscutter.getLogger().info(String
+ .format("[Dispatch] Client %s failed to log in: Account no found", t.getRemoteAddress()));
+ }
} else {
// Account was found, log the player in
responseData.message = "OK";
@@ -273,7 +348,8 @@ public final class DispatchServer {
responseData.data.account.token = account.generateSessionKey();
responseData.data.account.email = account.getEmail();
- Grasscutter.getLogger().info(String.format("[Dispatch] Client %s logged in as %s", t.getRemoteAddress(), responseData.data.account.uid));
+ Grasscutter.getLogger().info(String.format("[Dispatch] Client %s logged in as %s", t.getRemoteAddress(),
+ responseData.data.account.uid));
}
responseJSON(t, responseData);
@@ -285,31 +361,35 @@ public final class DispatchServer {
try {
String body = Utils.toString(t.getRequestBody());
requestData = getGsonFactory().fromJson(body, LoginTokenRequestJson.class);
- } catch (Exception ignored) { }
+ } catch (Exception ignored) {
+ }
// Create response json
if (requestData == null) {
return;
}
LoginResultJson responseData = new LoginResultJson();
- Grasscutter.getLogger().info(String.format("[Dispatch] Client %s is trying to log in via token", t.getRemoteAddress()));
+ Grasscutter.getLogger()
+ .info(String.format("[Dispatch] Client %s is trying to log in via token", t.getRemoteAddress()));
// Login
Account account = DatabaseHelper.getAccountById(requestData.uid);
-
+
// Test
if (account == null || !account.getSessionKey().equals(requestData.token)) {
responseData.retcode = -111;
responseData.message = "Game account cache information error";
- Grasscutter.getLogger().info(String.format("[Dispatch] Client %s failed to log in via token", t.getRemoteAddress()));
+ Grasscutter.getLogger()
+ .info(String.format("[Dispatch] Client %s failed to log in via token", t.getRemoteAddress()));
} else {
responseData.message = "OK";
responseData.data.account.uid = requestData.uid;
responseData.data.account.token = requestData.token;
responseData.data.account.email = account.getEmail();
- Grasscutter.getLogger().info(String.format("[Dispatch] Client %s logged in via token as %s", t.getRemoteAddress(), responseData.data.account.uid));
+ Grasscutter.getLogger().info(String.format("[Dispatch] Client %s logged in via token as %s",
+ t.getRemoteAddress(), responseData.data.account.uid));
}
responseJSON(t, responseData);
@@ -321,113 +401,112 @@ public final class DispatchServer {
try {
String body = Utils.toString(t.getRequestBody());
requestData = getGsonFactory().fromJson(body, ComboTokenReqJson.class);
- } catch (Exception ignored) { }
+ } catch (Exception ignored) {
+ }
// Create response json
if (requestData == null || requestData.data == null) {
return;
}
- LoginTokenData loginData = getGsonFactory().fromJson(requestData.data, LoginTokenData.class); // Get login data
+ LoginTokenData loginData = getGsonFactory().fromJson(requestData.data, LoginTokenData.class); // Get login
+ // data
ComboTokenResJson responseData = new ComboTokenResJson();
// Login
Account account = DatabaseHelper.getAccountById(loginData.uid);
-
+
// Test
if (account == null || !account.getSessionKey().equals(loginData.token)) {
responseData.retcode = -201;
responseData.message = "Wrong session key.";
- Grasscutter.getLogger().info(String.format("[Dispatch] Client %s failed to exchange combo token", t.getRemoteAddress()));
+ Grasscutter.getLogger().info(
+ String.format("[Dispatch] Client %s failed to exchange combo token", t.getRemoteAddress()));
} else {
responseData.message = "OK";
responseData.data.open_id = loginData.uid;
responseData.data.combo_id = "157795300";
responseData.data.combo_token = account.generateLoginToken();
- Grasscutter.getLogger().info(String.format("[Dispatch] Client %s succeed to exchange combo token", t.getRemoteAddress()));
+ Grasscutter.getLogger().info(
+ String.format("[Dispatch] Client %s succeed to exchange combo token", t.getRemoteAddress()));
}
responseJSON(t, responseData);
});
// Agreement and Protocol
server.createContext( // hk4e-sdk-os.hoyoverse.com
- "/hk4e_global/mdk/agreement/api/getAgreementInfos",
- new DispatchHttpJsonHandler("{\"retcode\":0,\"message\":\"OK\",\"data\":{\"marketing_agreements\":[]}}")
- );
+ "/hk4e_global/mdk/agreement/api/getAgreementInfos",
+ new DispatchHttpJsonHandler(
+ "{\"retcode\":0,\"message\":\"OK\",\"data\":{\"marketing_agreements\":[]}}"));
server.createContext( // hk4e-sdk-os.hoyoverse.com
- "/hk4e_global/combo/granter/api/compareProtocolVersion",
- new DispatchHttpJsonHandler("{\"retcode\":0,\"message\":\"OK\",\"data\":{\"modified\":true,\"protocol\":{\"id\":0,\"app_id\":4,\"language\":\"en\",\"user_proto\":\"\",\"priv_proto\":\"\",\"major\":7,\"minimum\":0,\"create_time\":\"0\",\"teenager_proto\":\"\",\"third_proto\":\"\"}}}")
- );
+ "/hk4e_global/combo/granter/api/compareProtocolVersion",
+ new DispatchHttpJsonHandler(
+ "{\"retcode\":0,\"message\":\"OK\",\"data\":{\"modified\":true,\"protocol\":{\"id\":0,\"app_id\":4,\"language\":\"en\",\"user_proto\":\"\",\"priv_proto\":\"\",\"major\":7,\"minimum\":0,\"create_time\":\"0\",\"teenager_proto\":\"\",\"third_proto\":\"\"}}}"));
// Game data
server.createContext( // hk4e-api-os.hoyoverse.com
- "/common/hk4e_global/announcement/api/getAlertPic",
- new DispatchHttpJsonHandler("{\"retcode\":0,\"message\":\"OK\",\"data\":{\"total\":0,\"list\":[]}}")
- );
+ "/common/hk4e_global/announcement/api/getAlertPic",
+ new DispatchHttpJsonHandler("{\"retcode\":0,\"message\":\"OK\",\"data\":{\"total\":0,\"list\":[]}}"));
server.createContext( // hk4e-api-os.hoyoverse.com
"/common/hk4e_global/announcement/api/getAlertAnn",
- new DispatchHttpJsonHandler("{\"retcode\":0,\"message\":\"OK\",\"data\":{\"alert\":false,\"alert_id\":0,\"remind\":true}}")
- );
+ new DispatchHttpJsonHandler(
+ "{\"retcode\":0,\"message\":\"OK\",\"data\":{\"alert\":false,\"alert_id\":0,\"remind\":true}}"));
server.createContext( // hk4e-api-os.hoyoverse.com
- "/common/hk4e_global/announcement/api/getAnnList",
- new DispatchHttpJsonHandler("{\"retcode\":0,\"message\":\"OK\",\"data\":{\"list\":[],\"total\":0,\"type_list\":[],\"alert\":false,\"alert_id\":0,\"timezone\":0,\"t\":\"" + System.currentTimeMillis() + "\"}}")
- );
+ "/common/hk4e_global/announcement/api/getAnnList",
+ new DispatchHttpJsonHandler(
+ "{\"retcode\":0,\"message\":\"OK\",\"data\":{\"list\":[],\"total\":0,\"type_list\":[],\"alert\":false,\"alert_id\":0,\"timezone\":0,\"t\":\""
+ + System.currentTimeMillis() + "\"}}"));
server.createContext( // hk4e-api-os-static.hoyoverse.com
- "/common/hk4e_global/announcement/api/getAnnContent",
- new DispatchHttpJsonHandler("{\"retcode\":0,\"message\":\"OK\",\"data\":{\"list\":[],\"total\":0}}")
- );
+ "/common/hk4e_global/announcement/api/getAnnContent",
+ new DispatchHttpJsonHandler("{\"retcode\":0,\"message\":\"OK\",\"data\":{\"list\":[],\"total\":0}}"));
server.createContext( // hk4e-sdk-os.hoyoverse.com
- "/hk4e_global/mdk/shopwindow/shopwindow/listPriceTier",
- new DispatchHttpJsonHandler("{\"retcode\":0,\"message\":\"OK\",\"data\":{\"suggest_currency\":\"USD\",\"tiers\":[]}}")
- );
+ "/hk4e_global/mdk/shopwindow/shopwindow/listPriceTier",
+ new DispatchHttpJsonHandler(
+ "{\"retcode\":0,\"message\":\"OK\",\"data\":{\"suggest_currency\":\"USD\",\"tiers\":[]}}"));
// Captcha
server.createContext( // api-account-os.hoyoverse.com
- "/account/risky/api/check",
- new DispatchHttpJsonHandler("{\"retcode\":0,\"message\":\"OK\",\"data\":{\"id\":\"c8820f246a5241ab9973f71df3ddd791\",\"action\":\"\",\"geetest\":{\"challenge\":\"\",\"gt\":\"\",\"new_captcha\":0,\"success\":1}}}")
- );
- // Config
+ "/account/risky/api/check",
+ new DispatchHttpJsonHandler(
+ "{\"retcode\":0,\"message\":\"OK\",\"data\":{\"id\":\"c8820f246a5241ab9973f71df3ddd791\",\"action\":\"\",\"geetest\":{\"challenge\":\"\",\"gt\":\"\",\"new_captcha\":0,\"success\":1}}}"));
+ // Config
server.createContext( // sdk-os-static.hoyoverse.com
- "/combo/box/api/config/sdk/combo",
- new DispatchHttpJsonHandler("{\"retcode\":0,\"message\":\"OK\",\"data\":{\"vals\":{\"disable_email_bind_skip\":\"false\",\"email_bind_remind_interval\":\"7\",\"email_bind_remind\":\"true\"}}}")
- );
+ "/combo/box/api/config/sdk/combo",
+ new DispatchHttpJsonHandler(
+ "{\"retcode\":0,\"message\":\"OK\",\"data\":{\"vals\":{\"disable_email_bind_skip\":\"false\",\"email_bind_remind_interval\":\"7\",\"email_bind_remind\":\"true\"}}}"));
server.createContext( // hk4e-sdk-os-static.hoyoverse.com
- "/hk4e_global/combo/granter/api/getConfig",
- new DispatchHttpJsonHandler("{\"retcode\":0,\"message\":\"OK\",\"data\":{\"protocol\":true,\"qr_enabled\":false,\"log_level\":\"INFO\",\"announce_url\":\"https://webstatic-sea.hoyoverse.com/hk4e/announcement/index.html?sdk_presentation_style=fullscreen\\u0026sdk_screen_transparent=true\\u0026game_biz=hk4e_global\\u0026auth_appid=announcement\\u0026game=hk4e#/\",\"push_alias_type\":2,\"disable_ysdk_guard\":false,\"enable_announce_pic_popup\":true}}")
- );
+ "/hk4e_global/combo/granter/api/getConfig",
+ new DispatchHttpJsonHandler(
+ "{\"retcode\":0,\"message\":\"OK\",\"data\":{\"protocol\":true,\"qr_enabled\":false,\"log_level\":\"INFO\",\"announce_url\":\"https://webstatic-sea.hoyoverse.com/hk4e/announcement/index.html?sdk_presentation_style=fullscreen\\u0026sdk_screen_transparent=true\\u0026game_biz=hk4e_global\\u0026auth_appid=announcement\\u0026game=hk4e#/\",\"push_alias_type\":2,\"disable_ysdk_guard\":false,\"enable_announce_pic_popup\":true}}"));
server.createContext( // hk4e-sdk-os-static.hoyoverse.com
- "/hk4e_global/mdk/shield/api/loadConfig",
- new DispatchHttpJsonHandler("{\"retcode\":0,\"message\":\"OK\",\"data\":{\"id\":6,\"game_key\":\"hk4e_global\",\"client\":\"PC\",\"identity\":\"I_IDENTITY\",\"guest\":false,\"ignore_versions\":\"\",\"scene\":\"S_NORMAL\",\"name\":\"原神海外\",\"disable_regist\":false,\"enable_email_captcha\":false,\"thirdparty\":[\"fb\",\"tw\"],\"disable_mmt\":false,\"server_guest\":false,\"thirdparty_ignore\":{\"tw\":\"\",\"fb\":\"\"},\"enable_ps_bind_account\":false,\"thirdparty_login_configs\":{\"tw\":{\"token_type\":\"TK_GAME_TOKEN\",\"game_token_expires_in\":604800},\"fb\":{\"token_type\":\"TK_GAME_TOKEN\",\"game_token_expires_in\":604800}}}}")
- );
+ "/hk4e_global/mdk/shield/api/loadConfig",
+ new DispatchHttpJsonHandler(
+ "{\"retcode\":0,\"message\":\"OK\",\"data\":{\"id\":6,\"game_key\":\"hk4e_global\",\"client\":\"PC\",\"identity\":\"I_IDENTITY\",\"guest\":false,\"ignore_versions\":\"\",\"scene\":\"S_NORMAL\",\"name\":\"原神海外\",\"disable_regist\":false,\"enable_email_captcha\":false,\"thirdparty\":[\"fb\",\"tw\"],\"disable_mmt\":false,\"server_guest\":false,\"thirdparty_ignore\":{\"tw\":\"\",\"fb\":\"\"},\"enable_ps_bind_account\":false,\"thirdparty_login_configs\":{\"tw\":{\"token_type\":\"TK_GAME_TOKEN\",\"game_token_expires_in\":604800},\"fb\":{\"token_type\":\"TK_GAME_TOKEN\",\"game_token_expires_in\":604800}}}}"));
// Test api?
server.createContext( // abtest-api-data-sg.hoyoverse.com
- "/data_abtest_api/config/experiment/list",
- new DispatchHttpJsonHandler("{\"retcode\":0,\"success\":true,\"message\":\"\",\"data\":[{\"code\":1000,\"type\":2,\"config_id\":\"14\",\"period_id\":\"6036_99\",\"version\":\"1\",\"configs\":{\"cardType\":\"old\"}}]}")
- );
- // Log Server
+ "/data_abtest_api/config/experiment/list",
+ new DispatchHttpJsonHandler(
+ "{\"retcode\":0,\"success\":true,\"message\":\"\",\"data\":[{\"code\":1000,\"type\":2,\"config_id\":\"14\",\"period_id\":\"6036_99\",\"version\":\"1\",\"configs\":{\"cardType\":\"old\"}}]}"));
+ // Log Server
server.createContext( // log-upload-os.mihoyo.com
- "/log/sdk/upload",
- new DispatchHttpJsonHandler("{\"code\":0}")
- );
+ "/log/sdk/upload",
+ new DispatchHttpJsonHandler("{\"code\":0}"));
server.createContext( // log-upload-os.mihoyo.com
- "/sdk/upload",
- new DispatchHttpJsonHandler("{\"code\":0}")
- );
+ "/sdk/upload",
+ new DispatchHttpJsonHandler("{\"code\":0}"));
server.createContext( // /perf/config/verify?device_id=xxx&platform=x&name=xxx
- "/perf/config/verify",
- new DispatchHttpJsonHandler("{\"code\":0}")
- );
-
+ "/perf/config/verify",
+ new DispatchHttpJsonHandler("{\"code\":0}"));
+
// Logging servers
server.createContext( // overseauspider.yuanshen.com
"/log",
- new DispatchHttpJsonHandler("{\"code\":0}")
- );
+ new DispatchHttpJsonHandler("{\"code\":0}"));
server.createContext( // log-upload-os.mihoyo.com
"/crash/dataUpload",
- new DispatchHttpJsonHandler("{\"code\":0}")
- );
- server.createContext("/gacha", t -> responseHTML(t, "Gacha"));
+ new DispatchHttpJsonHandler("{\"code\":0}"));
+ server.createContext("/gacha", t -> responseHTML(t,
+ "Gacha"));
// Start server
server.start();
@@ -450,12 +529,12 @@ public final class DispatchServer {
// Set the response header status and length
t.getResponseHeaders().put("Content-Type", Collections.singletonList("text/html; charset=UTF-8"));
t.sendResponseHeaders(200, response.getBytes().length);
- //Write the response string
+ // Write the response string
OutputStream os = t.getResponseBody();
os.write(response.getBytes());
os.close();
}
-
+
private Map parseQueryString(String qs) {
Map result = new HashMap<>();
if (qs == null) {
@@ -475,7 +554,8 @@ public final class DispatchServer {
if (eqPos < 0 || eqPos > next) {
result.put(URLDecoder.decode(qs.substring(last, next), "utf-8"), "");
} else {
- result.put(URLDecoder.decode(qs.substring(last, eqPos), "utf-8"), URLDecoder.decode(qs.substring(eqPos + 1, next), "utf-8"));
+ result.put(URLDecoder.decode(qs.substring(last, eqPos), "utf-8"),
+ URLDecoder.decode(qs.substring(eqPos + 1, next), "utf-8"));
}
} catch (UnsupportedEncodingException e) {
throw new RuntimeException(e); // will never happen, utf-8 support is mandatory for java
diff --git a/src/main/java/emu/grasscutter/server/packet/recv/HandlerAvatarFetterLevelRewardReq.java b/src/main/java/emu/grasscutter/server/packet/recv/HandlerAvatarFetterLevelRewardReq.java
new file mode 100644
index 000000000..b8b4f5e57
--- /dev/null
+++ b/src/main/java/emu/grasscutter/server/packet/recv/HandlerAvatarFetterLevelRewardReq.java
@@ -0,0 +1,55 @@
+package emu.grasscutter.server.packet.recv;
+
+import emu.grasscutter.data.GenshinData;
+import emu.grasscutter.data.def.RewardData;
+import emu.grasscutter.game.avatar.GenshinAvatar;
+import emu.grasscutter.game.inventory.GenshinItem;
+import emu.grasscutter.game.props.ActionReason;
+import emu.grasscutter.net.packet.Opcodes;
+import emu.grasscutter.net.packet.PacketOpcodes;
+import emu.grasscutter.net.proto.AvatarFetterLevelRewardReqOuterClass.AvatarFetterLevelRewardReq;
+import emu.grasscutter.server.game.GameSession;
+import emu.grasscutter.server.packet.send.PacketAvatarDataNotify;
+import emu.grasscutter.server.packet.send.PacketAvatarFetterDataNotify;
+import emu.grasscutter.server.packet.send.PacketAvatarFetterLevelRewardRsp;
+import emu.grasscutter.server.packet.send.PacketItemAddHintNotify;
+import emu.grasscutter.server.packet.send.PacketUnlockNameCardNotify;
+import emu.grasscutter.net.packet.PacketHandler;
+
+@Opcodes(PacketOpcodes.AvatarFetterLevelRewardReq)
+public class HandlerAvatarFetterLevelRewardReq extends PacketHandler {
+ @Override
+ public void handle(GameSession session, byte[] header, byte[] payload) throws Exception {
+ AvatarFetterLevelRewardReq req = AvatarFetterLevelRewardReq.parseFrom(payload);
+ if (req.getFetterLevel() < 10) {
+ // You don't have a full level of fetter level, why do you want to get a divorce certificate?
+ session.send(new PacketAvatarFetterLevelRewardRsp(req.getAvatarGuid(), req.getFetterLevel()));
+ } else {
+ long avatarGuid = req.getAvatarGuid();
+
+ GenshinAvatar avatar = session
+ .getPlayer()
+ .getAvatars()
+ .getAvatarByGuid(avatarGuid);
+
+ int rewardId = avatar.getNameCardRewardId();
+
+ RewardData card = GenshinData.getRewardDataMap().get(rewardId);
+ int cardId = card.getRewardItemList().get(0).getItemId();
+
+ if (session.getPlayer().getNameCardList().contains(cardId)) {
+ // Already got divorce certificate.
+ session.getPlayer().sendPacket(new PacketAvatarFetterLevelRewardRsp(req.getAvatarGuid(), req.getFetterLevel(), rewardId));
+ return;
+ }
+
+ GenshinItem item = new GenshinItem(cardId);
+ session.getPlayer().getInventory().addItem(item);
+ session.getPlayer().sendPacket(new PacketItemAddHintNotify(item, ActionReason.FetterLevelReward));
+ session.getPlayer().sendPacket(new PacketUnlockNameCardNotify(cardId));
+ session.send(new PacketAvatarFetterDataNotify(avatar));
+ session.send(new PacketAvatarDataNotify(avatar.getPlayer()));
+ session.send(new PacketAvatarFetterLevelRewardRsp(avatarGuid, req.getFetterLevel(), rewardId));
+ }
+ }
+}
diff --git a/src/main/java/emu/grasscutter/server/packet/recv/HandlerSetPlayerBirthdayReq.java b/src/main/java/emu/grasscutter/server/packet/recv/HandlerSetPlayerBirthdayReq.java
index 0edb08f73..31f86128a 100644
--- a/src/main/java/emu/grasscutter/server/packet/recv/HandlerSetPlayerBirthdayReq.java
+++ b/src/main/java/emu/grasscutter/server/packet/recv/HandlerSetPlayerBirthdayReq.java
@@ -1,38 +1,67 @@
package emu.grasscutter.server.packet.recv;
+import emu.grasscutter.net.packet.Opcodes;
+import emu.grasscutter.net.packet.PacketHandler;
+import emu.grasscutter.net.packet.PacketOpcodes;
+import emu.grasscutter.net.proto.SetPlayerBirthdayReqOuterClass.SetPlayerBirthdayReq;
+import emu.grasscutter.net.proto.SocialDetailOuterClass.SocialDetail;
import emu.grasscutter.server.game.GameSession;
import emu.grasscutter.server.packet.send.PacketGetPlayerSocialDetailRsp;
import emu.grasscutter.server.packet.send.PacketSetPlayerBirthdayRsp;
-import emu.grasscutter.net.packet.Opcodes;
-import emu.grasscutter.net.packet.PacketOpcodes;
-import emu.grasscutter.net.packet.PacketHandler;
-
-import emu.grasscutter.net.proto.SocialDetailOuterClass.SocialDetail;
-import emu.grasscutter.net.proto.SetPlayerBirthdayReqOuterClass.SetPlayerBirthdayReq;
-
-import com.google.gson.Gson;
-
@Opcodes(PacketOpcodes.SetPlayerBirthdayReq)
public class HandlerSetPlayerBirthdayReq extends PacketHandler {
@Override
public void handle(GameSession session, byte[] header, byte[] payload) throws Exception {
SetPlayerBirthdayReq req = SetPlayerBirthdayReq.parseFrom(payload);
- if(req.getBirth() != null && req.getBirth().getDay() > 0 && req.getBirth().getMonth() > 0)
- {
- int day = req.getBirth().getDay();
- int month = req.getBirth().getMonth();
-
- // Update birthday value
- session.getPlayer().setBirthday(day, month);
-
- // Save birthday month and day
- session.getPlayer().save();
- SocialDetail.Builder detail = session.getPlayer().getSocialDetail();
-
- session.send(new PacketSetPlayerBirthdayRsp(session.getPlayer()));
- session.send(new PacketGetPlayerSocialDetailRsp(detail));
+ // RET_BIRTHDAY_CANNOT_BE_SET_TWICE = 7009
+ if (session.getPlayer().hasBirthday()) {
+ session.send(new PacketSetPlayerBirthdayRsp(7009));
+ return;
}
+
+ int month = req.getBirthday().getMonth();
+ int day = req.getBirthday().getDay();
+
+ // RET_BIRTHDAY_FORMAT_ERROR = 7022
+ if (!isValidBirthday(month, day)) {
+ session.send(new PacketSetPlayerBirthdayRsp(7022));
+ return;
+ }
+
+ // Update birthday value
+ session.getPlayer().setBirthday(day, month);
+
+ // Save birthday month and day
+ session.getPlayer().save();
+ SocialDetail.Builder detail = session.getPlayer().getSocialDetail();
+
+ session.send(new PacketSetPlayerBirthdayRsp(session.getPlayer()));
+ session.send(new PacketGetPlayerSocialDetailRsp(detail));
}
+
+ private boolean isValidBirthday(int month, int day) {
+
+ switch (month) {
+ case 1:
+ case 3:
+ case 5:
+ case 7:
+ case 8:
+ case 10:
+ case 12:
+ return day > 0 & day <= 31;
+ case 4:
+ case 6:
+ case 9:
+ case 11:
+ return day > 0 && day <= 30;
+ case 2:
+ return day > 0 & day <= 29;
+ }
+
+ return false;
+ }
+
}
diff --git a/src/main/java/emu/grasscutter/server/packet/recv/HandlerTakePlayerLevelRewardReq.java b/src/main/java/emu/grasscutter/server/packet/recv/HandlerTakePlayerLevelRewardReq.java
new file mode 100644
index 000000000..a81da7758
--- /dev/null
+++ b/src/main/java/emu/grasscutter/server/packet/recv/HandlerTakePlayerLevelRewardReq.java
@@ -0,0 +1,48 @@
+package emu.grasscutter.server.packet.recv;
+
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Set;
+
+import emu.grasscutter.Grasscutter;
+import emu.grasscutter.data.GenshinData;
+import emu.grasscutter.data.common.RewardItemData;
+import emu.grasscutter.game.inventory.GenshinItem;
+import emu.grasscutter.game.props.ActionReason;
+import emu.grasscutter.net.packet.Opcodes;
+import emu.grasscutter.net.packet.PacketHandler;
+import emu.grasscutter.net.packet.PacketOpcodes;
+import emu.grasscutter.net.proto.TakePlayerLevelRewardReqOuterClass.TakePlayerLevelRewardReq;
+import emu.grasscutter.server.game.GameSession;
+import emu.grasscutter.server.packet.send.PacketItemAddHintNotify;
+import emu.grasscutter.server.packet.send.PacketTakePlayerLevelRewardRsp;
+
+@Opcodes(PacketOpcodes.TakePlayerLevelRewardReq)
+public class HandlerTakePlayerLevelRewardReq extends PacketHandler {
+ @Override
+ public void handle(GameSession session, byte[] header, byte[] payload) throws Exception {
+ TakePlayerLevelRewardReq req = TakePlayerLevelRewardReq.parseFrom(payload);
+
+ int level = req.getLevel();
+ int rewardId = GenshinData.getPlayerLevelDataMap().get(level).getRewardId();
+
+ if (rewardId != 0) {
+ List rewardItems = GenshinData.getRewardDataMap().get(rewardId).getRewardItemList();
+ List items = new LinkedList<>();
+ for (RewardItemData rewardItem : rewardItems) {
+ if (rewardItem != null) {
+ if (rewardItem.getItemId() != 0) {
+ items.add(new GenshinItem(rewardItem.getItemId(), rewardItem.getItemCount()));
+ }
+ }
+ }
+ session.getPlayer().getInventory().addItems(items);
+ session.getPlayer().sendPacket(new PacketItemAddHintNotify(items, ActionReason.PlayerUpgradeReward));
+ Set rewardedLevels = session.getPlayer().getRewardedLevels();
+ rewardedLevels.add(level);
+ session.getPlayer().setRewardedLevels(rewardedLevels);
+ }
+
+ session.send(new PacketTakePlayerLevelRewardRsp(level, rewardId));
+ }
+}
diff --git a/src/main/java/emu/grasscutter/server/packet/send/PacketAvatarFetterDataNotify.java b/src/main/java/emu/grasscutter/server/packet/send/PacketAvatarFetterDataNotify.java
new file mode 100644
index 000000000..68d3dbeac
--- /dev/null
+++ b/src/main/java/emu/grasscutter/server/packet/send/PacketAvatarFetterDataNotify.java
@@ -0,0 +1,49 @@
+package emu.grasscutter.server.packet.send;
+
+import emu.grasscutter.game.avatar.GenshinAvatar;
+import emu.grasscutter.game.props.FetterState;
+import emu.grasscutter.net.packet.GenshinPacket;
+import emu.grasscutter.net.packet.PacketOpcodes;
+import emu.grasscutter.net.proto.AvatarFetterDataNotifyOuterClass.AvatarFetterDataNotify;
+import emu.grasscutter.net.proto.AvatarFetterInfoOuterClass.AvatarFetterInfo;
+import emu.grasscutter.net.proto.FetterDataOuterClass.FetterData;
+
+public class PacketAvatarFetterDataNotify extends GenshinPacket {
+
+ public PacketAvatarFetterDataNotify(GenshinAvatar avatar) {
+ super(PacketOpcodes.AvatarFetterDataNotify);
+
+ int fetterLevel = avatar.getFetterLevel();
+
+ AvatarFetterInfo.Builder avatarFetter = AvatarFetterInfo.newBuilder()
+ .setExpLevel(avatar.getFetterLevel());
+
+ if (fetterLevel != 10) {
+ avatarFetter.setExpNumber(avatar.getFetterExp());
+ }
+
+ if (avatar.getFetterList() != null) {
+ for (int i = 0; i < avatar.getFetterList().size(); i++) {
+ avatarFetter.addFetterList(
+ FetterData.newBuilder()
+ .setFetterId(avatar.getFetterList().get(i))
+ .setFetterState(FetterState.FINISH.getValue())
+ );
+ }
+ }
+
+ int cardId = avatar.getNameCardId();
+
+ if (avatar.getPlayer().getNameCardList().contains(cardId)) {
+ avatarFetter.addRewardedFetterLevelList(10);
+ }
+
+ AvatarFetterInfo avatarFetterInfo = avatarFetter.build();
+
+ AvatarFetterDataNotify proto = AvatarFetterDataNotify.newBuilder()
+ .putFetterInfoMap(avatar.getGuid(), avatarFetterInfo)
+ .build();
+
+ this.setData(proto);
+ }
+}
diff --git a/src/main/java/emu/grasscutter/server/packet/send/PacketAvatarFetterLevelRewardRsp.java b/src/main/java/emu/grasscutter/server/packet/send/PacketAvatarFetterLevelRewardRsp.java
new file mode 100644
index 000000000..e9d4895ff
--- /dev/null
+++ b/src/main/java/emu/grasscutter/server/packet/send/PacketAvatarFetterLevelRewardRsp.java
@@ -0,0 +1,35 @@
+package emu.grasscutter.server.packet.send;
+
+import emu.grasscutter.net.packet.GenshinPacket;
+import emu.grasscutter.net.packet.PacketOpcodes;
+import emu.grasscutter.net.proto.AvatarFetterLevelRewardRspOuterClass.AvatarFetterLevelRewardRsp;
+
+public class PacketAvatarFetterLevelRewardRsp extends GenshinPacket {
+
+ public PacketAvatarFetterLevelRewardRsp(long guid, int fetterLevel, int rewardId) {
+ super(PacketOpcodes.AvatarFetterLevelRewardRsp);
+
+ AvatarFetterLevelRewardRsp proto = AvatarFetterLevelRewardRsp.newBuilder()
+ .setAvatarGuid(guid)
+ .setFetterLevel(fetterLevel)
+ .setRetcode(0)
+ .setRewardId(rewardId)
+ .build();
+
+ this.setData(proto);
+ }
+
+ public PacketAvatarFetterLevelRewardRsp(long guid, int fetterLevel) {
+ super(PacketOpcodes.AvatarFetterLevelRewardRsp);
+
+ AvatarFetterLevelRewardRsp proto = AvatarFetterLevelRewardRsp.newBuilder()
+ .setAvatarGuid(guid)
+ .setFetterLevel(fetterLevel)
+ .setRetcode(1)
+ .setRewardId(0)
+ .build();
+
+ this.setData(proto);
+ }
+
+}
diff --git a/src/main/java/emu/grasscutter/server/packet/send/PacketPlayerLevelRewardUpdateNotify.java b/src/main/java/emu/grasscutter/server/packet/send/PacketPlayerLevelRewardUpdateNotify.java
new file mode 100644
index 000000000..6a20e288b
--- /dev/null
+++ b/src/main/java/emu/grasscutter/server/packet/send/PacketPlayerLevelRewardUpdateNotify.java
@@ -0,0 +1,22 @@
+package emu.grasscutter.server.packet.send;
+
+import java.util.Set;
+
+import emu.grasscutter.net.packet.GenshinPacket;
+import emu.grasscutter.net.packet.PacketOpcodes;
+import emu.grasscutter.net.proto.PlayerLevelRewardUpdateNotifyOuterClass.PlayerLevelRewardUpdateNotify;
+
+public class PacketPlayerLevelRewardUpdateNotify extends GenshinPacket {
+
+ public PacketPlayerLevelRewardUpdateNotify(Set rewardedLevels) {
+ super(PacketOpcodes.PlayerLevelRewardUpdateNotify);
+
+ PlayerLevelRewardUpdateNotify.Builder proto = PlayerLevelRewardUpdateNotify.newBuilder();
+
+ for (Integer level : rewardedLevels) {
+ proto.addLevelList(level);
+ }
+
+ this.setData(proto.build());
+ }
+}
diff --git a/src/main/java/emu/grasscutter/server/packet/send/PacketSetPlayerBirthdayRsp.java b/src/main/java/emu/grasscutter/server/packet/send/PacketSetPlayerBirthdayRsp.java
index 9b73b6b13..8a343bf1d 100644
--- a/src/main/java/emu/grasscutter/server/packet/send/PacketSetPlayerBirthdayRsp.java
+++ b/src/main/java/emu/grasscutter/server/packet/send/PacketSetPlayerBirthdayRsp.java
@@ -1,20 +1,29 @@
package emu.grasscutter.server.packet.send;
-import emu.grasscutter.Grasscutter;
import emu.grasscutter.game.GenshinPlayer;
import emu.grasscutter.net.packet.GenshinPacket;
import emu.grasscutter.net.packet.PacketOpcodes;
import emu.grasscutter.net.proto.SetPlayerBirthdayRspOuterClass.SetPlayerBirthdayRsp;
-import emu.grasscutter.net.proto.BirthdayOuterClass.Birthday;
public class PacketSetPlayerBirthdayRsp extends GenshinPacket {
- public PacketSetPlayerBirthdayRsp(GenshinPlayer player) {
- super(PacketOpcodes.SetPlayerBirthdayRsp);
- SetPlayerBirthdayRsp proto = SetPlayerBirthdayRsp.newBuilder()
- .setBirth(player.getBirthday().toProto())
- .build();
+ public PacketSetPlayerBirthdayRsp(int retCode) {
+ super(PacketOpcodes.SetPlayerBirthdayRsp);
- this.setData(proto);
- }
+ SetPlayerBirthdayRsp proto = SetPlayerBirthdayRsp.newBuilder()
+ .setRetcode(retCode)
+ .build();
+
+ this.setData(proto);
+ }
+
+ public PacketSetPlayerBirthdayRsp(GenshinPlayer player) {
+ super(PacketOpcodes.SetPlayerBirthdayRsp);
+
+ SetPlayerBirthdayRsp proto = SetPlayerBirthdayRsp.newBuilder()
+ .setBirthday(player.getBirthday().toProto())
+ .build();
+
+ this.setData(proto);
+ }
}
diff --git a/src/main/java/emu/grasscutter/server/packet/send/PacketTakePlayerLevelRewardRsp.java b/src/main/java/emu/grasscutter/server/packet/send/PacketTakePlayerLevelRewardRsp.java
new file mode 100644
index 000000000..5a50d0806
--- /dev/null
+++ b/src/main/java/emu/grasscutter/server/packet/send/PacketTakePlayerLevelRewardRsp.java
@@ -0,0 +1,26 @@
+package emu.grasscutter.server.packet.send;
+
+import emu.grasscutter.net.packet.GenshinPacket;
+import emu.grasscutter.net.packet.PacketOpcodes;
+import emu.grasscutter.net.proto.TakePlayerLevelRewardRspOuterClass.TakePlayerLevelRewardRsp;
+
+public class PacketTakePlayerLevelRewardRsp extends GenshinPacket {
+
+ public PacketTakePlayerLevelRewardRsp(int level, int rewardId) {
+ super(PacketOpcodes.TakePlayerLevelRewardRsp);
+
+ int retcode = 0;
+
+ if (rewardId == 0) {
+ retcode = 1;
+ }
+
+ TakePlayerLevelRewardRsp proto = TakePlayerLevelRewardRsp.newBuilder()
+ .setLevel(level)
+ .setRewardId(rewardId)
+ .setRetcode(retcode)
+ .build();
+
+ this.setData(proto);
+ }
+}
diff --git a/start.cmd b/start.cmd
index 8c525dfc8..f44a5f00d 100644
--- a/start.cmd
+++ b/start.cmd
@@ -74,10 +74,11 @@ for /f "tokens=2*" %%a in ('reg query "HKCU\Software\Microsoft\Windows\CurrentVe
@rem TODO: External proxy when ORIG_PROXY_ENABLE == 0x1
echo set ws = createobject("wscript.shell") > "%temp%\proxy.vbs"
+
if not "%MITMDUMP_PATH%" == "" (
-echo ws.currentdirectory = "%MITMDUMP_PATH%" >> "%temp%\proxy.vbs"
+ echo ws.currentdirectory = "%MITMDUMP_PATH%" >> "%temp%\proxy.vbs"
)
-echo ws.run "cmd /c mitmdump.exe -s "^&chr(34)^&"%PROXY_SCRIPT_NAME%"^&chr(34)^&" -k",0 >> "%temp%\proxy.vbs"
+echo ws.run "cmd /c mitmdump.exe -s "^&chr(34)^&"%CUR_PATH%%PROXY_SCRIPT_NAME%"^&chr(34)^&" -k --allow-hosts "^&chr(34)^&".*\.yuanshen\.com|.*\.mihoyo\.com|.*\.hoyoverse\.com"^&chr(34),0 >> "%temp%\proxy.vbs"
"%temp%\proxy.vbs"
del /f /q "%temp%\proxy.vbs" >nul 2>nul
@@ -161,4 +162,4 @@ call :LOG [INFO] See you again :)
goto :EOF
:LOG
-echo [%time:~0,8%] %*
\ No newline at end of file
+echo [%time:~0,8%] %*