mirror of
https://github.com/Grasscutters/Grasscutter.git
synced 2025-01-10 04:12:54 +08:00
UPGRADE TO 1.1.0 POG
Merge `development` into `stable`
This commit is contained in:
commit
01b190bced
23
.github/ISSUE_TEMPLATE/a_issue_report.md
vendored
Normal file
23
.github/ISSUE_TEMPLATE/a_issue_report.md
vendored
Normal file
@ -0,0 +1,23 @@
|
||||
---
|
||||
name: Issues
|
||||
about: Create an issue if you need any help
|
||||
title: '[Issue] '
|
||||
labels: 'help wanted, question'
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
**Did you look for other closed issues that have the same problem?**
|
||||
<!--- It will be easier for us to solve your problem if there is less duplication of problems -->
|
||||
|
||||
**Describe the issue**
|
||||
<!--- A clear and concise description of what the issue is. -->
|
||||
|
||||
**Which branch did you use?**
|
||||
<!--- Stable branch / Development branch -->
|
||||
|
||||
**Screenshots**
|
||||
<!--- If applicable, add screenshots to help explain your problem. -->
|
||||
|
||||
**Additional context**
|
||||
<!--- Add any other context about the problem here. -->
|
||||
|
21
.github/ISSUE_TEMPLATE/b_bug_report.md
vendored
Normal file
21
.github/ISSUE_TEMPLATE/b_bug_report.md
vendored
Normal file
@ -0,0 +1,21 @@
|
||||
---
|
||||
name: Bug report
|
||||
about: Create a bug report to help us improve Grasscutter
|
||||
title: '[Bug] '
|
||||
labels: 'bug'
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
<!--- ONLY USE this form for bug reporting. If you need help or support, please USE issue report form instead. -->
|
||||
|
||||
**Describe the bug**
|
||||
<!--- A clear and concise description of what the bug is. -->
|
||||
|
||||
**Which branch did you use?**
|
||||
<!--- Stable branch / Development branch -->
|
||||
|
||||
**Screenshots**
|
||||
<!--- If applicable, add screenshots to help explain your problem. -->
|
||||
|
||||
**Additional context**
|
||||
<!--- Add any other context about the problem here. -->
|
20
.github/ISSUE_TEMPLATE/c_feature_request.md
vendored
Normal file
20
.github/ISSUE_TEMPLATE/c_feature_request.md
vendored
Normal file
@ -0,0 +1,20 @@
|
||||
---
|
||||
name: Feature request
|
||||
about: Suggest an idea for Grasscutter
|
||||
title: '[Feature Request] '
|
||||
labels: 'enhancement, suggestion'
|
||||
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. -->
|
6
.github/ISSUE_TEMPLATE/config.yml
vendored
Normal file
6
.github/ISSUE_TEMPLATE/config.yml
vendored
Normal file
@ -0,0 +1,6 @@
|
||||
blank_issues_enabled: false
|
||||
contact_links:
|
||||
- name: Grasscutter Discord
|
||||
url: https://discord.gg/T5vZU6UyeG
|
||||
about: For support, discuss and and other things with Grasscutter.
|
||||
|
22
.github/PULL_REQUEST_TEMPLATE.md
vendored
Normal file
22
.github/PULL_REQUEST_TEMPLATE.md
vendored
Normal file
@ -0,0 +1,22 @@
|
||||
## Description
|
||||
|
||||
Please carefully read the [Contributing note](https://github.com/Grasscutters/Grasscutter/blob/stable/CONTRIBUTING.md) and [Code of conduct](https://github.com/Grasscutters/Grasscutter/blob/development/CODE_OF_CONDUCT.md) before making any pull requests.
|
||||
And, **Do not make a pull request to merge into stable unless it is a hotfix. Use the development branch instead.**
|
||||
## Issues fixed by this PR
|
||||
|
||||
<!--- Put the links of issues that may be fixed by this PR here (if any). -->
|
||||
## Type of changes
|
||||
|
||||
<!--- Put an `x` in all the boxes that apply your changes. -->
|
||||
|
||||
- [ ] Bug fix
|
||||
- [ ] New feature
|
||||
- [ ] Enhancement
|
||||
- [ ] Documentation
|
||||
|
||||
## Checklist:
|
||||
|
||||
- [ ] My code follows the style guidelines of this project
|
||||
- [ ] My pull request is unique and no other pull requests have been opened for these changes
|
||||
- [ ] I have read the [Contributing note](https://github.com/Grasscutters/Grasscutter/blob/stable/CONTRIBUTING.md) and [Code of conduct](https://github.com/Grasscutters/Grasscutter/blob/development/CODE_OF_CONDUCT.md)
|
||||
- [ ] I am responsible for any copyright issues with my code if it occurs in the future.
|
20
.github/workflows/build.yml
vendored
20
.github/workflows/build.yml
vendored
@ -1,11 +1,22 @@
|
||||
name: "Build"
|
||||
on:
|
||||
workflow_dispatch: ~
|
||||
push:
|
||||
paths:
|
||||
- "**.java"
|
||||
branches:
|
||||
- "stable"
|
||||
- "development"
|
||||
pull_request:
|
||||
paths:
|
||||
- "**.java"
|
||||
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 +24,11 @@ jobs:
|
||||
uses: actions/setup-java@v3
|
||||
with:
|
||||
distribution: temurin
|
||||
java-version: '8'
|
||||
java-version: '17'
|
||||
- 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
|
||||
|
||||
path: grasscutter-*-dev.jar
|
||||
|
25
.gitignore
vendored
25
.gitignore
vendored
@ -30,6 +30,9 @@ hs_err_pid*
|
||||
build/
|
||||
out/
|
||||
|
||||
# Ignore Gradle properties
|
||||
gradle.properties
|
||||
|
||||
# Eclipse
|
||||
.project
|
||||
.classpath
|
||||
@ -45,14 +48,24 @@ tmp/
|
||||
.loadpath
|
||||
.recommenders
|
||||
|
||||
# VSCode
|
||||
.vscode
|
||||
|
||||
# Grasscutter
|
||||
resources/*
|
||||
resources/
|
||||
logs/
|
||||
plugins/
|
||||
data/AbilityEmbryos.json
|
||||
data/OpenConfig.json
|
||||
proto/auto/
|
||||
proto/protoc.exe
|
||||
GM Handbook.txt
|
||||
config.json
|
||||
mitmdump.exe
|
||||
grasscutter.jar
|
||||
config.json
|
||||
mitmdump.exe
|
||||
*.jar
|
||||
!lib/*.jar
|
||||
mongod.exe
|
||||
/src/generated/
|
||||
/*.sh
|
||||
language/
|
||||
languages/
|
||||
gacha-mapping.js
|
||||
data/gacha_mappings.js
|
||||
|
3
.gitmodules
vendored
3
.gitmodules
vendored
@ -1,3 +0,0 @@
|
||||
[submodule "Grasscutter-Protos"]
|
||||
path = Grasscutter-Protos
|
||||
url = https://github.com/Melledy/Grasscutter-Protos
|
@ -1 +0,0 @@
|
||||
Subproject commit 0537e9cc4c7856a7c6f88bbbaa908a80c4ee677e
|
178
README.md
178
README.md
@ -1,79 +1,159 @@
|
||||
# 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)
|
||||
<div align="center"><img alt="Documention" src="https://img.shields.io/badge/Wiki-Grasscutter-blue?style=for-the-badge&link=https://github.com/Grasscutters/Grasscutter/wiki&link=https://github.com/Grasscutters/Grasscutter/wiki"> <img alt="GitHub release (latest by date)" src="https://img.shields.io/github/v/release/Grasscutters/Grasscutter?logo=java&style=for-the-badge"> <img alt="GitHub" src="https://img.shields.io/github/license/Grasscutters/Grasscutter?style=for-the-badge"> <img alt="GitHub last commit" src="https://img.shields.io/github/last-commit/Grasscutters/Grasscutter?style=for-the-badge"> <img alt="GitHub Workflow Status" src="https://img.shields.io/github/workflow/status/Grasscutters/Grasscutter/Build?logo=github&style=for-the-badge"></div>
|
||||
|
||||
<div align="center"><a href="https://discord.gg/T5vZU6UyeG"><img alt="Discord - Grasscutter" src="https://img.shields.io/discord/965284035985305680?label=Discord&logo=discord&style=for-the-badge"></a></div>
|
||||
|
||||
EN | [中文](README_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* works
|
||||
* 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
|
||||
* JDK-8u202 ([mirror link](https://mirrors.huaweicloud.com/java/jdk/8u202-b08/) since Oracle required an account to download old builds)
|
||||
* Mongodb (recommended 4.0+)
|
||||
## Quick setup guide
|
||||
|
||||
**Note:** For support please join our [Discord](https://discord.gg/T5vZU6UyeG).
|
||||
|
||||
### Requirements
|
||||
|
||||
* Java SE - 17 ([link](https://www.oracle.com/java/technologies/javase/jdk17-archive-downloads.html))
|
||||
|
||||
**Note:** If you just want to **run it**, then **jre** only 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 updated from an older version, delete `config.json` to regenerate it.
|
||||
|
||||
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 move your `BinOutput` and `ExcelBinOutput` folders there *(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 - 17
|
||||
- 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
|
||||
You may want to use this command (`java -jar grasscutter.jar -gachamap`) to generate a mapping file for the gacha record subsystem. The file will be generated to `GRASSCUTTER_RESOURCE/gcstatic` folder. Otherwise you may only see number IDs in the gacha record page.
|
||||
|
||||
`pos` - Gets your current coordinate.
|
||||
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`
|
||||
|
||||
`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 <create\|delete> \<username> [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 \<message> | server.broadcast | Both side | Sends a message to all the players. | b |
|
||||
| coop | coop \<playerId> \<target playerId> | server.coop | Both side | Forces someone to join the world of others. | |
|
||||
| changescene | changescene \<scene id> | player.changescene | Client only | Switch scenes by scene ID. | scene |
|
||||
| clear | clear <all\|wp\|art\|mat> [UID] | player.clearinv | Client only | Deletes all unequipped and unlocked level 0 artifacts(art)/weapons(wp)/material(all) or all, including 5-star rarity ones from your inventory. | clear |
|
||||
| drop | drop <itemID\|itemName> [amount] | server.drop | Client only | Drops an item around you. | `d` `dropitem` |
|
||||
| enterdungeon | enterdungeon \<dungeon id> | player.enterdungeon | Client only | Enter a dungeon by dungeon ID | |
|
||||
| give | give [player] <itemId\|itemName> [amount] [level] [finement] | player.give | Both side | Gives item(s) to you or the specified player. (finement option only weapon.) | `g` `item` `giveitem` |
|
||||
| givechar | givechar \<uid> \<avatarId> | player.givechar | Both side | Gives the player a specified character. | givec |
|
||||
| giveart | giveart [player] \<artifactId> \<mainPropId> [\<appendPropId>[,\<times>]]... [level] | player.giveart | Both side | Gives the player a specified artifact. | gart |
|
||||
| giveall | giveall [uid] [amount] | player.giveall | Both side | Gives all items. | givea |
|
||||
| godmode | godmode [uid] | player.godmode | Client only | Prevents you from taking damage. | |
|
||||
| heal | heal | player.heal | Client only | Heals 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 \<player> | server.kick | Both side | Kicks the specified player from the server. (WIP) | k |
|
||||
| killall | killall [playerUid] [sceneId] | server.killall | Both side | Kills all entities in the current scene or specified scene of the corresponding player. | |
|
||||
| list | list | | Both side | Lists online players. | |
|
||||
| permission | permission <add\|remove> \<UID> \<permission> | * | Both side | Grants or removes a permission for a user. | |
|
||||
| position | position | | Client only | Sends your current coordinates. | pos |
|
||||
| reload | reload | server.reload | Both side | Reloads the server config | |
|
||||
| resetconst | resetconst [all] | player.resetconstellation | Client only | Resets the constellation level on your currently selected character, will need to relog after using the command to see any changes. | resetconstellation |
|
||||
| restart | | | Both side | Restarts the current session | |
|
||||
| say | say \<player> \<message> | server.sendmessage | Both side | Sends a message to a player as the server | `sendservmsg` `sendservermessage` `sendmessage` |
|
||||
| setfetterlevel | setfetterlevel \<level> | player.setfetterlevel | Client only | Sets the friendship level for your currently selected character | setfetterlvl |
|
||||
| setstats | setstats \<stat> \<value> | player.setstats | Client only | Sets a stat for your currently selected character | stats |
|
||||
| setworldlevel | setworldlevel \<level> | player.setworldlevel | Client only | Sets your world level (Relog to see proper effects) | setworldlvl |
|
||||
| spawn | spawn \<entityId> [amount] [level(monster only)] | server.spawn | Client only | Spawns some entities around you | |
|
||||
| stop | stop | server.stop | Both side | Stops the server | |
|
||||
| talent | talent \<talentID> \<value> | player.settalent | Client only | Sets talent level for your currently selected character | |
|
||||
| teleport | teleport [@playerUid] \<x> \<y> \<z> [sceneId] | player.teleport | Both side | Change the player's position. | tp |
|
||||
| tpall | | player.tpall | Client only | Teleports all players in your world to your position | |
|
||||
| weather | weather \<weatherID> \<climateID> | 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.
|
||||
|
||||
- Teleporting
|
||||
- When you want to teleport to somewhere, use the in-game marking function on Map.
|
||||
- Mark a point on the map using the fish hook marking (the last one.)
|
||||
- (Optional) rename the map marker to a number to override the default Y coordinate (height, default 300.)
|
||||
- Confirm and close the map.
|
||||
- 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
|
||||
* Startup sequence: Mongodb > Grasscutter > Proxy daemon (mitmdump, fiddler, etc.) > Client
|
||||
|
||||
* If compiling wasn't successful, please check your JDK installation (JDK 17 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.) > Game
|
||||
|
159
README_zh-CN.md
Normal file
159
README_zh-CN.md
Normal file
@ -0,0 +1,159 @@
|
||||
![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)
|
||||
<div align="center"><img alt="Documention" src="https://img.shields.io/badge/Wiki-Grasscutter-blue?style=for-the-badge&link=https://github.com/Grasscutters/Grasscutter/wiki&link=https://github.com/Grasscutters/Grasscutter/wiki"> <img alt="GitHub release (latest by date)" src="https://img.shields.io/github/v/release/Grasscutters/Grasscutter?logo=java&style=for-the-badge"> <img alt="GitHub" src="https://img.shields.io/github/license/Grasscutters/Grasscutter?style=for-the-badge"> <img alt="GitHub last commit" src="https://img.shields.io/github/last-commit/Grasscutters/Grasscutter?style=for-the-badge"> <img alt="GitHub Workflow Status" src="https://img.shields.io/github/workflow/status/Grasscutters/Grasscutter/Build?logo=github&style=for-the-badge"></div>
|
||||
|
||||
<div align="center"><a href="https://discord.gg/T5vZU6UyeG"><img alt="Discord - Grasscutter" src="https://img.shields.io/discord/965284035985305680?label=Discord&logo=discord&style=for-the-badge"></a></div>
|
||||
|
||||
[EN](README.md) | 中文
|
||||
|
||||
**注意:** 我们一直欢迎您成为该项目的贡献者。在添加您的代码之前,请仔细阅读我们的 [代码规范](https://github.com/Grasscutters/Grasscutter/blob/stable/CONTRIBUTING.md).
|
||||
|
||||
## 当前特性
|
||||
|
||||
* 登录
|
||||
* 战斗
|
||||
* 好友列表
|
||||
* 传送系统
|
||||
* 祈愿系统
|
||||
* 从控制台生成魔物
|
||||
* 多人游戏 *部分* 可用
|
||||
* 物品栏相关 (接收物品/角色, 升级角色/武器等)
|
||||
|
||||
## 快速设置指南
|
||||
|
||||
**附:** 加入我们的 [Discord](https://discord.gg/T5vZU6UyeG) 获取更多帮助!
|
||||
|
||||
### 环境需求
|
||||
|
||||
* Java SE - 17 (当您没有Oracle账户,可以使用[镜像](https://mirrors.tuna.tsinghua.edu.cn/Adoptium/17/jdk/))
|
||||
|
||||
**注:** 如果您仅仅想要简单地**运行服务端**, 那么使用 **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 - 17
|
||||
- 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等
|
||||
|
||||
你可能需要在终端中运行 `java -jar grasscutter.jar -gachamap` 来使得祈愿历史记录系统正常显示物品信息。 这个命令生成一个配置文件到如下文件夹:`GRASSCUTTER_RESOURCE/gcstatic`。 不执行此命令,您的祈愿历史记录中将只会显示数字ID而非物品名称。(目前仅支持自动生成英文记录信息)
|
||||
|
||||
在每个玩家的朋友列表中都有一个名为“Server”的虚拟用户,你可以通过发送消息来使用命令。命令也适用于其他聊天室,例如私人/团队聊天。
|
||||
要在游戏中使用命令,需要添加 `/` 或 `!` 前缀,如 `/pos`
|
||||
|
||||
| 命令 | 用法 | 权限节点 | 可用性 | 注释 | 别名 |
|
||||
| -------------- | -------------------------------------------- | ------------------------- | -------- | ------------------------------------------ | ----------------------------------------------- |
|
||||
| account | account <create\|delete> <用户名> [uid] | | 仅服务端 | 通过指定用户名和uid增删账户 | |
|
||||
| broadcast | broadcast <消息内容> | server.broadcast | 均可使用 | 给所有玩家发送公告 | b |
|
||||
| coop | coop \<uid> <目标uid> | server.coop | 均可使用 | 强制某位玩家进入指定玩家的多人世界 | |
|
||||
| changescene | changescene <场景ID> | player.changescene | 仅客户端 | 切换到指定场景 | scene |
|
||||
| clear | clear <all\|wp\|art\|mat> [UID] | player.clearinv | 仅客户端 | 删除所有未装备及未解锁的圣遗物(art)或武器(wp)或材料(mat)或者所有(all),包括五星 | clear |
|
||||
| drop | drop <物品ID\|物品名称> [数量] | server.drop | 仅客户端 | 在指定玩家周围掉落指定物品 | `d` `dropitem` |
|
||||
| enterdungeon | enterdungeon <地牢ID> | player.enterdungeon | 仅客户端 | 进入某个地牢 | |
|
||||
| give | give [uid] <物品ID\|物品名称> [数量] [等级] [精炼等级] | player.give | 均可使用 | 给予指定玩家一定数量及等级的物品 (精炼等级仅适用于武器) | `g` `item` `giveitem` |
|
||||
| givechar | givechar \<uid> <角色ID> [等级] | player.givechar | 均可使用 | 给予指定玩家对应角色 | givec |
|
||||
| giveart | giveart [uid] \<圣遗物ID> \<主属性ID> [\<副属性ID>[,<次数>]]... [等级] | player.giveart | 均可使用 | 给予玩家指定属性的圣遗物 | gart |
|
||||
| giveall | giveall [uid] [数量] | player.giveall | 均可使用 | 给予指定玩家全部物品 | givea |
|
||||
| godmode | godmode [uid] | player.godmode | 仅客户端 | 保护你不受到任何伤害(依然会被击退) | |
|
||||
| heal | heal | player.heal | 仅客户端 | 治疗队伍中所有角色 | h |
|
||||
| help | help [命令] | | 均可使用 | 显示帮助或展示指定命令的帮助 | |
|
||||
| kick | kick \<uid> | server.kick | 均可使用 | 从服务器中踢出指定玩家 (WIP) | k |
|
||||
| killall | killall [uid] [场景ID] | server.killall | 均可使用 | 杀死指定玩家世界中所在或指定场景的全部生物 | |
|
||||
| list | list | | 均可使用 | 列出在线玩家 | |
|
||||
| permission | permission <add\|remove> <UID> <权限节点> | * | 均可使用 | 添加或移除玩家的权限 | |
|
||||
| position | position | | 仅客户端 | 获取当前坐标 | pos |
|
||||
| reload | reload | server.reload | 均可使用 | 重载服务器配置 | |
|
||||
| resetconst | resetconst [all] | player.resetconstellation | 仅客户端 | 重置当前角色的命座,重新登录即可生效 | resetconstellation |
|
||||
| restart | restart | | 均可使用 | 重启服务端 | |
|
||||
| say | say \<uid> <消息> | server.sendmessage | 均可使用 | 作为服务器发送消息给玩家 | `sendservmsg` `sendservermessage` `sendmessage` |
|
||||
| setfetterlevel | setfetterlevel <好感等级> | player.setfetterlevel | 仅客户端 | 设置当前角色的好感等级 | `setfetterlvl` `setfriendship` |
|
||||
| setstats | setstats <属性> <数值> | player.setstats | 仅客户端 | 直接修改当前角色的面板 | stats |
|
||||
| setworldlevel | setworldlevel <世界等级> | player.setworldlevel | 仅客户端 | 设置世界等级(重新登录即可生效) | setworldlvl |
|
||||
| spawn | spawn <实体ID> [数量] [等级] | server.spawn | 仅客户端 | 在你周围生成实体 | |
|
||||
| stop | stop | server.stop | 均可使用 | 停止服务器 | |
|
||||
| talent | talent <天赋ID> <等级> | player.settalent | 仅客户端 | 设置当前角色的天赋等级 | |
|
||||
| teleport | teleport [@playerUid] \<x> \<y> \<z> [sceneId] | player.teleport | 均可使用 | 传送玩家到指定坐标 | tp |
|
||||
| tpall | | player.tpall | 仅客户端 | 传送多人世界中所有的玩家到自身地点 | |
|
||||
| weather | weather <天气ID> <气候ID> | player.weather | 仅客户端 | 改变天气 | w |
|
||||
|
||||
### 额外功能
|
||||
|
||||
当你想传送到某个地点, 只需要在地图中创建标记, 关闭地图后即可到达目标地点上空
|
||||
- 传送
|
||||
- 当你想传送到某个地点时,可以使用游戏里的地图标记功能。
|
||||
- 用鱼钩(最后一个图标)在地图上标记一个点位。
|
||||
- (可选) 将标记名称改为数字,即可修改传送位置的Y坐标(高度,缺省值是300)。
|
||||
- 确认添加标记,并关闭地图。
|
||||
- 你会看到你的角色从你选定点位的正上方高空落下。
|
||||
|
||||
# 快速排除问题
|
||||
|
||||
* 如果编译未能成功,请检查您的jdk安装 (JDK 17并确认jdk处于环境变量`PATH`中
|
||||
* 我的客户端无法登录/连接, 4206, 其它... - 大部分情况下这是因为您的代理存在问题.如果使用Fiddler请确认Fiddler监听端口不是`8888`
|
||||
* 启动顺序: MongoDB > Grasscutter > 代理程序 (mitmdump, fiddler等.) > 客户端
|
198
build.gradle
198
build.gradle
@ -6,38 +6,90 @@
|
||||
* User Manual available at https://docs.gradle.org/5.6.3/userguide/tutorial_java_projects.html
|
||||
*/
|
||||
|
||||
buildscript {
|
||||
repositories {
|
||||
maven { url "https://plugins.gradle.org/m2/" }
|
||||
}
|
||||
dependencies {
|
||||
classpath 'com.google.protobuf:protobuf-gradle-plugin:0.8.18'
|
||||
}
|
||||
}
|
||||
|
||||
plugins {
|
||||
// Apply the application plugin to add support for building a CLI application
|
||||
id 'application'
|
||||
|
||||
// Apply the java plugin to add support for Java
|
||||
id 'java'
|
||||
|
||||
// Apply the application plugin to add support for building a CLI application
|
||||
id 'application'
|
||||
// Apply the protobuf auto generator
|
||||
id 'com.google.protobuf' version "0.8.18"
|
||||
|
||||
// Eclipse Support
|
||||
id 'eclipse'
|
||||
|
||||
// IntelliJ Support
|
||||
id 'idea'
|
||||
|
||||
// Maven
|
||||
id 'maven-publish'
|
||||
id 'signing'
|
||||
}
|
||||
|
||||
sourceCompatibility = 1.8
|
||||
targetCompatibility = 1.8
|
||||
compileJava.options.encoding = "UTF-8"
|
||||
compileTestJava.options.encoding = "UTF-8"
|
||||
|
||||
sourceCompatibility = JavaVersion.VERSION_17
|
||||
targetCompatibility = JavaVersion.VERSION_17
|
||||
|
||||
group = 'xyz.grasscutters'
|
||||
version = '1.1.0'
|
||||
|
||||
sourceCompatibility = 17
|
||||
targetCompatibility = 17
|
||||
|
||||
java {
|
||||
withJavadocJar()
|
||||
withSourcesJar()
|
||||
}
|
||||
|
||||
repositories {
|
||||
mavenCentral()
|
||||
jcenter()
|
||||
}
|
||||
|
||||
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: 'com.google.code.gson', name: 'gson', version: '2.8.8'
|
||||
implementation group: 'com.google.protobuf', name: 'protobuf-java', version: '3.18.1'
|
||||
|
||||
implementation group: 'org.reflections', name: 'reflections', version: '0.10.2'
|
||||
implementation group: 'org.slf4j', name: 'slf4j-api', version: '1.7.32'
|
||||
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: 'dev.morphia.morphia', name: 'core', version: '1.6.1'
|
||||
implementation group: 'org.jline', name: 'jline', version: '3.21.0'
|
||||
implementation group: 'org.jline', name: 'jline-terminal-jna', version: '3.21.0'
|
||||
implementation group: 'net.java.dev.jna', name: 'jna', version: '5.10.0'
|
||||
|
||||
implementation group: '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.2'
|
||||
|
||||
implementation group: 'org.reflections', name: 'reflections', version: '0.10.2'
|
||||
|
||||
implementation group: 'dev.morphia.morphia', name: 'morphia-core', version: '2.2.6'
|
||||
|
||||
implementation group: 'org.greenrobot', name: 'eventbus-java', version: '3.3.1'
|
||||
implementation group: 'org.danilopianini', name: 'java-quadtree', version: '0.1.9'
|
||||
|
||||
implementation group: 'org.quartz-scheduler', name: 'quartz', version: '2.3.2'
|
||||
implementation group: 'org.quartz-scheduler', name: 'quartz-jobs', version: '2.3.2'
|
||||
|
||||
implementation group: 'org.luaj', name: 'luaj-jse', version: '3.0.1'
|
||||
|
||||
protobuf files('proto/')
|
||||
}
|
||||
|
||||
configurations.all {
|
||||
exclude group: 'org.slf4j', module: 'slf4j'
|
||||
}
|
||||
|
||||
application {
|
||||
@ -59,9 +111,121 @@ jar {
|
||||
duplicatesStrategy = DuplicatesStrategy.INCLUDE
|
||||
|
||||
from('src/main/java') {
|
||||
include '*.xml'
|
||||
}
|
||||
include '*.xml'
|
||||
}
|
||||
|
||||
destinationDir = file(".")
|
||||
}
|
||||
|
||||
publishing {
|
||||
publications {
|
||||
mavenJava(MavenPublication) {
|
||||
artifactId = 'grasscutter'
|
||||
from components.java
|
||||
versionMapping {
|
||||
usage('java-api') {
|
||||
fromResolutionOf('runtimeClasspath')
|
||||
}
|
||||
usage('java-runtime') {
|
||||
fromResolutionResult()
|
||||
}
|
||||
}
|
||||
pom {
|
||||
name = 'Grasscutter'
|
||||
description = 'A server software reimplementation for an anime game.'
|
||||
url = 'https://github.com/Grasscutters/Grasscutter'
|
||||
licenses {
|
||||
license {
|
||||
name = 'The Apache License, Version 2.0'
|
||||
url = 'http://www.apache.org/licenses/LICENSE-2.0.txt'
|
||||
}
|
||||
}
|
||||
developers {
|
||||
developer {
|
||||
id = 'meledy'
|
||||
name = 'Meledy'
|
||||
email = 'meledy@xigam.tech' // not a real email kek
|
||||
}
|
||||
developer {
|
||||
id = 'magix'
|
||||
name = 'Magix'
|
||||
email = 'magix@xigam.tech'
|
||||
}
|
||||
}
|
||||
scm {
|
||||
connection = 'scm:git:git@github.com:Grasscutters/Grasscutter.git'
|
||||
developerConnection = 'scm:git:ssh://github.com:Grasscutters/Grasscutter.git'
|
||||
url = 'https://github.com/Grasscutters/Grasscutter'
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
repositories {
|
||||
maven {
|
||||
// change URLs to point to your repos, e.g. http://my.org/repo
|
||||
def releasesRepoUrl = 'https://s01.oss.sonatype.org/service/local/staging/deploy/maven2/'
|
||||
def snapshotsRepoUrl = 'https://s01.oss.sonatype.org/content/repositories/snapshots/'
|
||||
url = version.endsWith('SNAPSHOT') ? snapshotsRepoUrl : releasesRepoUrl
|
||||
|
||||
name = 'sonatype'
|
||||
credentials(PasswordCredentials)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
clean {
|
||||
delete protobuf.generatedFilesBaseDir
|
||||
}
|
||||
|
||||
protobuf {
|
||||
protoc {
|
||||
// The artifact spec for the Protobuf Compiler
|
||||
artifact = 'com.google.protobuf:protoc:3.18.1'
|
||||
}
|
||||
// generatedFilesBaseDir = "$projectDir/src/main/java/emu/grasscutter/net/proto/"
|
||||
generatedFilesBaseDir = "$projectDir/src/generated/"
|
||||
}
|
||||
|
||||
sourceSets {
|
||||
main {
|
||||
proto {
|
||||
// In addition to the default 'src/main/proto'
|
||||
srcDir 'src/generated'
|
||||
}
|
||||
java {
|
||||
srcDir 'src/java'
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
idea {
|
||||
module {
|
||||
// proto files and generated Java files are automatically added as
|
||||
// source dirs.
|
||||
// If you have additional sources, add them here:
|
||||
sourceDirs += file("/proto/");
|
||||
}
|
||||
}
|
||||
|
||||
eclipse {
|
||||
classpath {
|
||||
file.whenMerged { cp ->
|
||||
cp.entries.add( new org.gradle.plugins.ide.eclipse.model.SourceFolder('src/generated/main/java', null) )
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
signing {
|
||||
sign publishing.publications.mavenJava
|
||||
}
|
||||
|
||||
javadoc {
|
||||
options.encoding = 'UTF-8'
|
||||
if(JavaVersion.current().isJava9Compatible()) {
|
||||
options.addBooleanOption('html5', true)
|
||||
}
|
||||
}
|
||||
|
||||
processResources {
|
||||
dependsOn "generateProto"
|
||||
}
|
||||
|
199808
data/Drop.json
Normal file
199808
data/Drop.json
Normal file
File diff suppressed because it is too large
Load Diff
1072
data/ExpeditionReward.json
Normal file
1072
data/ExpeditionReward.json
Normal file
File diff suppressed because it is too large
Load Diff
29
data/GameAnnouncement.json
Normal file
29
data/GameAnnouncement.json
Normal file
@ -0,0 +1,29 @@
|
||||
{
|
||||
"list": [
|
||||
{
|
||||
"ann_id": 1,
|
||||
"title": "<b>Welcome to Grasscutter!</b>",
|
||||
"subtitle": "<b>Welcome</b>",
|
||||
"banner": "https://uploadstatic-sea.mihoyo.com/announcement/2020/09/17/f4aa42d505822805eebf4a55d72a78d8_2755691727027973637.jpg",
|
||||
"content": "Hi there!<br>First of all, welcome to Grasscutter. If you have any issues, please let us know so that Lawnmower can help you! Check out our:<br><div><p style=\"white-space: pre-wrap;\"><strong>¡þDiscord¡þ</strong></p><p style=\"white-space: pre-wrap;\"><a href=\"https://discord.gg/T5vZU6UyeG\">https://discord.gg/T5vZU6UyeG</a></p><p style=\"white-space: pre-wrap;\"><strong>¡þGitHub¡þ</strong></p><p style=\"white-space: pre-wrap;\"><a href=\"https://github.com/Grasscutters/Grasscutter\">https://github.com/Grasscutters/Grasscutter</a></p></div>",
|
||||
"lang": "es-es"
|
||||
},
|
||||
{
|
||||
"ann_id": 2,
|
||||
"title": "<b>How to use announcements</b>",
|
||||
"subtitle": "<b>How to use</b>",
|
||||
"banner": "https://uploadstatic-sea.mihoyo.com/announcement/2020/09/17/f4aa42d505822805eebf4a55d72a78d8_2755691727027973637.jpg",
|
||||
"content": "<strong>Tips<br></strong>>How to use announcements<br><br>>Announcement content can use HTML<br><br>>The specific content of the announcement is stored in the program directory<code>data/GameAnnouncement.json</code>, while<code>GameAnnouncementList.json</code> stores the announcement list data<br><br><strong>How to use</strong><br>>In <code>GameAnnouncement</code><table><thead><thead><tr><th>Parameters</th><th>Description</th></thead></thead><thbody><thead><tr><th>ann_Id</th><th>Announcement unique id</th></thead><thead><tr><th>title</th><th>Show at the top of the content</th></thead><thead><tr><th>subtitle</th><th>title shown on the left</th></thead><thead><tr><th>banner</th><th>Display between content and title</th></thead><thead><tr><th>content</th><th>as u see</th></thead><thead><tr><th>lang</th><th>display language</th></thead><thead><tr><th>total</th><th>Announcement quantity</th></thead></thbody></table><br><br>>In <code>GameAnnouncementList</code><br>If you want to add an annouement, please add the list data in the announcement type corresponding to GameAnnouncementList, and finally add the announcement content in GameAnnouncement",
|
||||
"lang": "es-es"
|
||||
},
|
||||
{
|
||||
"ann_id": 3,
|
||||
"title": "<b>ÕâÊǻ¹«¸æ--This is the event announcement</b>",
|
||||
"subtitle": "<b>Welcome</b>",
|
||||
"banner":"https://uploadstatic-sea.mihoyo.com/announcement/2020/09/22/7d85f19b152d218e73224d7c138a0fd0_5818585260283672899.jpg",
|
||||
"content": "Welcome",
|
||||
"lang": "es-es"
|
||||
}
|
||||
],
|
||||
"total": 3
|
||||
}
|
119
data/GameAnnouncementList.json
Normal file
119
data/GameAnnouncementList.json
Normal file
@ -0,0 +1,119 @@
|
||||
{
|
||||
"t": "System.currentTimeMillis()",
|
||||
"list": [
|
||||
{
|
||||
"list": [
|
||||
{
|
||||
"ann_id": 1,
|
||||
"title": "<b>Welcome to Grasscutter!</b>",
|
||||
"subtitle": "<b>Welcome</b>",
|
||||
"banner": "https://uploadstatic-sea.mihoyo.com/announcement/2020/09/22/7d85f19b152d218e73224d7c138a0fd0_5818585260283672899.jpg",
|
||||
"content": "",
|
||||
"type_label": "Juego",
|
||||
"tag_label": "1",
|
||||
"tag_icon": "https://uploadstatic-sea.mihoyo.com/announcement/2020/03/05/a2588f1a51faee9fa8dfe9aead649dd6_7237021399135895303.png",
|
||||
"login_alert": 1,
|
||||
"lang": "es-es",
|
||||
"start_time": "2020-09-25 04:05:30",
|
||||
"end_time": "2023-10-30 11:00:00",
|
||||
"type": 2,
|
||||
"remind": 0,
|
||||
"alert": 0,
|
||||
"tag_start_time": "2000-01-02 15:04:05",
|
||||
"tag_end_time": "2030-01-02 15:04:05",
|
||||
"remind_ver": 1,
|
||||
"has_content": true,
|
||||
"extra_remind": 0
|
||||
},
|
||||
{
|
||||
"ann_id": 2,
|
||||
"title": "<b>这是游戏公告 -- This is the game announcement</b>",
|
||||
"subtitle": "<b>This is the game announcement</b>",
|
||||
"banner": "https://uploadstatic-sea.mihoyo.com/announcement/2020/09/17/85b7163c95745a76d49b3d163d893592_6487108933004985049.jpg",
|
||||
"content": "",
|
||||
"type_label": "Juego",
|
||||
"tag_label": "1",
|
||||
"tag_icon": "https://uploadstatic-sea.mihoyo.com/announcement/2020/03/05/a2588f1a51faee9fa8dfe9aead649dd6_7237021399135895303.png",
|
||||
"login_alert": 1,
|
||||
"lang": "es-es",
|
||||
"start_time": "2020-09-25 15:12:09",
|
||||
"end_time": "2030-10-30 11:00:00",
|
||||
"type": 2,
|
||||
"remind": 0,
|
||||
"alert": 0,
|
||||
"tag_start_time": "2000-01-02 08:04:05",
|
||||
"tag_end_time": "2030-01-02 08:04:05",
|
||||
"remind_ver": 1,
|
||||
"has_content": true,
|
||||
"extra_remind": 0
|
||||
}
|
||||
],
|
||||
"type_id": 2,
|
||||
"type_label": "Juego"
|
||||
},
|
||||
{
|
||||
"list": [
|
||||
{
|
||||
"ann_id": 3,
|
||||
"title": "<b>这是活动公告--This is the event announcement</b>",
|
||||
"subtitle": "<b>Welcome</b>",
|
||||
"banner": "https://uploadstatic-sea.mihoyo.com/announcement/2020/09/22/7d85f19b152d218e73224d7c138a0fd0_5818585260283672899.jpg",
|
||||
"content": "",
|
||||
"type_label": "Eventos",
|
||||
"tag_label": "1",
|
||||
"tag_icon": "https://uploadstatic-sea.mihoyo.com/announcement/2020/03/05/a2588f1a51faee9fa8dfe9aead649dd6_7237021399135895303.png",
|
||||
"login_alert": 1,
|
||||
"lang": "es-es",
|
||||
"start_time": "2020-09-25 04:05:30",
|
||||
"end_time": "2022-05-02 00:51:00",
|
||||
"type": 2,
|
||||
"remind": 0,
|
||||
"alert": 0,
|
||||
"tag_start_time": "2000-01-02 15:04:05",
|
||||
"tag_end_time": "2022-05-02 00:51:00",
|
||||
"remind_ver": 1,
|
||||
"has_content": true,
|
||||
"extra_remind": 0
|
||||
}
|
||||
],
|
||||
"type_id": 1,
|
||||
"type_label": "Eventos"
|
||||
},
|
||||
{
|
||||
"list": [
|
||||
{}
|
||||
],
|
||||
"type_id": 3,
|
||||
"type_label": "Others"
|
||||
}
|
||||
],
|
||||
"total": 3,
|
||||
"type_list": [
|
||||
{
|
||||
"id": 2,
|
||||
"name": "游戏系统公告",
|
||||
"mi18n_name": "Juego"
|
||||
},
|
||||
{
|
||||
"id": 1,
|
||||
"name": "活动公告",
|
||||
"mi18n_name": "Eventos"
|
||||
},
|
||||
{
|
||||
"id": 3,
|
||||
"name": "其他",
|
||||
"mi18n_name": "Others"
|
||||
}
|
||||
],
|
||||
"alert": true,
|
||||
"alert_id": 2,
|
||||
"timezone": -5,
|
||||
"pic_list": [
|
||||
],
|
||||
"pic_total": 0,
|
||||
"pic_type_list": [
|
||||
],
|
||||
"pic_alert": false,
|
||||
"pic_alert_id": 0,
|
||||
"static_sign": ""
|
||||
}
|
54
data/Shop.json
Normal file
54
data/Shop.json
Normal file
@ -0,0 +1,54 @@
|
||||
[
|
||||
{
|
||||
"shopId": 1004,
|
||||
"items": [
|
||||
{
|
||||
"goodsId": 1004202,
|
||||
"goodsItem": {
|
||||
"Id": 202,
|
||||
"Count": 1000000
|
||||
},
|
||||
"scoin": 1,
|
||||
"buyLimit": 500,
|
||||
"beginTime": 1575129600,
|
||||
"endTime": 2051193600,
|
||||
"minLevel": 1,
|
||||
"maxLevel": 99,
|
||||
"costItemList": [
|
||||
{
|
||||
"Id": 223,
|
||||
"Count": 100
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"goodsId": 10048006,
|
||||
"goodsItem": {
|
||||
"Id": 108006,
|
||||
"Count": 20
|
||||
},
|
||||
"scoin": 100,
|
||||
"hcoin": 100,
|
||||
"mcoin": 100,
|
||||
"buyLimit": 50000,
|
||||
"beginTime": 1575129600,
|
||||
"endTime": 2051193600,
|
||||
"minLevel": 1,
|
||||
"maxLevel": 99
|
||||
},
|
||||
{
|
||||
"goodsId": 10048033,
|
||||
"goodsItem": {
|
||||
"Id": 108033,
|
||||
"Count": 20
|
||||
},
|
||||
"scoin": 1,
|
||||
"buyLimit": 50000,
|
||||
"beginTime": 1575129600,
|
||||
"endTime": 2051193600,
|
||||
"minLevel": 1,
|
||||
"maxLevel": 99
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
153
data/ShopChest.json
Normal file
153
data/ShopChest.json
Normal file
@ -0,0 +1,153 @@
|
||||
[
|
||||
{
|
||||
"itemId": 115019,
|
||||
"containsItem": [
|
||||
{
|
||||
"Id": 104002,
|
||||
"Count": 40
|
||||
},
|
||||
{
|
||||
"Id": 202,
|
||||
"Count": 30000
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"itemId": 115020,
|
||||
"containsItem": [
|
||||
{
|
||||
"Id": 104013,
|
||||
"Count": 25
|
||||
},
|
||||
{
|
||||
"Id": 202,
|
||||
"Count": 30000
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"itemId": 115021,
|
||||
"containsItem": [
|
||||
{
|
||||
"Id": 115013,
|
||||
"Count": 5
|
||||
},
|
||||
{
|
||||
"Id": 104003,
|
||||
"Count": 40
|
||||
},
|
||||
{
|
||||
"Id": 202,
|
||||
"Count": 120000
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"itemId": 115022,
|
||||
"containsItem": [
|
||||
{
|
||||
"Id": 115017,
|
||||
"Count": 25
|
||||
},
|
||||
{
|
||||
"Id": 202,
|
||||
"Count": 150000
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"itemId": 115023,
|
||||
"containsItem": [
|
||||
{
|
||||
"Id": 115025,
|
||||
"Count": 10
|
||||
},
|
||||
{
|
||||
"Id": 202,
|
||||
"Count": 60000
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"itemId": 115029,
|
||||
"containsItem": [
|
||||
{
|
||||
"Id": 104013,
|
||||
"Count": 100
|
||||
},
|
||||
{
|
||||
"Id": 202,
|
||||
"Count": 100000
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"itemId": 115030,
|
||||
"containsItem": [
|
||||
{
|
||||
"Id": 104003,
|
||||
"Count": 12
|
||||
},
|
||||
{
|
||||
"Id": 202,
|
||||
"Count": 10000
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"itemId": 115034,
|
||||
"containsItem": [
|
||||
{
|
||||
"Id": 115013,
|
||||
"Count": 6
|
||||
},
|
||||
{
|
||||
"Id": 202,
|
||||
"Count": 60000
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"itemId": 115032,
|
||||
"containsItem": [
|
||||
{
|
||||
"Id": 115024,
|
||||
"Count": 12
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"itemId": 115010,
|
||||
"containsItem": [
|
||||
{
|
||||
"Id": 104002,
|
||||
"Count": 80
|
||||
},
|
||||
{
|
||||
"Id": 104012,
|
||||
"Count": 40
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"itemId": 115011,
|
||||
"containsItem": [
|
||||
{
|
||||
"Id": 104003,
|
||||
"Count": 50
|
||||
},
|
||||
{
|
||||
"Id": 104013,
|
||||
"Count": 25
|
||||
},
|
||||
{
|
||||
"Id": 107009,
|
||||
"Count": 1
|
||||
},
|
||||
{
|
||||
"Id": 202,
|
||||
"Count": 50000
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
55
data/ShopChestBatchUse.json
Normal file
55
data/ShopChestBatchUse.json
Normal file
@ -0,0 +1,55 @@
|
||||
[
|
||||
{
|
||||
"itemId": 115017,
|
||||
"optionItem": [
|
||||
104302,
|
||||
104305,
|
||||
104308,
|
||||
104311,
|
||||
104314,
|
||||
104317,
|
||||
104321,
|
||||
104324,
|
||||
104327
|
||||
]
|
||||
},
|
||||
{
|
||||
"itemId": 115024,
|
||||
"optionItem": [
|
||||
114001,
|
||||
114005,
|
||||
114009,
|
||||
114013,
|
||||
114017,
|
||||
114021,
|
||||
114025,
|
||||
114029,
|
||||
114033
|
||||
]
|
||||
},
|
||||
{
|
||||
"itemId": 115013,
|
||||
"optionItem": [
|
||||
104112,
|
||||
104122,
|
||||
104142,
|
||||
104152,
|
||||
104162,
|
||||
104172
|
||||
]
|
||||
},
|
||||
{
|
||||
"itemId": 115025,
|
||||
"optionItem": [
|
||||
114002,
|
||||
114006,
|
||||
114010,
|
||||
114014,
|
||||
114018,
|
||||
114022,
|
||||
114026,
|
||||
114030,
|
||||
114034
|
||||
]
|
||||
}
|
||||
]
|
1
data/Spawns.json
Normal file
1
data/Spawns.json
Normal file
File diff suppressed because one or more lines are too long
227
data/gacha_records.html
Normal file
227
data/gacha_records.html
Normal file
@ -0,0 +1,227 @@
|
||||
<!doctype html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
|
||||
<link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Roboto:300,400&display=swap">
|
||||
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@4.6.1/dist/css/bootstrap.min.css">
|
||||
<style>
|
||||
body {
|
||||
background-color: #f0f0f0;
|
||||
}
|
||||
p {
|
||||
font-weight:300;
|
||||
}
|
||||
a,a:hover {
|
||||
text-decoration:none !important;
|
||||
color:#626976;
|
||||
}
|
||||
.content {
|
||||
padding:3rem 0;
|
||||
}
|
||||
.container {
|
||||
color:#626976;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
h2 {
|
||||
font-size:20px;
|
||||
}
|
||||
.custom-table {
|
||||
min-width:900px;
|
||||
}
|
||||
.custom-table thead tr,.custom-table thead th {
|
||||
padding-bottom:30px;
|
||||
color:#000;
|
||||
}
|
||||
.custom-table tbody th,.custom-table tbody td {
|
||||
color:#777;
|
||||
font-weight:400;
|
||||
padding-bottom:20px;
|
||||
padding-top:20px;
|
||||
font-weight:300;
|
||||
border:none;
|
||||
}
|
||||
.yellow {
|
||||
color: rgb(255, 162, 0);
|
||||
}
|
||||
.blue {
|
||||
color: rgb(75, 107, 251);
|
||||
}
|
||||
.purple {
|
||||
color: rgb(242, 40, 242);
|
||||
}
|
||||
</style>
|
||||
<title>Gacha Records</title>
|
||||
<script>
|
||||
// Debug entry
|
||||
// record = [
|
||||
// {"time": 10000341, "item": 1041},
|
||||
// {"time": 10000342, "item": 1032},
|
||||
// {"time": 10000343, "item": 1035},
|
||||
// ];
|
||||
// maxPage = 5;
|
||||
|
||||
// in production environment
|
||||
record = {{REPLACE_RECORD}};
|
||||
maxPage = {{REPLACE_MAXPAGE}};
|
||||
|
||||
// TODO: implement this mapper by yourself
|
||||
// I don't want to put real items' name here to avoid being DMCA'd
|
||||
mappings = {
|
||||
'en-us': {
|
||||
200: "Standard",
|
||||
301: "Event Avatar",
|
||||
302: "Event Weapon",
|
||||
1041 : ["M0n4", "blue"],
|
||||
1032 : ["B4nn477", "purple"],
|
||||
1035 : ["77", "yellow"]
|
||||
},
|
||||
'zh-cn': {
|
||||
// encoding issues here, maybe we should consider load mappings remotely
|
||||
// will display as "锟斤铐锟斤铐锟斤铐", lmao
|
||||
// 200: "常驻",
|
||||
// 301: "角色UP-1",
|
||||
// 302: "武器UP"
|
||||
200: "Standard",
|
||||
301: "Event Avatar",
|
||||
302: "Event Weapon",
|
||||
}
|
||||
};
|
||||
</script>
|
||||
<!-- This file could be generated automatically using `java -jar grasscutter.jar -gachamap` -->
|
||||
<!-- You can also modify the file manually to customize it -->
|
||||
<!-- Otherwise you may onle see number IDs in the gacha record -->
|
||||
<script type="text/javascript" src="/gacha/mappings"></script>
|
||||
<script>
|
||||
mappings['default'] = mappings['en-us']; // make en-us as default/fallback option
|
||||
</script>
|
||||
</head>
|
||||
<body>
|
||||
<div class="content">
|
||||
<div class="container">
|
||||
<h2 class="mb-5">Gacha Records</h2>
|
||||
<table id="container" class="table table-striped custom-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th scope="col">Date</th>
|
||||
<th scope="col">Item</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
</tbody>
|
||||
</table>
|
||||
<div class="navbar">
|
||||
<a href="" id="prev"><<<</a>
|
||||
<span id="curpage">1</span>
|
||||
<a href="" id="next">>>></a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<footer>
|
||||
<div class="copyright">
|
||||
<div class="container">
|
||||
<div class="row">
|
||||
<div class="col-md-6">
|
||||
<span>
|
||||
Template by BecodReyes. All rights reserved.
|
||||
</span>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<ul style="float:right">
|
||||
<li class="list-inline-item">
|
||||
<a href="https://github.com/Grasscutters/Grasscutter">Github</a>
|
||||
</li>
|
||||
<li class="list-inline-item">·</li>
|
||||
<li class="list-inline-item">
|
||||
<a href="https://github.com/Grasscutters/Grasscutter/blob/stable/LICENSE">License</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</footer>
|
||||
|
||||
<script>
|
||||
var lang = new window.URLSearchParams(window.location.search).get("lang");
|
||||
function itemMapper(itemID) {
|
||||
if (mappings[lang] != null && mappings[lang][itemID] != null) {
|
||||
var entry = mappings[lang][itemID];
|
||||
if (entry){
|
||||
return "<span class='" + entry[1] + "'>" + entry[0] + "</span>";
|
||||
}
|
||||
} else {
|
||||
if (mappings['default'][itemID] != null) {
|
||||
var entry = mappings['default'][itemID];
|
||||
if (entry){
|
||||
return "<span class='" + entry[1] + "'>" + entry[0] + "</span>";
|
||||
}
|
||||
}
|
||||
}
|
||||
return "<span class='blue'>" + itemID + "</span>";
|
||||
}
|
||||
function dateFormatter(timeStamp) {
|
||||
var date = new Date(timeStamp);
|
||||
if (lang == "en-us" || lang == null) { // MM/DD/YYYY hh:mm:ss.SSS
|
||||
return String(date.getMonth()+1).padStart(2, "0") +
|
||||
"/"+String(date.getDate()).padStart(2, "0")+
|
||||
"/"+date.getFullYear()+
|
||||
" "+String(date.getHours()).padStart(2, "0")+
|
||||
":"+String(date.getMinutes()).padStart(2, "0")+
|
||||
":"+String(date.getSeconds()).padStart(2, "0")+
|
||||
"."+String(date.getMilliseconds()).padStart(3, "0");
|
||||
} else if (lang == "zh-cn") { // YYYY/MM/DD hh:mm:ss.SSS
|
||||
return date.getFullYear()+
|
||||
"/" + String(date.getMonth()+1).padStart(2, "0") +
|
||||
"/"+String(date.getDate()).padStart(2, "0")+
|
||||
" "+String(date.getHours()).padStart(2, "0")+
|
||||
":"+String(date.getMinutes()).padStart(2, "0")+
|
||||
":"+String(date.getSeconds()).padStart(2, "0")+
|
||||
"."+String(date.getMilliseconds()).padStart(3, "0");
|
||||
}
|
||||
}
|
||||
(function (){
|
||||
var container = document.getElementById("container");
|
||||
record.forEach(element => {
|
||||
var e = document.createElement("tr");
|
||||
|
||||
e.innerHTML= "<td>" + dateFormatter(element.time) + "</td><td>" + itemMapper(element.item) + "</td>";
|
||||
container.appendChild(e);
|
||||
});
|
||||
// setup pagenation buttons
|
||||
var page = parseInt(new window.URLSearchParams(window.location.search).get("p"));
|
||||
if (!page){
|
||||
page = 0;
|
||||
}
|
||||
document.getElementById("curpage").innerText = page + 1;
|
||||
var href = new URL(window.location);
|
||||
href.searchParams.set("p", page - 1);
|
||||
document.getElementById("prev").href = href.toString();
|
||||
href.searchParams.set("p", page + 1);
|
||||
document.getElementById("next").href = href.toString();
|
||||
|
||||
if (page <= 0) {
|
||||
document.getElementById("prev").style.display = "none";
|
||||
}
|
||||
if (page >= maxPage - 1) {
|
||||
document.getElementById("next").style.display = "none";
|
||||
}
|
||||
|
||||
// setup gacha type info
|
||||
var gachaType = new window.URLSearchParams(window.location.search).get("gachaType");
|
||||
var gachaString;
|
||||
if (mappings[lang] != null && mappings[lang][gachaType] != null) {
|
||||
gachaString = mappings[lang][gachaType];
|
||||
}else{
|
||||
gachaString = mappings['default'][gachaType];
|
||||
if (gachaString == null) {
|
||||
gachaString = gachaType;
|
||||
}
|
||||
}
|
||||
document.getElementById("gacha-type").innerText = gachaString;
|
||||
})();
|
||||
</script>
|
||||
|
||||
</body>
|
||||
</html>
|
BIN
keystore.p12
BIN
keystore.p12
Binary file not shown.
BIN
lib/java-express.jar
Normal file
BIN
lib/java-express.jar
Normal file
Binary file not shown.
BIN
lib/kcp-netty-1.5.0.jar
Normal file
BIN
lib/kcp-netty-1.5.0.jar
Normal file
Binary file not shown.
Binary file not shown.
4
proxy.py
4
proxy.py
@ -57,7 +57,9 @@ class MlgmXyysd_Genshin_Impact_Proxy:
|
||||
"minor-api.mihoyo.com",
|
||||
"public-data-api.mihoyo.com",
|
||||
"uspider.yuanshen.com",
|
||||
"sdk-static.mihoyo.com"
|
||||
"sdk-static.mihoyo.com",
|
||||
"abtest-api-data-sg.hoyoverse.com",
|
||||
"log-upload-os.hoyoverse.com"
|
||||
]
|
||||
|
||||
def request(self, flow: http.HTTPFlow) -> None:
|
||||
|
@ -1,9 +1,11 @@
|
||||
package emu.grasscutter;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Locale;
|
||||
import emu.grasscutter.Grasscutter.ServerDebugMode;
|
||||
import emu.grasscutter.Grasscutter.ServerRunMode;
|
||||
import emu.grasscutter.game.mail.Mail;
|
||||
|
||||
public final class Config {
|
||||
|
||||
public String DatabaseUrl = "mongodb://localhost:27017";
|
||||
public String DatabaseCollection = "grasscutter";
|
||||
|
||||
@ -12,11 +14,18 @@ public final class Config {
|
||||
public String PACKETS_FOLDER = "./packets/";
|
||||
public String DUMPS_FOLDER = "./dumps/";
|
||||
public String KEY_FOLDER = "./keys/";
|
||||
public String SCRIPTS_FOLDER = "./resources/Scripts/";
|
||||
public String PLUGINS_FOLDER = "./plugins/";
|
||||
public String LANGUAGE_FOLDER = "./languages/";
|
||||
|
||||
public String RunMode = "HYBRID"; // HYBRID, DISPATCH_ONLY, GAME_ONLY
|
||||
public ServerDebugMode DebugMode = ServerDebugMode.NONE; // ALL, MISSING, NONE
|
||||
public ServerRunMode RunMode = ServerRunMode.HYBRID; // HYBRID, DISPATCH_ONLY, GAME_ONLY
|
||||
public GameServerOptions GameServer = new GameServerOptions();
|
||||
public DispatchServerOptions DispatchServer = new DispatchServerOptions();
|
||||
public Locale LocaleLanguage = Locale.getDefault();
|
||||
public Locale DefaultLanguage = Locale.ENGLISH;
|
||||
|
||||
public Boolean OpenStamina = true;
|
||||
public GameServerOptions getGameServerOptions() {
|
||||
return GameServer;
|
||||
}
|
||||
@ -27,11 +36,16 @@ public final class Config {
|
||||
public String Ip = "0.0.0.0";
|
||||
public String PublicIp = "127.0.0.1";
|
||||
public int Port = 443;
|
||||
public int PublicPort = 0;
|
||||
public String KeystorePath = "./keystore.p12";
|
||||
public String KeystorePassword = "";
|
||||
public String KeystorePassword = "123456";
|
||||
public Boolean UseSSL = true;
|
||||
public Boolean FrontHTTPS = true;
|
||||
public Boolean CORS = false;
|
||||
public String[] CORSAllowedOrigins = new String[] { "*" };
|
||||
|
||||
public boolean AutomaticallyCreateAccounts = false;
|
||||
public String[] defaultPermissions = new String[] { "" };
|
||||
|
||||
public RegionInfo[] GameServers = {};
|
||||
|
||||
@ -46,18 +60,17 @@ public final class Config {
|
||||
public int Port = 22102;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public static class GameServerOptions {
|
||||
public String Name = "Test";
|
||||
public String Ip = "0.0.0.0";
|
||||
public String PublicIp = "127.0.0.1";
|
||||
public int Port = 22102;
|
||||
public int PublicPort = 0;
|
||||
|
||||
public String DispatchServerDatabaseUrl = "mongodb://localhost:27017";
|
||||
public String DispatchServerDatabaseCollection = "grasscutter";
|
||||
|
||||
public boolean LOG_PACKETS = false;
|
||||
|
||||
public int InventoryLimitWeapon = 2000;
|
||||
public int InventoryLimitRelic = 2000;
|
||||
public int InventoryLimitMaterial = 2000;
|
||||
@ -67,8 +80,23 @@ public final class Config {
|
||||
public int MaxAvatarsInTeamMultiplayer = 4;
|
||||
public int MaxEntityLimit = 1000; // Max entity limit per world. // TODO: Enforce later.
|
||||
public boolean WatchGacha = false;
|
||||
public String ServerNickname = "Server";
|
||||
public int ServerAvatarId = 10000007;
|
||||
public int ServerNameCardId = 210001;
|
||||
public int ServerLevel = 1;
|
||||
public int ServerWorldLevel = 1;
|
||||
public String ServerSignature = "Server Signature";
|
||||
public int[] WelcomeEmotes = {2007, 1002, 4010};
|
||||
public String WelcomeMotd = "Welcome to Grasscutter emu";
|
||||
public String WelcomeMailTitle = "Welcome to Grasscutter!";
|
||||
public String WelcomeMailSender = "Lawnmower";
|
||||
public String WelcomeMailContent = "Hi there!\r\nFirst of all, welcome to Grasscutter. If you have any issues, please let us know so that Lawnmower can help you! \r\n\r\nCheck out our:\r\n<type=\"browser\" text=\"Discord\" href=\"https://discord.gg/T5vZU6UyeG\"/>";
|
||||
public Mail.MailItem[] WelcomeMailItems = {
|
||||
new Mail.MailItem(13509, 1, 1),
|
||||
new Mail.MailItem(201, 10000, 1),
|
||||
};
|
||||
|
||||
public boolean EnableOfficialShop = true;
|
||||
|
||||
public GameRates Game = new GameRates();
|
||||
|
||||
|
@ -5,7 +5,7 @@ import java.util.Arrays;
|
||||
import emu.grasscutter.utils.Position;
|
||||
import emu.grasscutter.utils.Utils;
|
||||
|
||||
public final class GenshinConstants {
|
||||
public final class GameConstants {
|
||||
public static String VERSION = "2.6.0";
|
||||
|
||||
public static final int MAX_TEAMS = 4;
|
@ -1,14 +1,22 @@
|
||||
package emu.grasscutter;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.File;
|
||||
import java.io.FileReader;
|
||||
import java.io.FileWriter;
|
||||
import java.io.InputStreamReader;
|
||||
import java.net.InetSocketAddress;
|
||||
import java.io.IOError;
|
||||
import java.util.Calendar;
|
||||
|
||||
import emu.grasscutter.command.CommandMap;
|
||||
import emu.grasscutter.plugin.PluginManager;
|
||||
import emu.grasscutter.plugin.api.ServerHook;
|
||||
import emu.grasscutter.scripts.ScriptLoader;
|
||||
import emu.grasscutter.utils.Utils;
|
||||
import org.jline.reader.EndOfFileException;
|
||||
import org.jline.reader.LineReader;
|
||||
import org.jline.reader.LineReaderBuilder;
|
||||
import org.jline.reader.UserInterruptException;
|
||||
import org.jline.terminal.Terminal;
|
||||
import org.jline.terminal.TerminalBuilder;
|
||||
import org.reflections.Reflections;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
@ -18,86 +26,117 @@ import com.google.gson.GsonBuilder;
|
||||
import ch.qos.logback.classic.Logger;
|
||||
import emu.grasscutter.data.ResourceLoader;
|
||||
import emu.grasscutter.database.DatabaseManager;
|
||||
import emu.grasscutter.utils.Language;
|
||||
import emu.grasscutter.server.dispatch.DispatchServer;
|
||||
import emu.grasscutter.server.game.GameServer;
|
||||
import emu.grasscutter.tools.Tools;
|
||||
import emu.grasscutter.utils.Crypto;
|
||||
|
||||
import static emu.grasscutter.utils.Language.translate;
|
||||
|
||||
public final class Grasscutter {
|
||||
private static final Logger log = (Logger) LoggerFactory.getLogger(Grasscutter.class);
|
||||
private static LineReader consoleLineReader = null;
|
||||
|
||||
private static Config config;
|
||||
|
||||
private static Language language;
|
||||
|
||||
private static final Gson gson = new GsonBuilder().setPrettyPrinting().create();
|
||||
private static final File configFile = new File("./config.json");
|
||||
|
||||
public static RunMode MODE = RunMode.BOTH;
|
||||
|
||||
private static int day; // Current day of week.
|
||||
|
||||
private static DispatchServer dispatchServer;
|
||||
private static GameServer gameServer;
|
||||
|
||||
private static PluginManager pluginManager;
|
||||
|
||||
public static final Reflections reflector = new Reflections("emu.grasscutter");
|
||||
|
||||
|
||||
static {
|
||||
// Declare logback configuration.
|
||||
System.setProperty("logback.configurationFile", "src/main/resources/logback.xml");
|
||||
|
||||
|
||||
// Load server configuration.
|
||||
Grasscutter.loadConfig();
|
||||
|
||||
|
||||
// Load translation files.
|
||||
Grasscutter.loadLanguage();
|
||||
|
||||
// Check server structure.
|
||||
Utils.startupCheck();
|
||||
}
|
||||
|
||||
public static void main(String[] args) throws Exception {
|
||||
Crypto.loadKeys();
|
||||
|
||||
|
||||
public static void main(String[] args) throws Exception {
|
||||
Crypto.loadKeys(); // Load keys from buffers.
|
||||
|
||||
// Parse arguments.
|
||||
boolean exitEarly = false;
|
||||
for (String arg : args) {
|
||||
switch (arg.toLowerCase()) {
|
||||
case "-auth":
|
||||
MODE = RunMode.AUTH;
|
||||
break;
|
||||
case "-game":
|
||||
MODE = RunMode.GAME;
|
||||
break;
|
||||
case "-handbook":
|
||||
Tools.createGmHandbook();
|
||||
return;
|
||||
case "-handbook" -> {
|
||||
Tools.createGmHandbook(); exitEarly = true;
|
||||
}
|
||||
case "-gachamap" -> {
|
||||
Tools.createGachaMapping(Grasscutter.getConfig().DATA_FOLDER + "/gacha_mappings.js"); exitEarly = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Initialize server.
|
||||
Grasscutter.getLogger().info("Starting Grasscutter...");
|
||||
|
||||
// Load all resources.
|
||||
ResourceLoader.loadAll();
|
||||
// Database
|
||||
DatabaseManager.initialize();
|
||||
|
||||
// Start servers.
|
||||
if(getConfig().RunMode.equalsIgnoreCase("HYBRID")) {
|
||||
dispatchServer = new DispatchServer();
|
||||
dispatchServer.start();
|
||||
// Exit early if argument sets it.
|
||||
if(exitEarly) System.exit(0);
|
||||
|
||||
gameServer = new GameServer(new InetSocketAddress(getConfig().getGameServerOptions().Ip, getConfig().getGameServerOptions().Port));
|
||||
gameServer.start();
|
||||
} else if(getConfig().RunMode.equalsIgnoreCase("DISPATCH_ONLY")) {
|
||||
dispatchServer = new DispatchServer();
|
||||
// Initialize server.
|
||||
Grasscutter.getLogger().info(translate("messages.status.starting"));
|
||||
|
||||
// Load all resources.
|
||||
Grasscutter.updateDayOfWeek();
|
||||
ResourceLoader.loadAll();
|
||||
ScriptLoader.init();
|
||||
|
||||
// Initialize database.
|
||||
DatabaseManager.initialize();
|
||||
|
||||
// Create server instances.
|
||||
dispatchServer = new DispatchServer();
|
||||
gameServer = new GameServer();
|
||||
// Create a server hook instance with both servers.
|
||||
new ServerHook(gameServer, dispatchServer);
|
||||
// Create plugin manager instance.
|
||||
pluginManager = new PluginManager();
|
||||
|
||||
// Start servers.
|
||||
if (getConfig().RunMode == ServerRunMode.HYBRID) {
|
||||
dispatchServer.start();
|
||||
} else if(getConfig().RunMode.equalsIgnoreCase("GAME_ONLY")) {
|
||||
gameServer = new GameServer(new InetSocketAddress(getConfig().getGameServerOptions().Ip, getConfig().getGameServerOptions().Port));
|
||||
gameServer.start();
|
||||
} else if (getConfig().RunMode == ServerRunMode.DISPATCH_ONLY) {
|
||||
dispatchServer.start();
|
||||
} else if (getConfig().RunMode == ServerRunMode.GAME_ONLY) {
|
||||
gameServer.start();
|
||||
} else {
|
||||
getLogger().error("Invalid server run mode. " + getConfig().RunMode);
|
||||
getLogger().error("Server run mode must be 'HYBRID', 'DISPATCH_ONLY', or 'GAME_ONLY'. Unable to start Grasscutter...");
|
||||
getLogger().error("Shutting down...");
|
||||
getLogger().error(translate("messages.status.run_mode_error", getConfig().RunMode));
|
||||
getLogger().error(translate("messages.status.run_mode_help"));
|
||||
getLogger().error(translate("messages.status.shutdown"));
|
||||
System.exit(1);
|
||||
}
|
||||
|
||||
// Enable all plugins.
|
||||
pluginManager.enablePlugins();
|
||||
|
||||
// Hook into shutdown event.
|
||||
Runtime.getRuntime().addShutdownHook(new Thread(Grasscutter::onShutdown));
|
||||
|
||||
|
||||
// Open console.
|
||||
startConsole();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Server shutdown event.
|
||||
*/
|
||||
private static void onShutdown() {
|
||||
// Disable all plugins.
|
||||
pluginManager.disablePlugins();
|
||||
}
|
||||
|
||||
public static void loadConfig() {
|
||||
try (FileReader file = new FileReader(configFile)) {
|
||||
config = gson.fromJson(file, Config.class);
|
||||
@ -107,49 +146,97 @@ public final class Grasscutter {
|
||||
saveConfig();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public static void loadLanguage() {
|
||||
var locale = config.LocaleLanguage;
|
||||
var languageTag = locale.toLanguageTag();
|
||||
|
||||
if (languageTag.equals("und")) {
|
||||
Grasscutter.getLogger().error("Illegal locale language, using 'en-US' instead.");
|
||||
language = Language.getLanguage("en-US");
|
||||
} else {
|
||||
language = Language.getLanguage(languageTag);
|
||||
}
|
||||
}
|
||||
|
||||
public static void saveConfig() {
|
||||
try (FileWriter file = new FileWriter(configFile)) {
|
||||
file.write(gson.toJson(config));
|
||||
} catch (Exception e) {
|
||||
Grasscutter.getLogger().error("Config save error");
|
||||
Grasscutter.getLogger().error("Unable to save config file.");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public static void startConsole() {
|
||||
String input;
|
||||
try (BufferedReader br = new BufferedReader(new InputStreamReader(System.in))) {
|
||||
while ((input = br.readLine()) != null) {
|
||||
try {
|
||||
if(getConfig().RunMode.equalsIgnoreCase("DISPATCH_ONLY")) {
|
||||
getLogger().error("Commands are not supported in dispatch only mode");
|
||||
return;
|
||||
}
|
||||
CommandMap.getInstance().invoke(null, input);
|
||||
} catch (Exception e) {
|
||||
Grasscutter.getLogger().error("Command error: ");
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
Grasscutter.getLogger().error("An error occurred.", e);
|
||||
// Console should not start in dispatch only mode.
|
||||
if (getConfig().RunMode == ServerRunMode.DISPATCH_ONLY) {
|
||||
getLogger().info(translate("messages.dispatch.no_commands_error"));
|
||||
return;
|
||||
}
|
||||
|
||||
getLogger().info(translate("messages.status.done"));
|
||||
String input = null;
|
||||
boolean isLastInterrupted = false;
|
||||
while (true) {
|
||||
try {
|
||||
input = consoleLineReader.readLine("> ");
|
||||
} catch (UserInterruptException e) {
|
||||
if (!isLastInterrupted) {
|
||||
isLastInterrupted = true;
|
||||
Grasscutter.getLogger().info("Press Ctrl-C again to shutdown.");
|
||||
continue;
|
||||
} else {
|
||||
Runtime.getRuntime().exit(0);
|
||||
}
|
||||
} catch (EndOfFileException e) {
|
||||
Grasscutter.getLogger().info("EOF detected.");
|
||||
continue;
|
||||
} catch (IOError e) {
|
||||
Grasscutter.getLogger().error("An IO error occurred.", e);
|
||||
continue;
|
||||
}
|
||||
|
||||
isLastInterrupted = false;
|
||||
try {
|
||||
CommandMap.getInstance().invoke(null, null, input);
|
||||
} catch (Exception e) {
|
||||
Grasscutter.getLogger().error(translate("messages.game.command_error"), e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public enum RunMode {
|
||||
BOTH,
|
||||
AUTH,
|
||||
GAME
|
||||
}
|
||||
|
||||
public static Config getConfig() {
|
||||
return config;
|
||||
}
|
||||
|
||||
public static Language getLanguage() {
|
||||
return language;
|
||||
}
|
||||
|
||||
public static Logger getLogger() {
|
||||
return log;
|
||||
}
|
||||
|
||||
public static LineReader getConsole() {
|
||||
if (consoleLineReader == null) {
|
||||
Terminal terminal = null;
|
||||
try {
|
||||
terminal = TerminalBuilder.builder().jna(true).build();
|
||||
} catch (Exception e) {
|
||||
try {
|
||||
// Fallback to a dumb jline terminal.
|
||||
terminal = TerminalBuilder.builder().dumb(true).build();
|
||||
} catch (Exception ignored) {
|
||||
// When dumb is true, build() never throws.
|
||||
}
|
||||
}
|
||||
consoleLineReader = LineReaderBuilder.builder()
|
||||
.terminal(terminal)
|
||||
.build();
|
||||
}
|
||||
return consoleLineReader;
|
||||
}
|
||||
|
||||
public static Gson getGsonFactory() {
|
||||
return gson;
|
||||
}
|
||||
@ -161,4 +248,25 @@ public final class Grasscutter {
|
||||
public static GameServer getGameServer() {
|
||||
return gameServer;
|
||||
}
|
||||
|
||||
public static PluginManager getPluginManager() {
|
||||
return pluginManager;
|
||||
}
|
||||
|
||||
public static void updateDayOfWeek() {
|
||||
Calendar calendar = Calendar.getInstance();
|
||||
day = calendar.get(Calendar.DAY_OF_WEEK);
|
||||
}
|
||||
|
||||
public static int getCurrentDayOfWeek() {
|
||||
return day;
|
||||
}
|
||||
|
||||
public enum ServerRunMode {
|
||||
HYBRID, DISPATCH_ONLY, GAME_ONLY
|
||||
}
|
||||
|
||||
public enum ServerDebugMode {
|
||||
ALL, MISSING, NONE
|
||||
}
|
||||
}
|
||||
|
@ -14,4 +14,8 @@ public @interface Command {
|
||||
String[] aliases() default {};
|
||||
|
||||
String permission() default "";
|
||||
|
||||
String permissionTargeted() default "";
|
||||
|
||||
boolean threading() default false;
|
||||
}
|
||||
|
@ -1,7 +1,7 @@
|
||||
package emu.grasscutter.command;
|
||||
|
||||
import emu.grasscutter.Grasscutter;
|
||||
import emu.grasscutter.game.GenshinPlayer;
|
||||
import emu.grasscutter.game.player.Player;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@ -12,7 +12,7 @@ public interface CommandHandler {
|
||||
* @param player The player to send the message to, or null for the server console.
|
||||
* @param message The message to send.
|
||||
*/
|
||||
static void sendMessage(GenshinPlayer player, String message) {
|
||||
static void sendMessage(Player player, String message) {
|
||||
if (player == null) {
|
||||
Grasscutter.getLogger().info(message);
|
||||
} else {
|
||||
@ -25,6 +25,6 @@ public interface CommandHandler {
|
||||
* @param sender The player/console that invoked the command.
|
||||
* @param args The arguments to the command.
|
||||
*/
|
||||
default void execute(GenshinPlayer sender, List<String> args) {
|
||||
default void execute(Player sender, Player targetPlayer, List<String> args) {
|
||||
}
|
||||
}
|
||||
|
@ -2,15 +2,20 @@ package emu.grasscutter.command;
|
||||
|
||||
import emu.grasscutter.Grasscutter;
|
||||
import emu.grasscutter.game.Account;
|
||||
import emu.grasscutter.game.GenshinPlayer;
|
||||
import emu.grasscutter.game.player.Player;
|
||||
|
||||
import org.reflections.Reflections;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
import static emu.grasscutter.utils.Language.translate;
|
||||
|
||||
@SuppressWarnings({"UnusedReturnValue", "unused"})
|
||||
public final class CommandMap {
|
||||
private final Map<String, CommandHandler> commands = new HashMap<>();
|
||||
private final Map<String, Command> annotations = new HashMap<>();
|
||||
private final Map<String, Integer> targetPlayerIds = new HashMap<>();
|
||||
private static final String consoleId = "console";
|
||||
public CommandMap() {
|
||||
this(false);
|
||||
}
|
||||
@ -74,6 +79,12 @@ public final class CommandMap {
|
||||
return this;
|
||||
}
|
||||
|
||||
public List<Command> getAnnotationsAsList() { return new LinkedList<>(this.annotations.values()); }
|
||||
|
||||
public HashMap<String, Command> getAnnotations() {
|
||||
return new LinkedHashMap<>(this.annotations);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a list of all registered commands.
|
||||
*
|
||||
@ -103,40 +114,121 @@ public final class CommandMap {
|
||||
* @param player The player invoking the command or null for the server console.
|
||||
* @param rawMessage The messaged used to invoke the command.
|
||||
*/
|
||||
public void invoke(GenshinPlayer player, String rawMessage) {
|
||||
public void invoke(Player player, Player targetPlayer, String rawMessage) {
|
||||
rawMessage = rawMessage.trim();
|
||||
if(rawMessage.length() == 0) {
|
||||
CommandHandler.sendMessage(player, "No command specified."); return;
|
||||
if (rawMessage.length() == 0) {
|
||||
CommandHandler.sendMessage(player, translate("commands.generic.not_specified"));
|
||||
return;
|
||||
}
|
||||
|
||||
// Remove prefix if present.
|
||||
if (!Character.isLetter(rawMessage.charAt(0)))
|
||||
rawMessage = rawMessage.substring(1);
|
||||
|
||||
// Parse message.
|
||||
String[] split = rawMessage.split(" ");
|
||||
List<String> args = new LinkedList<>(Arrays.asList(split));
|
||||
String label = args.remove(0);
|
||||
String playerId = (player == null) ? consoleId : player.getAccount().getId();
|
||||
|
||||
// Check for special cases - currently only target command.
|
||||
String targetUidStr = null;
|
||||
if (label.startsWith("@")) { // @[UID]
|
||||
targetUidStr = label.substring(1);
|
||||
} else if (label.equalsIgnoreCase("target")) { // target [[@]UID]
|
||||
if (args.size() > 0) {
|
||||
targetUidStr = args.get(0);
|
||||
if (targetUidStr.startsWith("@")) {
|
||||
targetUidStr = targetUidStr.substring(1);
|
||||
}
|
||||
} else {
|
||||
targetUidStr = "";
|
||||
}
|
||||
}
|
||||
if (targetUidStr != null) {
|
||||
if (targetUidStr.equals("")) { // Clears the default targetPlayer.
|
||||
targetPlayerIds.remove(playerId);
|
||||
CommandHandler.sendMessage(player, translate("commands.execution.clear_target"));
|
||||
} else { // Sets default targetPlayer to the UID provided.
|
||||
try {
|
||||
int uid = Integer.parseInt(targetUidStr);
|
||||
targetPlayer = Grasscutter.getGameServer().getPlayerByUid(uid);
|
||||
if (targetPlayer == null) {
|
||||
CommandHandler.sendMessage(player, translate("commands.generic.execution.player_exist_offline_error"));
|
||||
} else {
|
||||
targetPlayerIds.put(playerId, uid);
|
||||
CommandHandler.sendMessage(player, translate("commands.execution.set_target", targetUidStr));
|
||||
}
|
||||
} catch (NumberFormatException e) {
|
||||
CommandHandler.sendMessage(player, translate("commands.execution.uid_error"));
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// Get command handler.
|
||||
CommandHandler handler = this.commands.get(label);
|
||||
if (handler == null) {
|
||||
CommandHandler.sendMessage(player, "Unknown command: " + label);
|
||||
CommandHandler.sendMessage(player, translate("commands.generic.unknown_command", label));
|
||||
return;
|
||||
}
|
||||
|
||||
// If any @UID argument is present, override targetPlayer with it.
|
||||
for (int i = 0; i < args.size(); i++) {
|
||||
String arg = args.get(i);
|
||||
if (arg.startsWith("@")) {
|
||||
arg = args.remove(i).substring(1);
|
||||
try {
|
||||
int uid = Integer.parseInt(arg);
|
||||
targetPlayer = Grasscutter.getGameServer().getPlayerByUid(uid);
|
||||
if (targetPlayer == null) {
|
||||
CommandHandler.sendMessage(player, translate("commands.generic.execution.player_exist_offline_error"));
|
||||
return;
|
||||
}
|
||||
break;
|
||||
} catch (NumberFormatException e) {
|
||||
CommandHandler.sendMessage(player, translate("commands.execution.uid_error"));
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// If there's still no targetPlayer at this point, use previously-set target
|
||||
if (targetPlayer == null) {
|
||||
if (targetPlayerIds.containsKey(playerId)) {
|
||||
targetPlayer = Grasscutter.getGameServer().getPlayerByUid(targetPlayerIds.get(playerId)); // We check every time in case the target goes offline after being targeted
|
||||
if (targetPlayer == null) {
|
||||
CommandHandler.sendMessage(player, translate("commands.generic.execution.player_exist_offline_error"));
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
// If there's still no targetPlayer at this point, use executor.
|
||||
targetPlayer = player;
|
||||
}
|
||||
}
|
||||
|
||||
// Check for permission.
|
||||
if (player != null) {
|
||||
String permissionNode = this.annotations.get(label).permission();
|
||||
String permissionNodeTargeted = this.annotations.get(label).permissionTargeted();
|
||||
Account account = player.getAccount();
|
||||
if(!permissionNode.isEmpty() && !account.hasPermission(permissionNode)) {
|
||||
CommandHandler.sendMessage(player, "You do not have permission to run this command.");
|
||||
if (player != targetPlayer) { // Additional permission required for targeting another player
|
||||
if (!permissionNodeTargeted.isEmpty() && !account.hasPermission(permissionNodeTargeted)) {
|
||||
CommandHandler.sendMessage(player, translate("commands.generic.permission_error"));
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (!permissionNode.isEmpty() && !account.hasPermission(permissionNode)) {
|
||||
CommandHandler.sendMessage(player, translate("commands.generic.permission_error"));
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Invoke execute method for handler.
|
||||
handler.execute(player, args);
|
||||
boolean threading = this.annotations.get(label).threading();
|
||||
final Player targetPlayerF = targetPlayer; // Is there a better way to do this?
|
||||
Runnable runnable = () -> handler.execute(player, targetPlayerF, args);
|
||||
if(threading) {
|
||||
new Thread(runnable).start();
|
||||
} else {
|
||||
runnable.run();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -3,23 +3,24 @@ package emu.grasscutter.command.commands;
|
||||
import emu.grasscutter.command.Command;
|
||||
import emu.grasscutter.command.CommandHandler;
|
||||
import emu.grasscutter.database.DatabaseHelper;
|
||||
import emu.grasscutter.game.GenshinPlayer;
|
||||
import emu.grasscutter.game.player.Player;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@Command(label = "account", usage = "account <create|delete> <username> [uid]",
|
||||
description = "Modify user accounts")
|
||||
import static emu.grasscutter.utils.Language.translate;
|
||||
|
||||
@Command(label = "account", usage = "account <create|delete> <username> [uid]", description = "Modify user accounts")
|
||||
public final class AccountCommand implements CommandHandler {
|
||||
|
||||
@Override
|
||||
public void execute(GenshinPlayer sender, List<String> args) {
|
||||
public void execute(Player sender, Player targetPlayer, List<String> args) {
|
||||
if (sender != null) {
|
||||
CommandHandler.sendMessage(sender, "This command can only be run from the console.");
|
||||
CommandHandler.sendMessage(sender, translate("commands.generic.console_execute_error"));
|
||||
return;
|
||||
}
|
||||
|
||||
if (args.size() < 2) {
|
||||
CommandHandler.sendMessage(null, "Usage: account <create|delete> <username> [uid]");
|
||||
CommandHandler.sendMessage(null, translate("commands.account.command_usage"));
|
||||
return;
|
||||
}
|
||||
|
||||
@ -28,7 +29,7 @@ public final class AccountCommand implements CommandHandler {
|
||||
|
||||
switch (action) {
|
||||
default:
|
||||
CommandHandler.sendMessage(null, "Usage: account <create|delete> <username> [uid]");
|
||||
CommandHandler.sendMessage(null, translate("commands.account.command_usage"));
|
||||
return;
|
||||
case "create":
|
||||
int uid = 0;
|
||||
@ -36,26 +37,27 @@ public final class AccountCommand implements CommandHandler {
|
||||
try {
|
||||
uid = Integer.parseInt(args.get(2));
|
||||
} catch (NumberFormatException ignored) {
|
||||
CommandHandler.sendMessage(null, "Invalid UID.");
|
||||
CommandHandler.sendMessage(null, translate("commands.account.invalid"));
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
emu.grasscutter.game.Account account = DatabaseHelper.createAccountWithId(username, uid);
|
||||
if (account == null) {
|
||||
CommandHandler.sendMessage(null, "Account already exists.");
|
||||
CommandHandler.sendMessage(null, translate("commands.account.exists"));
|
||||
return;
|
||||
} else {
|
||||
CommandHandler.sendMessage(null, "Account created with UID " + account.getPlayerUid() + ".");
|
||||
account.addPermission("*"); // Grant the player superuser permissions.
|
||||
account.addPermission("*");
|
||||
account.save(); // Save account to database.
|
||||
|
||||
CommandHandler.sendMessage(null, translate("commands.account.create", Integer.toString(account.getPlayerUid())));
|
||||
}
|
||||
return;
|
||||
case "delete":
|
||||
if (DatabaseHelper.deleteAccount(username)) {
|
||||
CommandHandler.sendMessage(null, "Account deleted.");
|
||||
CommandHandler.sendMessage(null, translate("commands.account.delete"));
|
||||
} else {
|
||||
CommandHandler.sendMessage(null, "Account not found.");
|
||||
CommandHandler.sendMessage(null, translate("commands.account.no_account"));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -3,27 +3,29 @@ 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.player.Player;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import static emu.grasscutter.utils.Language.translate;
|
||||
|
||||
@Command(label = "broadcast", usage = "broadcast <message>",
|
||||
description = "Sends a message to all the players", aliases = {"b"}, permission = "server.broadcast")
|
||||
public final class BroadcastCommand implements CommandHandler {
|
||||
|
||||
@Override
|
||||
public void execute(GenshinPlayer sender, List<String> args) {
|
||||
public void execute(Player sender, Player targetPlayer, List<String> args) {
|
||||
if (args.size() < 1) {
|
||||
CommandHandler.sendMessage(sender, "Usage: broadcast <message>");
|
||||
CommandHandler.sendMessage(sender, translate("commands.broadcast.command_usage"));
|
||||
return;
|
||||
}
|
||||
|
||||
String message = String.join(" ", args.subList(0, args.size()));
|
||||
|
||||
for (GenshinPlayer p : Grasscutter.getGameServer().getPlayers().values()) {
|
||||
for (Player p : Grasscutter.getGameServer().getPlayers().values()) {
|
||||
CommandHandler.sendMessage(p, message);
|
||||
}
|
||||
|
||||
CommandHandler.sendMessage(sender, "Message sent.");
|
||||
CommandHandler.sendMessage(sender, translate("commands.broadcast.message_sent"));
|
||||
}
|
||||
}
|
||||
|
@ -2,41 +2,42 @@ package emu.grasscutter.command.commands;
|
||||
|
||||
import emu.grasscutter.command.Command;
|
||||
import emu.grasscutter.command.CommandHandler;
|
||||
import emu.grasscutter.game.GenshinPlayer;
|
||||
import emu.grasscutter.game.player.Player;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import static emu.grasscutter.utils.Language.translate;
|
||||
|
||||
@Command(label = "changescene", usage = "changescene <scene id>",
|
||||
description = "Changes your scene", aliases = {"scene"}, permission = "player.changescene")
|
||||
public final class ChangeSceneCommand implements CommandHandler {
|
||||
@Override
|
||||
public void execute(GenshinPlayer sender, List<String> args) {
|
||||
if (sender == null) {
|
||||
CommandHandler.sendMessage(null, "Run this command in-game.");
|
||||
public void execute(Player sender, Player targetPlayer, List<String> args) {
|
||||
if (targetPlayer == null) {
|
||||
CommandHandler.sendMessage(sender, translate("commands.execution.need_target"));
|
||||
return;
|
||||
}
|
||||
|
||||
if (args.size() < 1) {
|
||||
CommandHandler.sendMessage(sender, "Usage: changescene <scene id>");
|
||||
if (args.size() != 1) {
|
||||
CommandHandler.sendMessage(sender, translate("commands.changescene.usage"));
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
int sceneId = Integer.parseInt(args.get(0));
|
||||
|
||||
if (sceneId == sender.getSceneId()) {
|
||||
CommandHandler.sendMessage(sender, "You are already in that scene");
|
||||
if (sceneId == targetPlayer.getSceneId()) {
|
||||
CommandHandler.sendMessage(sender, translate("commands.changescene.already_in_scene"));
|
||||
return;
|
||||
}
|
||||
|
||||
boolean result = sender.getWorld().transferPlayerToScene(sender, sceneId, sender.getPos());
|
||||
CommandHandler.sendMessage(sender, "Changed to scene " + sceneId);
|
||||
boolean result = targetPlayer.getWorld().transferPlayerToScene(targetPlayer, sceneId, targetPlayer.getPos());
|
||||
CommandHandler.sendMessage(sender, translate("commands.changescene.result", Integer.toString(sceneId)));
|
||||
|
||||
if (!result) {
|
||||
CommandHandler.sendMessage(sender, "Scene does not exist");
|
||||
CommandHandler.sendMessage(sender, translate("commands.changescene.exists_error"));
|
||||
}
|
||||
} catch (Exception e) {
|
||||
CommandHandler.sendMessage(sender, "Usage: changescene <scene id>");
|
||||
CommandHandler.sendMessage(sender, translate("commands.execution.argument_error"));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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<String> 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()));
|
||||
}
|
||||
}
|
109
src/main/java/emu/grasscutter/command/commands/ClearCommand.java
Normal file
109
src/main/java/emu/grasscutter/command/commands/ClearCommand.java
Normal file
@ -0,0 +1,109 @@
|
||||
package emu.grasscutter.command.commands;
|
||||
|
||||
import emu.grasscutter.Grasscutter;
|
||||
import emu.grasscutter.command.Command;
|
||||
import emu.grasscutter.command.CommandHandler;
|
||||
import emu.grasscutter.game.inventory.GameItem;
|
||||
import emu.grasscutter.game.inventory.Inventory;
|
||||
import emu.grasscutter.game.inventory.ItemType;
|
||||
import emu.grasscutter.game.player.Player;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import static emu.grasscutter.utils.Language.translate;
|
||||
|
||||
@Command(label = "clear", usage = "clear <all|wp|art|mat>", //Merged /clearartifacts and /clearweapons to /clear <args> [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(Player sender, Player targetPlayer, List<String> args) {
|
||||
if (targetPlayer == null) {
|
||||
CommandHandler.sendMessage(sender, translate("commands.execution.need_target"));
|
||||
return;
|
||||
}
|
||||
if (args.size() < 1) {
|
||||
CommandHandler.sendMessage(sender, translate("commands.clear.command_usage"));
|
||||
return;
|
||||
}
|
||||
Inventory playerInventory = targetPlayer.getInventory();
|
||||
List<GameItem> toDelete = null;
|
||||
|
||||
switch (args.get(0)) {
|
||||
case "wp" -> {
|
||||
toDelete = playerInventory.getItems().values().stream()
|
||||
.filter(item -> item.getItemType() == ItemType.ITEM_WEAPON)
|
||||
.filter(item -> !item.isLocked() && !item.isEquipped())
|
||||
.toList();
|
||||
CommandHandler.sendMessage(sender, translate("commands.clear.weapons", targetPlayer.getNickname()));
|
||||
}
|
||||
case "art" -> {
|
||||
toDelete = 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())
|
||||
.toList();
|
||||
CommandHandler.sendMessage(sender, translate("commands.clear.artifacts", targetPlayer.getNickname()));
|
||||
}
|
||||
case "mat" -> {
|
||||
toDelete = 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())
|
||||
.toList();
|
||||
CommandHandler.sendMessage(sender, translate("commands.clear.materials", targetPlayer.getNickname()));
|
||||
}
|
||||
case "all" -> {
|
||||
toDelete = 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())
|
||||
.toList();
|
||||
CommandHandler.sendMessage(sender, translate("commands.clear.artifacts", targetPlayer.getNickname()));
|
||||
playerInventory.removeItems(toDelete);
|
||||
|
||||
toDelete = playerInventory.getItems().values().stream()
|
||||
.filter(item2 -> item2.getItemType() == ItemType.ITEM_MATERIAL)
|
||||
.filter(item2 -> !item2.isLocked() && !item2.isEquipped())
|
||||
.toList();
|
||||
playerInventory.removeItems(toDelete);
|
||||
CommandHandler.sendMessage(sender, translate("commands.clear.materials", targetPlayer.getNickname()));
|
||||
|
||||
toDelete = 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())
|
||||
.toList();
|
||||
playerInventory.removeItems(toDelete);
|
||||
CommandHandler.sendMessage(sender, translate("commands.clear.weapons", targetPlayer.getNickname()));
|
||||
|
||||
toDelete = playerInventory.getItems().values().stream()
|
||||
.filter(item4 -> item4.getItemType() == ItemType.ITEM_FURNITURE)
|
||||
.filter(item4 -> !item4.isLocked() && !item4.isEquipped())
|
||||
.toList();
|
||||
playerInventory.removeItems(toDelete);
|
||||
CommandHandler.sendMessage(sender, translate("commands.clear.furniture", targetPlayer.getNickname()));
|
||||
|
||||
toDelete = playerInventory.getItems().values().stream()
|
||||
.filter(item5 -> item5.getItemType() == ItemType.ITEM_DISPLAY)
|
||||
.filter(item5 -> !item5.isLocked() && !item5.isEquipped())
|
||||
.toList();
|
||||
playerInventory.removeItems(toDelete);
|
||||
CommandHandler.sendMessage(sender, translate("commands.clear.displays", targetPlayer.getNickname()));
|
||||
|
||||
toDelete = playerInventory.getItems().values().stream()
|
||||
.filter(item6 -> item6.getItemType() == ItemType.ITEM_VIRTUAL)
|
||||
.filter(item6 -> !item6.isLocked() && !item6.isEquipped())
|
||||
.toList();
|
||||
CommandHandler.sendMessage(sender, translate("commands.clear.virtuals", targetPlayer.getNickname()));
|
||||
CommandHandler.sendMessage(sender, translate("commands.clear.everything", targetPlayer.getNickname()));
|
||||
}
|
||||
}
|
||||
|
||||
if (toDelete != null) {
|
||||
playerInventory.removeItems(toDelete);
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,55 @@
|
||||
package emu.grasscutter.command.commands;
|
||||
|
||||
import emu.grasscutter.Grasscutter;
|
||||
import emu.grasscutter.command.Command;
|
||||
import emu.grasscutter.command.CommandHandler;
|
||||
import emu.grasscutter.game.player.Player;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import static emu.grasscutter.utils.Language.translate;
|
||||
|
||||
@Command(label = "coop", usage = "coop [host UID]",
|
||||
description = "Forces someone to join the world of others", permission = "server.coop")
|
||||
public final class CoopCommand implements CommandHandler {
|
||||
@Override
|
||||
public void execute(Player sender, Player targetPlayer, List<String> args) {
|
||||
if (targetPlayer == null) {
|
||||
CommandHandler.sendMessage(sender, translate("commands.execution.need_target"));
|
||||
return;
|
||||
}
|
||||
|
||||
Player host = sender;
|
||||
switch (args.size()) {
|
||||
case 0: // Summon target to self
|
||||
CommandHandler.sendMessage(sender, translate("commands.coop.usage"));
|
||||
if (sender == null) // Console doesn't have a self to summon to
|
||||
return;
|
||||
break;
|
||||
case 1: // Summon target to argument
|
||||
try {
|
||||
int hostId = Integer.parseInt(args.get(0));
|
||||
host = Grasscutter.getGameServer().getPlayerByUid(hostId);
|
||||
if (host == null) {
|
||||
CommandHandler.sendMessage(sender, translate("commands.execution.player_offline_error"));
|
||||
return;
|
||||
}
|
||||
break;
|
||||
} catch (NumberFormatException ignored) {
|
||||
CommandHandler.sendMessage(sender, translate("commands.execution.uid_error"));
|
||||
return;
|
||||
}
|
||||
default:
|
||||
CommandHandler.sendMessage(sender, translate("commands.coop.usage"));
|
||||
return;
|
||||
}
|
||||
|
||||
// There's no target==host check but this just places them in multiplayer in their own world which seems fine.
|
||||
if (targetPlayer.isInMultiplayer()) {
|
||||
targetPlayer.getServer().getMultiplayerManager().leaveCoop(targetPlayer);
|
||||
}
|
||||
host.getServer().getMultiplayerManager().applyEnterMp(targetPlayer, host.getUid());
|
||||
targetPlayer.getServer().getMultiplayerManager().applyEnterMpReply(host, targetPlayer.getUid(), true);
|
||||
CommandHandler.sendMessage(sender, translate("commands.coop.success", targetPlayer.getNickname(), host.getNickname()));
|
||||
}
|
||||
}
|
@ -1,56 +1,69 @@
|
||||
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.GameData;
|
||||
import emu.grasscutter.data.def.ItemData;
|
||||
import emu.grasscutter.game.GenshinPlayer;
|
||||
import emu.grasscutter.game.entity.EntityItem;
|
||||
import emu.grasscutter.game.player.Player;
|
||||
import emu.grasscutter.utils.Position;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import static emu.grasscutter.utils.Language.translate;
|
||||
|
||||
@Command(label = "drop", usage = "drop <itemId|itemName> [amount]",
|
||||
description = "Drops an item near you", aliases = {"d", "dropitem"}, permission = "server.drop")
|
||||
public final class DropCommand implements CommandHandler {
|
||||
|
||||
@Override
|
||||
public void execute(GenshinPlayer sender, List<String> args) {
|
||||
if (sender == null) {
|
||||
CommandHandler.sendMessage(null, "Run this command in-game.");
|
||||
public void execute(Player sender, Player targetPlayer, List<String> args) {
|
||||
if (targetPlayer == null) {
|
||||
CommandHandler.sendMessage(null, translate("commands.execution.need_target"));
|
||||
return;
|
||||
}
|
||||
|
||||
int item = 0;
|
||||
int amount = 1;
|
||||
|
||||
if (args.size() < 1) {
|
||||
CommandHandler.sendMessage(sender, "Usage: drop <itemId|itemName> [amount]");
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
int item = Integer.parseInt(args.get(0));
|
||||
int amount = 1;
|
||||
if (args.size() > 1) amount = Integer.parseInt(args.get(1));
|
||||
|
||||
ItemData itemData = GenshinData.getItemDataMap().get(item);
|
||||
if (itemData == null) {
|
||||
CommandHandler.sendMessage(sender, "Invalid item id.");
|
||||
return;
|
||||
}
|
||||
|
||||
if (itemData.isEquip()) {
|
||||
float range = (5f + (.1f * amount));
|
||||
for (int i = 0; i < amount; i++) {
|
||||
Position pos = sender.getPos().clone().addX((float) (Math.random() * range) - (range / 2)).addY(3f).addZ((float) (Math.random() * range) - (range / 2));
|
||||
EntityItem entity = new EntityItem(sender.getScene(), sender, itemData, pos, 1);
|
||||
sender.getScene().addEntity(entity);
|
||||
switch (args.size()) {
|
||||
case 2:
|
||||
try {
|
||||
amount = Integer.parseInt(args.get(1));
|
||||
} catch (NumberFormatException ignored) {
|
||||
CommandHandler.sendMessage(sender, translate("commands.generic.invalid.amount"));
|
||||
return;
|
||||
} // Slightly cheeky here: no break, so it falls through to initialize the first argument too
|
||||
case 1:
|
||||
try {
|
||||
item = Integer.parseInt(args.get(0));
|
||||
} catch (NumberFormatException ignored) {
|
||||
CommandHandler.sendMessage(sender, translate("commands.generic.invalid.itemId"));
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
EntityItem entity = new EntityItem(sender.getScene(), sender, itemData, sender.getPos().clone().addY(3f), amount);
|
||||
sender.getScene().addEntity(entity);
|
||||
}
|
||||
CommandHandler.sendMessage(sender, String.format("Dropped %s of %s.", amount, item));
|
||||
} catch (NumberFormatException ignored) {
|
||||
CommandHandler.sendMessage(sender, "Invalid item or player ID.");
|
||||
break;
|
||||
default:
|
||||
CommandHandler.sendMessage(sender, translate("commands.drop.command_usage"));
|
||||
return;
|
||||
}
|
||||
|
||||
ItemData itemData = GameData.getItemDataMap().get(item);
|
||||
if (itemData == null) {
|
||||
CommandHandler.sendMessage(sender, translate("commands.generic.invalid.itemId"));
|
||||
return;
|
||||
}
|
||||
if (itemData.isEquip()) {
|
||||
float range = (5f + (.1f * amount));
|
||||
for (int i = 0; i < amount; i++) {
|
||||
Position pos = targetPlayer.getPos().clone().addX((float) (Math.random() * range) - (range / 2)).addY(3f).addZ((float) (Math.random() * range) - (range / 2));
|
||||
EntityItem entity = new EntityItem(targetPlayer.getScene(), targetPlayer, itemData, pos, 1);
|
||||
targetPlayer.getScene().addEntity(entity);
|
||||
}
|
||||
} else {
|
||||
EntityItem entity = new EntityItem(targetPlayer.getScene(), targetPlayer, itemData, targetPlayer.getPos().clone().addY(3f), amount);
|
||||
targetPlayer.getScene().addEntity(entity);
|
||||
}
|
||||
CommandHandler.sendMessage(sender, translate("commands.drop.success", Integer.toString(amount), Integer.toString(item)));
|
||||
}
|
||||
}
|
@ -0,0 +1,43 @@
|
||||
package emu.grasscutter.command.commands;
|
||||
|
||||
import emu.grasscutter.command.Command;
|
||||
import emu.grasscutter.command.CommandHandler;
|
||||
import emu.grasscutter.game.player.Player;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import static emu.grasscutter.utils.Language.translate;
|
||||
|
||||
@Command(label = "enterdungeon", usage = "enterdungeon <dungeon id>",
|
||||
description = "Enter a dungeon", aliases = {"dungeon"}, permission = "player.enterdungeon")
|
||||
public final class EnterDungeonCommand implements CommandHandler {
|
||||
@Override
|
||||
public void execute(Player sender, Player targetPlayer, List<String> args) {
|
||||
if (targetPlayer == null) {
|
||||
CommandHandler.sendMessage(null, translate("commands.execution.need_target"));
|
||||
return;
|
||||
}
|
||||
|
||||
if (args.size() < 1) {
|
||||
CommandHandler.sendMessage(sender, translate("commands.enter_dungeon.usage"));
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
int dungeonId = Integer.parseInt(args.get(0));
|
||||
if (dungeonId == targetPlayer.getSceneId()) {
|
||||
CommandHandler.sendMessage(sender, translate("commands.enter_dungeon.in_dungeon_error"));
|
||||
return;
|
||||
}
|
||||
|
||||
boolean result = targetPlayer.getServer().getDungeonManager().enterDungeon(targetPlayer.getSession().getPlayer(), 0, dungeonId);
|
||||
CommandHandler.sendMessage(sender, translate("commands.enter_dungeon.changed", dungeonId));
|
||||
|
||||
if (!result) {
|
||||
CommandHandler.sendMessage(sender, translate("commands.enter_dungeon.not_found_error"));
|
||||
}
|
||||
} catch (Exception e) {
|
||||
CommandHandler.sendMessage(sender, translate("commands.enter_dungeon.usage"));
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,170 @@
|
||||
package emu.grasscutter.command.commands;
|
||||
|
||||
import emu.grasscutter.Grasscutter;
|
||||
import emu.grasscutter.command.Command;
|
||||
import emu.grasscutter.command.CommandHandler;
|
||||
import emu.grasscutter.data.GameData;
|
||||
import emu.grasscutter.data.def.AvatarData;
|
||||
import emu.grasscutter.data.def.ItemData;
|
||||
import emu.grasscutter.game.avatar.Avatar;
|
||||
import emu.grasscutter.game.inventory.GameItem;
|
||||
import emu.grasscutter.game.inventory.ItemType;
|
||||
import emu.grasscutter.game.player.Player;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
import static emu.grasscutter.utils.Language.translate;
|
||||
|
||||
@Command(label = "giveall", usage = "giveall [amount]",
|
||||
description = "Gives all items", aliases = {"givea"}, permission = "player.giveall", threading = true)
|
||||
public final class GiveAllCommand implements CommandHandler {
|
||||
|
||||
@Override
|
||||
public void execute(Player sender, Player targetPlayer, List<String> args) {
|
||||
if (targetPlayer == null) {
|
||||
CommandHandler.sendMessage(sender, translate("commands.execution.need_target"));
|
||||
return;
|
||||
}
|
||||
int amount = 99999;
|
||||
|
||||
switch (args.size()) {
|
||||
case 0:
|
||||
break;
|
||||
case 1: // [amount]
|
||||
try {
|
||||
amount = Integer.parseInt(args.get(0));
|
||||
} catch (NumberFormatException ignored) {
|
||||
CommandHandler.sendMessage(sender, translate("commands.generic.invalid.amount"));
|
||||
return;
|
||||
}
|
||||
break;
|
||||
default: // invalid
|
||||
CommandHandler.sendMessage(sender, translate("commands.giveAll.usage"));
|
||||
return;
|
||||
}
|
||||
|
||||
this.giveAllItems(targetPlayer, amount);
|
||||
CommandHandler.sendMessage(sender, translate("commands.giveAll.success", targetPlayer.getNickname()));
|
||||
}
|
||||
|
||||
public void giveAllItems(Player player, int amount) {
|
||||
CommandHandler.sendMessage(player, translate("commands.giveAll.started"));
|
||||
|
||||
for (AvatarData avatarData: GameData.getAvatarDataMap().values()) {
|
||||
//Exclude test avatar
|
||||
if (isTestAvatar(avatarData.getId())) continue;
|
||||
|
||||
Avatar avatar = new Avatar(avatarData);
|
||||
avatar.setLevel(90);
|
||||
avatar.setPromoteLevel(6);
|
||||
for(int i = 1;i <= 6;++i){
|
||||
avatar.getTalentIdList().add((avatar.getAvatarId()-10000000)*10+i);
|
||||
}
|
||||
// This will handle stats and talents
|
||||
avatar.recalcStats();
|
||||
player.addAvatar(avatar);
|
||||
}
|
||||
|
||||
//some test items
|
||||
List<GameItem> itemList = new ArrayList<>();
|
||||
for (ItemData itemdata: GameData.getItemDataMap().values()) {
|
||||
//Exclude test item
|
||||
if (isTestItem(itemdata.getId())) continue;
|
||||
|
||||
if (itemdata.isEquip()) {
|
||||
if (itemdata.getItemType() == ItemType.ITEM_WEAPON) {
|
||||
for (int i = 0; i < 5; ++i) {
|
||||
GameItem item = new GameItem(itemdata);
|
||||
item.setLevel(90);
|
||||
item.setPromoteLevel(6);
|
||||
item.setRefinement(4);
|
||||
itemList.add(item);
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
GameItem item = new GameItem(itemdata);
|
||||
item.setCount(amount);
|
||||
itemList.add(item);
|
||||
}
|
||||
}
|
||||
int packetNum = 10;
|
||||
int itemLength = itemList.size();
|
||||
int number = itemLength / packetNum;
|
||||
int remainder = itemLength % packetNum;
|
||||
int offset = 0;
|
||||
for (int i = 0; i < packetNum; ++i) {
|
||||
if (remainder > 0) {
|
||||
player.getInventory().addItems(itemList.subList(i * number + offset, (i + 1) * number + offset + 1));
|
||||
--remainder;
|
||||
++offset;
|
||||
}
|
||||
else {
|
||||
player.getInventory().addItems(itemList.subList(i * number + offset, (i + 1) * number + offset));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public boolean isTestAvatar(int avatarId) {
|
||||
return avatarId < 10000002 || avatarId >= 11000000;
|
||||
}
|
||||
|
||||
public boolean isTestItem(int itemId) {
|
||||
for (Range range: testItemRanges) {
|
||||
if (range.check(itemId)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return testItemsList.contains(itemId);
|
||||
}
|
||||
|
||||
static class Range {
|
||||
private final int min, max;
|
||||
|
||||
public Range(int min, int max) {
|
||||
if(min > max){
|
||||
min ^= max;
|
||||
max ^= min;
|
||||
min ^= max;
|
||||
}
|
||||
|
||||
this.min = min;
|
||||
this.max = max;
|
||||
}
|
||||
|
||||
public boolean check(int value) {
|
||||
return value >= this.min && value <= this.max;
|
||||
}
|
||||
}
|
||||
|
||||
private static final Range[] testItemRanges = new Range[] {
|
||||
new Range(106, 139),
|
||||
new Range(1000, 1099),
|
||||
new Range(2001, 3022),
|
||||
new Range(23300, 23340),
|
||||
new Range(23383, 23385),
|
||||
new Range(78310, 78554),
|
||||
new Range(99310, 99554),
|
||||
new Range(100001, 100187),
|
||||
new Range(100210, 100214),
|
||||
new Range(100303, 100398),
|
||||
new Range(100414, 100425),
|
||||
new Range(100454, 103008),
|
||||
new Range(109000, 109492),
|
||||
new Range(115001, 118004),
|
||||
new Range(141001, 141072),
|
||||
new Range(220050, 221016),
|
||||
};
|
||||
private static final Integer[] testItemsIds = new Integer[] {
|
||||
210, 211, 314, 315, 317, 1005, 1007, 1105, 1107, 1201, 1202,10366,
|
||||
101212, 11411, 11506, 11507, 11508, 12505, 12506, 12508, 12509, 13503,
|
||||
13506, 14411, 14503, 14505, 14508, 15411, 15504, 15505, 15506, 15508,
|
||||
20001, 10002, 10003, 10004, 10005, 10006, 10008,100231,100232,100431,
|
||||
101689,105001,105004, 106000,106001,108000,110000
|
||||
};
|
||||
|
||||
private static final Collection<Integer> testItemsList = Arrays.asList(testItemsIds);
|
||||
|
||||
}
|
||||
|
@ -0,0 +1,93 @@
|
||||
package emu.grasscutter.command.commands;
|
||||
|
||||
import emu.grasscutter.Grasscutter;
|
||||
import emu.grasscutter.command.Command;
|
||||
import emu.grasscutter.command.CommandHandler;
|
||||
import emu.grasscutter.data.GameData;
|
||||
import emu.grasscutter.data.def.ItemData;
|
||||
import emu.grasscutter.game.inventory.GameItem;
|
||||
import emu.grasscutter.game.inventory.ItemType;
|
||||
import emu.grasscutter.game.player.Player;
|
||||
import emu.grasscutter.game.props.ActionReason;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import static emu.grasscutter.utils.Language.translate;
|
||||
|
||||
@Command(label = "giveart", usage = "giveart <artifactId> <mainPropId> [<appendPropId>[,<times>]]... [level]", description = "Gives the player a specified artifact", aliases = {"gart"}, permission = "player.giveart")
|
||||
public final class GiveArtifactCommand implements CommandHandler {
|
||||
@Override
|
||||
public void execute(Player sender, Player targetPlayer, List<String> args) {
|
||||
if (targetPlayer == null) {
|
||||
CommandHandler.sendMessage(sender, translate("commands.execution.need_target"));
|
||||
return;
|
||||
}
|
||||
|
||||
if (args.size() < 2) {
|
||||
CommandHandler.sendMessage(sender, translate("commands.giveArtifact.usage"));
|
||||
return;
|
||||
}
|
||||
|
||||
int itemId;
|
||||
try {
|
||||
itemId = Integer.parseInt(args.remove(0));
|
||||
} catch (NumberFormatException ignored) {
|
||||
CommandHandler.sendMessage(sender, translate("commands.giveArtifact.id_error"));
|
||||
return;
|
||||
}
|
||||
ItemData itemData = GameData.getItemDataMap().get(itemId);
|
||||
if (itemData.getItemType() != ItemType.ITEM_RELIQUARY) {
|
||||
CommandHandler.sendMessage(sender, translate("commands.giveArtifact.id_error"));
|
||||
return;
|
||||
}
|
||||
|
||||
int mainPropId;
|
||||
try {
|
||||
mainPropId = Integer.parseInt(args.remove(0));
|
||||
} catch (NumberFormatException ignored) {
|
||||
CommandHandler.sendMessage(sender, translate("commands.generic.execution.argument_error"));
|
||||
return;
|
||||
}
|
||||
|
||||
int level = 1;
|
||||
try {
|
||||
int last = Integer.parseInt(args.get(args.size()-1));
|
||||
if (last > 0 && last < 22) { // Luckily appendPropIds aren't in the range of [1,21]
|
||||
level = last;
|
||||
args.remove(args.size()-1);
|
||||
}
|
||||
} catch (NumberFormatException ignored) { // Could be a stat,times string so no need to panic
|
||||
}
|
||||
|
||||
List<Integer> appendPropIdList = new ArrayList<>();
|
||||
try {
|
||||
args.forEach(it -> {
|
||||
String[] arr;
|
||||
int n = 1;
|
||||
if ((arr = it.split(",")).length == 2) {
|
||||
it = arr[0];
|
||||
n = Integer.parseInt(arr[1]);
|
||||
if (n > 200) {
|
||||
n = 200;
|
||||
}
|
||||
}
|
||||
appendPropIdList.addAll(Collections.nCopies(n, Integer.parseInt(it)));
|
||||
});
|
||||
} catch (Exception ignored) {
|
||||
CommandHandler.sendMessage(sender, translate("commands.execution.argument_error"));
|
||||
return;
|
||||
}
|
||||
|
||||
GameItem item = new GameItem(itemData);
|
||||
item.setLevel(level);
|
||||
item.setMainPropId(mainPropId);
|
||||
item.getAppendPropIdList().clear();
|
||||
item.getAppendPropIdList().addAll(appendPropIdList);
|
||||
targetPlayer.getInventory().addItem(item, ActionReason.SubfieldDrop);
|
||||
|
||||
CommandHandler.sendMessage(sender, translate("commands.giveArtifact.success", Integer.toString(itemId), Integer.toString(targetPlayer.getUid())));
|
||||
}
|
||||
}
|
||||
|
@ -3,84 +3,73 @@ 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.GameData;
|
||||
import emu.grasscutter.data.def.AvatarData;
|
||||
import emu.grasscutter.game.GenshinPlayer;
|
||||
import emu.grasscutter.game.avatar.GenshinAvatar;
|
||||
import emu.grasscutter.game.avatar.Avatar;
|
||||
import emu.grasscutter.game.player.Player;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@Command(label = "givechar", usage = "givechar <playerId> <avatarId> [level]",
|
||||
import static emu.grasscutter.utils.Language.translate;
|
||||
|
||||
@Command(label = "givechar", usage = "givechar <avatarId> [level]",
|
||||
description = "Gives the player a specified character", aliases = {"givec"}, permission = "player.givechar")
|
||||
public final class GiveCharCommand implements CommandHandler {
|
||||
|
||||
@Override
|
||||
public void execute(GenshinPlayer sender, List<String> args) {
|
||||
int target, avatarId, level = 1, ascension;
|
||||
|
||||
if (sender == null && args.size() < 2) {
|
||||
CommandHandler.sendMessage(null, "Usage: givechar <player> <itemId|itemName> [amount]");
|
||||
public void execute(Player sender, Player targetPlayer, List<String> args) {
|
||||
if (targetPlayer == null) {
|
||||
CommandHandler.sendMessage(sender, translate("commands.execution.need_target"));
|
||||
return;
|
||||
}
|
||||
|
||||
int avatarId;
|
||||
int level = 1;
|
||||
|
||||
switch (args.size()) {
|
||||
default:
|
||||
CommandHandler.sendMessage(sender, "Usage: givechar <player> <avatarId> [level]");
|
||||
return;
|
||||
case 2:
|
||||
try {
|
||||
target = Integer.parseInt(args.get(0));
|
||||
if (Grasscutter.getGameServer().getPlayerByUid(target) == null && sender != null) {
|
||||
target = sender.getUid();
|
||||
level = Integer.parseInt(args.get(1));
|
||||
avatarId = Integer.parseInt(args.get(0));
|
||||
} else {
|
||||
avatarId = Integer.parseInt(args.get(1));
|
||||
}
|
||||
level = Integer.parseInt(args.get(1));
|
||||
} catch (NumberFormatException ignored) {
|
||||
// TODO: Parse from avatar name using GM Handbook.
|
||||
CommandHandler.sendMessage(sender, "Invalid avatar or player ID.");
|
||||
CommandHandler.sendMessage(sender, translate("commands.execution.invalid.avatarLevel"));
|
||||
return;
|
||||
}
|
||||
break;
|
||||
case 3:
|
||||
} // Cheeky fall-through to parse first argument too
|
||||
case 1:
|
||||
try {
|
||||
target = Integer.parseInt(args.get(0));
|
||||
if (Grasscutter.getGameServer().getPlayerByUid(target) == null) {
|
||||
CommandHandler.sendMessage(sender, "Invalid player ID.");
|
||||
return;
|
||||
}
|
||||
|
||||
avatarId = Integer.parseInt(args.get(1));
|
||||
level = Integer.parseInt(args.get(2));
|
||||
avatarId = Integer.parseInt(args.get(0));
|
||||
} catch (NumberFormatException ignored) {
|
||||
// TODO: Parse from avatar name using GM Handbook.
|
||||
CommandHandler.sendMessage(sender, "Invalid avatar or player ID.");
|
||||
CommandHandler.sendMessage(sender, translate("commands.execution.invalid.avatarId"));
|
||||
return;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
CommandHandler.sendMessage(sender, translate("commands.giveChar.usage"));
|
||||
return;
|
||||
}
|
||||
|
||||
GenshinPlayer targetPlayer = Grasscutter.getGameServer().getPlayerByUid(target);
|
||||
if (targetPlayer == null) {
|
||||
CommandHandler.sendMessage(sender, "Player not found.");
|
||||
AvatarData avatarData = GameData.getAvatarDataMap().get(avatarId);
|
||||
if (avatarData == null) {
|
||||
CommandHandler.sendMessage(sender, translate("commands.execution.invalid.avatarId"));
|
||||
return;
|
||||
}
|
||||
|
||||
AvatarData avatarData = GenshinData.getAvatarDataMap().get(avatarId);
|
||||
if (avatarData == null) {
|
||||
CommandHandler.sendMessage(sender, "Invalid avatar id.");
|
||||
// Check level.
|
||||
if (level > 90) {
|
||||
CommandHandler.sendMessage(sender, translate("commands.execution.invalid.avatarLevel"));
|
||||
return;
|
||||
}
|
||||
|
||||
// Calculate ascension level.
|
||||
int ascension;
|
||||
if (level <= 40) {
|
||||
ascension = (int) Math.ceil(level / 20f);
|
||||
} else {
|
||||
ascension = (int) Math.ceil(level / 10f) - 3;
|
||||
}
|
||||
|
||||
GenshinAvatar avatar = new GenshinAvatar(avatarId);
|
||||
Avatar avatar = new Avatar(avatarId);
|
||||
avatar.setLevel(level);
|
||||
avatar.setPromoteLevel(ascension);
|
||||
|
||||
@ -88,6 +77,6 @@ public final class GiveCharCommand implements CommandHandler {
|
||||
avatar.recalcStats();
|
||||
|
||||
targetPlayer.addAvatar(avatar);
|
||||
CommandHandler.sendMessage(sender, String.format("Given %s to %s.", avatarId, target));
|
||||
CommandHandler.sendMessage(sender, translate("commands.giveChar.given", Integer.toString(avatarId), Integer.toString(level), Integer.toString(targetPlayer.getUid())));
|
||||
}
|
||||
}
|
||||
|
@ -3,111 +3,173 @@ 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.GameData;
|
||||
import emu.grasscutter.data.def.ItemData;
|
||||
import emu.grasscutter.game.GenshinPlayer;
|
||||
import emu.grasscutter.game.inventory.GenshinItem;
|
||||
import emu.grasscutter.game.inventory.GameItem;
|
||||
import emu.grasscutter.game.inventory.ItemType;
|
||||
import emu.grasscutter.game.player.Player;
|
||||
import emu.grasscutter.game.props.ActionReason;
|
||||
import emu.grasscutter.server.packet.send.PacketItemAddHintNotify;
|
||||
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.regex.Pattern;
|
||||
import java.util.regex.Matcher;
|
||||
|
||||
@Command(label = "give", usage = "give [player] <itemId|itemName> [amount]",
|
||||
description = "Gives an item to you or the specified player", aliases = {"g", "item", "giveitem"}, permission = "player.give")
|
||||
import static emu.grasscutter.utils.Language.translate;
|
||||
|
||||
@Command(label = "give", usage = "give <itemId|itemName> [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 {
|
||||
Pattern lvlRegex = Pattern.compile("l(?:vl?)?(\\d+)"); // Java is a joke of a proglang that doesn't have raw string literals
|
||||
Pattern refineRegex = Pattern.compile("r(\\d+)");
|
||||
Pattern amountRegex = Pattern.compile("((?<=x)\\d+|\\d+(?=x)(?!x\\d))");
|
||||
|
||||
private int matchIntOrNeg(Pattern pattern, String arg) {
|
||||
Matcher match = pattern.matcher(arg);
|
||||
if (match.find()) {
|
||||
return Integer.parseInt(match.group(1)); // This should be exception-safe as only \d+ can be passed to it (i.e. non-empty string of pure digits)
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void execute(GenshinPlayer sender, List<String> args) {
|
||||
int target, item, amount = 1;
|
||||
|
||||
if (sender == null && args.size() < 2) {
|
||||
CommandHandler.sendMessage(null, "Usage: give <player> <itemId|itemName> [amount]");
|
||||
public void execute(Player sender, Player targetPlayer, List<String> args) {
|
||||
if (targetPlayer == null) {
|
||||
CommandHandler.sendMessage(sender, translate("commands.execution.need_target"));
|
||||
return;
|
||||
}
|
||||
int item;
|
||||
int lvl = 1;
|
||||
int amount = 1;
|
||||
int refinement = 0;
|
||||
|
||||
for (int i = args.size()-1; i>=0; i--) { // Reverse iteration as we are deleting elements
|
||||
String arg = args.get(i).toLowerCase();
|
||||
boolean deleteArg = false;
|
||||
int argNum;
|
||||
if ((argNum = matchIntOrNeg(lvlRegex, arg)) != -1) {
|
||||
lvl = argNum;
|
||||
deleteArg = true;
|
||||
}
|
||||
if ((argNum = matchIntOrNeg(refineRegex, arg)) != -1) {
|
||||
refinement = argNum;
|
||||
deleteArg = true;
|
||||
}
|
||||
if ((argNum = matchIntOrNeg(amountRegex, arg)) != -1) {
|
||||
amount = argNum;
|
||||
deleteArg = true;
|
||||
}
|
||||
if (deleteArg) {
|
||||
args.remove(i);
|
||||
}
|
||||
}
|
||||
|
||||
switch (args.size()) {
|
||||
default: // *No args*
|
||||
CommandHandler.sendMessage(sender, "Usage: give [player] <itemId|itemName> [amount]");
|
||||
return;
|
||||
case 4: // <itemId|itemName> [amount] [level] [refinement]
|
||||
try {
|
||||
refinement = Integer.parseInt(args.get(3));
|
||||
} catch (NumberFormatException ignored) {
|
||||
CommandHandler.sendMessage(sender, translate("commands.generic.invalid.itemRefinement"));
|
||||
return;
|
||||
} // Fallthrough
|
||||
case 3: // <itemId|itemName> [amount] [level]
|
||||
try {
|
||||
lvl = Integer.parseInt(args.get(2));
|
||||
} catch (NumberFormatException ignored) {
|
||||
CommandHandler.sendMessage(sender, translate("commands.generic.invalid.itemLevel"));
|
||||
return;
|
||||
} // Fallthrough
|
||||
case 2: // <itemId|itemName> [amount]
|
||||
try {
|
||||
amount = Integer.parseInt(args.get(1));
|
||||
} catch (NumberFormatException ignored) {
|
||||
CommandHandler.sendMessage(sender, translate("commands.generic.invalid.amount"));
|
||||
return;
|
||||
} // Fallthrough
|
||||
case 1: // <itemId|itemName>
|
||||
try {
|
||||
item = Integer.parseInt(args.get(0));
|
||||
target = sender.getUid();
|
||||
} catch (NumberFormatException ignored) {
|
||||
// TODO: Parse from item name using GM Handbook.
|
||||
CommandHandler.sendMessage(sender, "Invalid item id.");
|
||||
return;
|
||||
}
|
||||
break;
|
||||
case 2: // <itemId|itemName> [amount] | [player] <itemId|itemName>
|
||||
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));
|
||||
} else {
|
||||
item = Integer.parseInt(args.get(1));
|
||||
}
|
||||
} catch (NumberFormatException ignored) {
|
||||
// TODO: Parse from item name using GM Handbook.
|
||||
CommandHandler.sendMessage(sender, "Invalid item or player ID.");
|
||||
return;
|
||||
}
|
||||
break;
|
||||
case 3: // [player] <itemId|itemName> [amount]
|
||||
try {
|
||||
target = Integer.parseInt(args.get(0));
|
||||
|
||||
if (Grasscutter.getGameServer().getPlayerByUid(target) == null) {
|
||||
CommandHandler.sendMessage(sender, "Invalid player ID.");
|
||||
return;
|
||||
}
|
||||
|
||||
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.");
|
||||
CommandHandler.sendMessage(sender, translate("commands.generic.invalid.itemId"));
|
||||
return;
|
||||
}
|
||||
break;
|
||||
default: // *No args*
|
||||
CommandHandler.sendMessage(sender, translate("commands.give.usage"));
|
||||
return;
|
||||
}
|
||||
|
||||
GenshinPlayer targetPlayer = Grasscutter.getGameServer().getPlayerByUid(target);
|
||||
|
||||
if (targetPlayer == null) {
|
||||
CommandHandler.sendMessage(sender, "Player not found.");
|
||||
return;
|
||||
}
|
||||
|
||||
ItemData itemData = GenshinData.getItemDataMap().get(item);
|
||||
ItemData itemData = GameData.getItemDataMap().get(item);
|
||||
if (itemData == null) {
|
||||
CommandHandler.sendMessage(sender, "Invalid item id.");
|
||||
CommandHandler.sendMessage(sender, translate("commands.generic.invalid.itemId"));
|
||||
return;
|
||||
}
|
||||
if (refinement != 0) {
|
||||
if (itemData.getItemType() == ItemType.ITEM_WEAPON) {
|
||||
if (refinement < 1 || refinement > 5) {
|
||||
CommandHandler.sendMessage(sender, translate("commands.give.refinement_must_between_1_and_5"));
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
CommandHandler.sendMessage(sender, translate("commands.give.refinement_only_applicable_weapons"));
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
this.item(targetPlayer, itemData, amount);
|
||||
this.item(targetPlayer, itemData, amount, lvl, refinement);
|
||||
|
||||
CommandHandler.sendMessage(sender, String.format("Given %s of %s to %s.", amount, item, target));
|
||||
if (!itemData.isEquip()) {
|
||||
CommandHandler.sendMessage(sender, translate("commands.give.given", Integer.toString(amount), Integer.toString(item), Integer.toString(targetPlayer.getUid())));
|
||||
} else if (itemData.getItemType() == ItemType.ITEM_WEAPON) {
|
||||
CommandHandler.sendMessage(sender, translate("commands.give.given_with_level_and_refinement", Integer.toString(item), Integer.toString(lvl), Integer.toString(refinement), Integer.toString(amount), Integer.toString(targetPlayer.getUid())));
|
||||
} else {
|
||||
CommandHandler.sendMessage(sender, translate("commands.give.given_level", Integer.toString(item), Integer.toString(lvl), Integer.toString(amount)));
|
||||
}
|
||||
}
|
||||
|
||||
private void item(GenshinPlayer player, ItemData itemData, int amount) {
|
||||
private void item(Player player, ItemData itemData, int amount, int lvl, int refinement) {
|
||||
if (itemData.isEquip()) {
|
||||
List<GenshinItem> items = new LinkedList<>();
|
||||
List<GameItem> items = new LinkedList<>();
|
||||
for (int i = 0; i < amount; i++) {
|
||||
items.add(new GenshinItem(itemData));
|
||||
GameItem item = new GameItem(itemData);
|
||||
if (item.isEquipped()) {
|
||||
// check item max level
|
||||
if (item.getItemType() == ItemType.ITEM_WEAPON) {
|
||||
if (lvl > 90) lvl = 90;
|
||||
} else {
|
||||
if (lvl > 21) lvl = 21;
|
||||
}
|
||||
}
|
||||
item.setCount(amount);
|
||||
item.setLevel(lvl);
|
||||
if (lvl > 80) {
|
||||
item.setPromoteLevel(6);
|
||||
} else if (lvl > 70) {
|
||||
item.setPromoteLevel(5);
|
||||
} else if (lvl > 60) {
|
||||
item.setPromoteLevel(4);
|
||||
} else if (lvl > 50) {
|
||||
item.setPromoteLevel(3);
|
||||
} else if (lvl > 40) {
|
||||
item.setPromoteLevel(2);
|
||||
} else if (lvl > 20) {
|
||||
item.setPromoteLevel(1);
|
||||
}
|
||||
if (item.getItemType() == ItemType.ITEM_WEAPON) {
|
||||
if (refinement > 0) {
|
||||
item.setRefinement(refinement - 1);
|
||||
} else {
|
||||
item.setRefinement(0);
|
||||
}
|
||||
}
|
||||
items.add(item);
|
||||
}
|
||||
player.getInventory().addItems(items);
|
||||
player.sendPacket(new PacketItemAddHintNotify(items, ActionReason.SubfieldDrop));
|
||||
player.getInventory().addItems(items, ActionReason.SubfieldDrop);
|
||||
} else {
|
||||
GenshinItem genshinItem = new GenshinItem(itemData);
|
||||
genshinItem.setCount(amount);
|
||||
player.getInventory().addItem(genshinItem);
|
||||
player.sendPacket(new PacketItemAddHintNotify(genshinItem, ActionReason.SubfieldDrop));
|
||||
GameItem item = new GameItem(itemData);
|
||||
item.setCount(amount);
|
||||
player.getInventory().addItem(item, ActionReason.SubfieldDrop);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1,22 +1,42 @@
|
||||
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.player.Player;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@Command(label = "godmode", usage = "godmode [playerId]",
|
||||
description = "Prevents you from taking damage", permission = "player.godmode")
|
||||
import static emu.grasscutter.utils.Language.translate;
|
||||
|
||||
@Command(label = "godmode", usage = "godmode [on|off|toggle]",
|
||||
description = "Prevents you from taking damage. Defaults to toggle.", permission = "player.godmode")
|
||||
public final class GodModeCommand implements CommandHandler {
|
||||
|
||||
@Override
|
||||
public void execute(GenshinPlayer sender, List<String> args) {
|
||||
if (sender == null) {
|
||||
CommandHandler.sendMessage(null, "Run this command in-game.");
|
||||
return; // TODO: toggle player's godmode statue from console or other players
|
||||
public void execute(Player sender, Player targetPlayer, List<String> args) {
|
||||
if (targetPlayer == null) {
|
||||
CommandHandler.sendMessage(sender, translate("commands.execution.need_target"));
|
||||
return;
|
||||
}
|
||||
sender.setGodmode(!sender.inGodmode());
|
||||
sender.dropMessage("Godmode is now " + (sender.inGodmode() ? "enabled" : "disabled") + ".");
|
||||
|
||||
boolean enabled = !targetPlayer.inGodmode();
|
||||
if (args.size() == 1) {
|
||||
switch (args.get(0).toLowerCase()) {
|
||||
case "on":
|
||||
enabled = true;
|
||||
break;
|
||||
case "off":
|
||||
enabled = false;
|
||||
break;
|
||||
case "toggle":
|
||||
break; // Already toggled
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
targetPlayer.setGodmode(enabled);
|
||||
CommandHandler.sendMessage(sender, translate("commands.godmode.success", (enabled ? translate("commands.status.enabled") : translate("commands.status.disabled")), targetPlayer.getNickname()));
|
||||
}
|
||||
}
|
||||
|
@ -2,25 +2,26 @@ package emu.grasscutter.command.commands;
|
||||
|
||||
import emu.grasscutter.command.Command;
|
||||
import emu.grasscutter.command.CommandHandler;
|
||||
import emu.grasscutter.game.GenshinPlayer;
|
||||
import emu.grasscutter.game.player.Player;
|
||||
import emu.grasscutter.game.props.FightProperty;
|
||||
import emu.grasscutter.server.packet.send.PacketAvatarFightPropUpdateNotify;
|
||||
import emu.grasscutter.server.packet.send.PacketAvatarLifeStateChangeNotify;
|
||||
import emu.grasscutter.server.packet.send.PacketEntityFightPropUpdateNotify;
|
||||
import emu.grasscutter.server.packet.send.PacketLifeStateChangeNotify;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@Command(label = "heal", usage = "heal|h",
|
||||
description = "Heal all characters in your current team.", aliases = {"h"}, permission = "player.heal")
|
||||
public class HealCommand implements CommandHandler {
|
||||
import static emu.grasscutter.utils.Language.translate;
|
||||
|
||||
@Command(label = "heal", usage = "heal|h", aliases = {"h"},
|
||||
description = "Heal all characters in your current team.", permission = "player.heal")
|
||||
public final class HealCommand implements CommandHandler {
|
||||
@Override
|
||||
public void execute(GenshinPlayer sender, List<String> args) {
|
||||
if (sender == null) {
|
||||
CommandHandler.sendMessage(null, "Run this command in-game.");
|
||||
public void execute(Player sender, Player targetPlayer, List<String> args) {
|
||||
if (targetPlayer == null) {
|
||||
CommandHandler.sendMessage(sender, translate("commands.execution.need_target"));
|
||||
return;
|
||||
}
|
||||
sender.getTeamManager().getActiveTeam().forEach(entity -> {
|
||||
|
||||
targetPlayer.getTeamManager().getActiveTeam().forEach(entity -> {
|
||||
boolean isAlive = entity.isAlive();
|
||||
entity.setFightProperty(
|
||||
FightProperty.FIGHT_PROP_CUR_HP,
|
||||
@ -31,6 +32,6 @@ public class HealCommand implements CommandHandler {
|
||||
entity.getWorld().broadcastPacket(new PacketAvatarLifeStateChangeNotify(entity.getAvatar()));
|
||||
}
|
||||
});
|
||||
CommandHandler.sendMessage(sender, "All characters are healed.");
|
||||
CommandHandler.sendMessage(sender, translate("commands.heal.success"));
|
||||
}
|
||||
}
|
||||
|
@ -1,18 +1,21 @@
|
||||
package emu.grasscutter.command.commands;
|
||||
|
||||
import emu.grasscutter.Grasscutter;
|
||||
import emu.grasscutter.command.Command;
|
||||
import emu.grasscutter.command.CommandHandler;
|
||||
import emu.grasscutter.command.CommandMap;
|
||||
import emu.grasscutter.game.GenshinPlayer;
|
||||
import emu.grasscutter.game.player.Player;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
import static emu.grasscutter.utils.Language.translate;
|
||||
|
||||
@Command(label = "help", usage = "help [command]",
|
||||
description = "Sends the help message or shows information about a specified command")
|
||||
public final class HelpCommand implements CommandHandler {
|
||||
|
||||
@Override
|
||||
public void execute(GenshinPlayer player, List<String> args) {
|
||||
public void execute(Player player, Player targetPlayer, List<String> args) {
|
||||
if (args.size() < 1) {
|
||||
HashMap<String, CommandHandler> handlers = CommandMap.getInstance().getHandlers();
|
||||
List<Command> annotations = new ArrayList<>();
|
||||
@ -30,16 +33,16 @@ public final class HelpCommand implements CommandHandler {
|
||||
} else {
|
||||
String command = args.get(0);
|
||||
CommandHandler handler = CommandMap.getInstance().getHandler(command);
|
||||
StringBuilder builder = new StringBuilder(player == null ? "\nHelp - " : "Help - ").append(command).append(": \n");
|
||||
StringBuilder builder = new StringBuilder(player == null ? "\n" + translate("commands.status.help") + " - " : translate("commands.status.help") + " - ").append(command).append(": \n");
|
||||
if (handler == null) {
|
||||
builder.append("No command found.");
|
||||
builder.append(translate("commands.generic.command_exist_error"));
|
||||
} else {
|
||||
Command annotation = handler.getClass().getAnnotation(Command.class);
|
||||
|
||||
builder.append(" ").append(annotation.description()).append("\n");
|
||||
builder.append(" Usage: ").append(annotation.usage());
|
||||
builder.append(translate("commands.help.usage")).append(annotation.usage());
|
||||
if (annotation.aliases().length >= 1) {
|
||||
builder.append("\n").append(" Aliases: ");
|
||||
builder.append("\n").append(translate("commands.help.aliases"));
|
||||
for (String alias : annotation.aliases()) {
|
||||
builder.append(alias).append(" ");
|
||||
}
|
||||
@ -53,15 +56,15 @@ public final class HelpCommand implements CommandHandler {
|
||||
}
|
||||
}
|
||||
|
||||
void SendAllHelpMessage(GenshinPlayer player, List<Command> annotations) {
|
||||
void SendAllHelpMessage(Player player, List<Command> annotations) {
|
||||
if (player == null) {
|
||||
StringBuilder builder = new StringBuilder("\nAvailable commands:\n");
|
||||
StringBuilder builder = new StringBuilder("\n" + translate("commands.help.available_commands") + "\n");
|
||||
annotations.forEach(annotation -> {
|
||||
builder.append(annotation.label()).append("\n");
|
||||
builder.append(" ").append(annotation.description()).append("\n");
|
||||
builder.append(" Usage: ").append(annotation.usage());
|
||||
builder.append(translate("commands.help.usage")).append(annotation.usage());
|
||||
if (annotation.aliases().length >= 1) {
|
||||
builder.append("\n").append(" Aliases: ");
|
||||
builder.append("\n").append(translate("commands.help.aliases"));
|
||||
for (String alias : annotation.aliases()) {
|
||||
builder.append(alias).append(" ");
|
||||
}
|
||||
@ -72,13 +75,13 @@ public final class HelpCommand implements CommandHandler {
|
||||
|
||||
CommandHandler.sendMessage(null, builder.toString());
|
||||
} else {
|
||||
CommandHandler.sendMessage(player, "Available commands:");
|
||||
CommandHandler.sendMessage(player, translate("commands.help.available_commands"));
|
||||
annotations.forEach(annotation -> {
|
||||
StringBuilder builder = new StringBuilder(annotation.label()).append("\n");
|
||||
builder.append(" ").append(annotation.description()).append("\n");
|
||||
builder.append(" Usage: ").append(annotation.usage());
|
||||
builder.append(translate("commands.help.usage")).append(annotation.usage());
|
||||
if (annotation.aliases().length >= 1) {
|
||||
builder.append("\n").append(" Aliases: ");
|
||||
builder.append("\n").append(translate("commands.help.aliases"));
|
||||
for (String alias : annotation.aliases()) {
|
||||
builder.append(alias).append(" ");
|
||||
}
|
||||
|
@ -1,30 +1,31 @@
|
||||
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.player.Player;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@Command(label = "kick", usage = "kick <player>",
|
||||
import static emu.grasscutter.utils.Language.translate;
|
||||
|
||||
@Command(label = "kick", usage = "kick",
|
||||
description = "Kicks the specified player from the server (WIP)", permission = "server.kick")
|
||||
public final class KickCommand implements CommandHandler {
|
||||
|
||||
@Override
|
||||
public void execute(GenshinPlayer sender, List<String> args) {
|
||||
int target = Integer.parseInt(args.get(0));
|
||||
|
||||
GenshinPlayer targetPlayer = Grasscutter.getGameServer().getPlayerByUid(target);
|
||||
public void execute(Player sender, Player targetPlayer, List<String> args) {
|
||||
if (targetPlayer == null) {
|
||||
CommandHandler.sendMessage(sender, "Player not found.");
|
||||
CommandHandler.sendMessage(sender, translate("commands.execution.need_target"));
|
||||
return;
|
||||
}
|
||||
|
||||
if (sender != null) {
|
||||
CommandHandler.sendMessage(sender, String.format("Player [%s:%s] has kicked player [%s:%s]", sender.getAccount().getPlayerUid(), sender.getAccount().getUsername(), target, targetPlayer.getAccount().getUsername()));
|
||||
CommandHandler.sendMessage(sender, translate("commands.kick.player_kick_player",
|
||||
Integer.toString(sender.getAccount().getPlayerUid()), sender.getAccount().getUsername(),
|
||||
Integer.toString(targetPlayer.getUid()), targetPlayer.getAccount().getUsername()));
|
||||
} else {
|
||||
CommandHandler.sendMessage(null, translate("commands.kick.server_kick_player", Integer.toString(targetPlayer.getUid()), targetPlayer.getAccount().getUsername()));
|
||||
}
|
||||
CommandHandler.sendMessage(sender, String.format("Kicking player [%s:%s]", target, targetPlayer.getAccount().getUsername()));
|
||||
|
||||
targetPlayer.getSession().close();
|
||||
}
|
||||
|
@ -3,62 +3,52 @@ 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.GenshinScene;
|
||||
import emu.grasscutter.game.entity.EntityMonster;
|
||||
import emu.grasscutter.game.entity.GameEntity;
|
||||
import emu.grasscutter.game.player.Player;
|
||||
import emu.grasscutter.game.world.Scene;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@Command(label = "killall", usage = "killall [playerUid] [sceneId]",
|
||||
import static emu.grasscutter.utils.Language.translate;
|
||||
|
||||
@Command(label = "killall", usage = "killall [sceneId]",
|
||||
description = "Kill all entities", permission = "server.killall")
|
||||
public final class KillAllCommand implements CommandHandler {
|
||||
|
||||
@Override
|
||||
public void execute(GenshinPlayer sender, List<String> args) {
|
||||
GenshinScene scene;
|
||||
GenshinPlayer genshinPlayer;
|
||||
public void execute(Player sender, Player targetPlayer, List<String> args) {
|
||||
if (targetPlayer == null) {
|
||||
CommandHandler.sendMessage(sender, translate("commands.execution.need_target"));
|
||||
return;
|
||||
}
|
||||
|
||||
Scene scene = targetPlayer.getScene();
|
||||
try {
|
||||
switch (args.size()) {
|
||||
case 0: // *No args*
|
||||
if (sender == null) {
|
||||
CommandHandler.sendMessage(null, "Usage: killall [playerUid] [sceneId]");
|
||||
return;
|
||||
}
|
||||
scene = sender.getScene();
|
||||
break;
|
||||
case 1: // [playerUid]
|
||||
genshinPlayer = Grasscutter.getGameServer().getPlayerByUid(Integer.parseInt(args.get(0)));
|
||||
if (genshinPlayer == null) {
|
||||
CommandHandler.sendMessage(sender, "Player not found or offline.");
|
||||
return;
|
||||
}
|
||||
scene = genshinPlayer.getScene();
|
||||
break;
|
||||
case 2: // [playerUid] [sceneId]
|
||||
genshinPlayer = Grasscutter.getGameServer().getPlayerByUid(Integer.parseInt(args.get(0)));
|
||||
if (genshinPlayer == null) {
|
||||
CommandHandler.sendMessage(sender, "Player not found or offline.");
|
||||
return;
|
||||
}
|
||||
GenshinScene genshinScene = sender.getWorld().getSceneById(Integer.parseInt(args.get(1)));
|
||||
if (genshinScene == null) {
|
||||
CommandHandler.sendMessage(sender, "Scene not found in player world");
|
||||
return;
|
||||
}
|
||||
scene = genshinScene;
|
||||
case 1: // [sceneId]
|
||||
scene = targetPlayer.getWorld().getSceneById(Integer.parseInt(args.get(0)));
|
||||
break;
|
||||
default:
|
||||
CommandHandler.sendMessage(sender, "Usage: killall [playerUid] [sceneId]");
|
||||
CommandHandler.sendMessage(sender, translate("commands.kill.usage"));
|
||||
return;
|
||||
}
|
||||
|
||||
scene.getEntities().values().stream()
|
||||
.filter(entity -> entity instanceof EntityMonster)
|
||||
.forEach(entity -> scene.killEntity(entity, 0));
|
||||
CommandHandler.sendMessage(sender, "Killing all monsters in scene " + scene.getId());
|
||||
} catch (NumberFormatException ignored) {
|
||||
CommandHandler.sendMessage(sender, "Invalid arguments.");
|
||||
CommandHandler.sendMessage(sender, translate("commands.execution.argument_error"));
|
||||
}
|
||||
if (scene == null) {
|
||||
CommandHandler.sendMessage(sender, translate("commands.kill.scene_not_found_in_player_world"));
|
||||
return;
|
||||
}
|
||||
|
||||
// Separate into list to avoid concurrency issue
|
||||
final Scene sceneF = scene;
|
||||
List<GameEntity> toKill = sceneF.getEntities().values().stream()
|
||||
.filter(entity -> entity instanceof EntityMonster)
|
||||
.toList();
|
||||
toKill.forEach(entity -> sceneF.killEntity(entity, 0));
|
||||
CommandHandler.sendMessage(sender, translate("commands.kill.kill_monsters_in_scene", Integer.toString(toKill.size()), Integer.toString(scene.getId())));
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,38 @@
|
||||
package emu.grasscutter.command.commands;
|
||||
|
||||
import emu.grasscutter.command.Command;
|
||||
import emu.grasscutter.command.CommandHandler;
|
||||
import emu.grasscutter.game.entity.EntityAvatar;
|
||||
import emu.grasscutter.game.player.Player;
|
||||
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;
|
||||
|
||||
import static emu.grasscutter.utils.Language.translate;
|
||||
|
||||
@Command(label = "killcharacter", usage = "killcharacter", aliases = {"suicide", "kill"},
|
||||
description = "Kills the players current character", permission = "player.killcharacter")
|
||||
public final class KillCharacterCommand implements CommandHandler {
|
||||
|
||||
@Override
|
||||
public void execute(Player sender, Player targetPlayer, List<String> args) {
|
||||
if (targetPlayer == null) {
|
||||
CommandHandler.sendMessage(sender, translate("commands.execution.need_target"));
|
||||
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, translate("commands.killCharacter.success", targetPlayer.getNickname()));
|
||||
}
|
||||
}
|
@ -3,30 +3,51 @@ 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.player.Player;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
@Command(label = "list", description = "List online players")
|
||||
public class ListCommand implements CommandHandler {
|
||||
import static emu.grasscutter.utils.Language.translate;
|
||||
|
||||
@Command(label = "list", usage = "list [uid]",
|
||||
description = "List online players", aliases = {"players"})
|
||||
public final class ListCommand implements CommandHandler {
|
||||
|
||||
@Override
|
||||
public void execute(GenshinPlayer sender, List<String> args) {
|
||||
Map<Integer, GenshinPlayer> playersMap = Grasscutter.getGameServer().getPlayers();
|
||||
public void execute(Player sender, Player targetPlayer, List<String> args) {
|
||||
Map<Integer, Player> playersMap = Grasscutter.getGameServer().getPlayers();
|
||||
boolean needUID = false;
|
||||
|
||||
CommandHandler.sendMessage(sender, String.format("There are %s player(s) online:", playersMap.size()));
|
||||
if (args.size() > 0) {
|
||||
needUID = args.get(0).equals("uid");
|
||||
}
|
||||
|
||||
CommandHandler.sendMessage(sender, translate("commands.list.success", Integer.toString(playersMap.size())));
|
||||
|
||||
if (playersMap.size() != 0) {
|
||||
StringBuilder playerSet = new StringBuilder();
|
||||
boolean finalNeedUID = needUID;
|
||||
|
||||
playersMap.values().forEach(player -> {
|
||||
playerSet.append(player.getNickname());
|
||||
|
||||
if (finalNeedUID) {
|
||||
if (sender != null) {
|
||||
playerSet.append(" <color=green>(")
|
||||
.append(player.getUid())
|
||||
.append(")</color>");
|
||||
} else {
|
||||
playerSet.append(" (")
|
||||
.append(player.getUid())
|
||||
.append(")");
|
||||
}
|
||||
}
|
||||
|
||||
for (Map.Entry<Integer, GenshinPlayer> entry : playersMap.entrySet()) {
|
||||
playerSet.append(entry.getValue().getNickname());
|
||||
playerSet.append(", ");
|
||||
}
|
||||
});
|
||||
|
||||
String players = playerSet.toString();
|
||||
|
||||
CommandHandler.sendMessage(sender, players.substring(0, players.length() - 2));
|
||||
}
|
||||
}
|
||||
|
@ -4,44 +4,50 @@ import emu.grasscutter.Grasscutter;
|
||||
import emu.grasscutter.command.Command;
|
||||
import emu.grasscutter.command.CommandHandler;
|
||||
import emu.grasscutter.game.Account;
|
||||
import emu.grasscutter.game.GenshinPlayer;
|
||||
import emu.grasscutter.game.player.Player;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@Command(label = "permission", usage = "permission <add|remove> <username> <permission>",
|
||||
import static emu.grasscutter.utils.Language.translate;
|
||||
|
||||
@Command(label = "permission", usage = "permission <add|remove> <permission>",
|
||||
description = "Grants or removes a permission for a user", permission = "*")
|
||||
public final class PermissionCommand implements CommandHandler {
|
||||
|
||||
@Override
|
||||
public void execute(GenshinPlayer sender, List<String> args) {
|
||||
if (args.size() < 3) {
|
||||
CommandHandler.sendMessage(sender, "Usage: permission <add|remove> <username> <permission>");
|
||||
public void execute(Player sender, Player targetPlayer, List<String> args) {
|
||||
if (targetPlayer == null) {
|
||||
CommandHandler.sendMessage(sender, translate("commands.execution.need_target"));
|
||||
return;
|
||||
}
|
||||
|
||||
if (args.size() != 2) {
|
||||
CommandHandler.sendMessage(sender, translate("commands.permission.usage"));
|
||||
return;
|
||||
}
|
||||
|
||||
String action = args.get(0);
|
||||
String username = args.get(1);
|
||||
String permission = args.get(2);
|
||||
String permission = args.get(1);
|
||||
|
||||
Account account = Grasscutter.getGameServer().getAccountByName(username);
|
||||
Account account = targetPlayer.getAccount();
|
||||
if (account == null) {
|
||||
CommandHandler.sendMessage(sender, "Account not found.");
|
||||
CommandHandler.sendMessage(sender, translate("commands.permission.account_error"));
|
||||
return;
|
||||
}
|
||||
|
||||
switch (action) {
|
||||
default:
|
||||
CommandHandler.sendMessage(sender, "Usage: permission <add|remove> <username> <permission>");
|
||||
CommandHandler.sendMessage(sender, translate("commands.permission.usage"));
|
||||
break;
|
||||
case "add":
|
||||
if (account.addPermission(permission)) {
|
||||
CommandHandler.sendMessage(sender, "Permission added.");
|
||||
} else CommandHandler.sendMessage(sender, "They already have this permission!");
|
||||
CommandHandler.sendMessage(sender, translate("commands.permission.add"));
|
||||
} else CommandHandler.sendMessage(sender, translate("commands.permission.has_error"));
|
||||
break;
|
||||
case "remove":
|
||||
if (account.removePermission(permission)) {
|
||||
CommandHandler.sendMessage(sender, "Permission removed.");
|
||||
} else CommandHandler.sendMessage(sender, "They don't have this permission!");
|
||||
CommandHandler.sendMessage(sender, translate("commands.permission.remove"));
|
||||
} else CommandHandler.sendMessage(sender, translate("commands.permission.not_have_error"));
|
||||
break;
|
||||
}
|
||||
|
||||
|
@ -2,21 +2,27 @@ package emu.grasscutter.command.commands;
|
||||
|
||||
import emu.grasscutter.command.Command;
|
||||
import emu.grasscutter.command.CommandHandler;
|
||||
import emu.grasscutter.game.GenshinPlayer;
|
||||
import emu.grasscutter.game.player.Player;
|
||||
import emu.grasscutter.utils.Position;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import static emu.grasscutter.utils.Language.translate;
|
||||
|
||||
@Command(label = "position", usage = "position", aliases = {"pos"},
|
||||
description = "Get coordinates.")
|
||||
public final class PositionCommand implements CommandHandler {
|
||||
|
||||
@Override
|
||||
public void execute(GenshinPlayer sender, List<String> args) {
|
||||
if (sender == null) {
|
||||
CommandHandler.sendMessage(null, "Run this command in-game.");
|
||||
public void execute(Player sender, Player targetPlayer, List<String> args) {
|
||||
if (targetPlayer == null) {
|
||||
CommandHandler.sendMessage(sender, translate("commands.execution.need_target"));
|
||||
return;
|
||||
}
|
||||
|
||||
sender.dropMessage(String.format("Coord: %.3f, %.3f, %.3f", sender.getPos().getX(), sender.getPos().getY(), sender.getPos().getZ()));
|
||||
Position pos = targetPlayer.getPos();
|
||||
CommandHandler.sendMessage(sender, translate("commands.position.success",
|
||||
Float.toString(pos.getX()), Float.toString(pos.getY()), Float.toString(pos.getZ()),
|
||||
Integer.toString(targetPlayer.getSceneId())));
|
||||
}
|
||||
}
|
||||
|
@ -3,20 +3,27 @@ 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.player.Player;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import static emu.grasscutter.utils.Language.translate;
|
||||
|
||||
@Command(label = "reload", usage = "reload",
|
||||
description = "Reload server config", permission = "server.reload")
|
||||
public final class ReloadCommand implements CommandHandler {
|
||||
|
||||
@Override
|
||||
public void execute(GenshinPlayer sender, List<String> args) {
|
||||
CommandHandler.sendMessage(sender, "Reloading config.");
|
||||
public void execute(Player sender, Player targetPlayer, List<String> args) {
|
||||
CommandHandler.sendMessage(sender, translate("commands.reload.reload_start"));
|
||||
|
||||
Grasscutter.loadConfig();
|
||||
Grasscutter.loadLanguage();
|
||||
Grasscutter.getGameServer().getGachaManager().load();
|
||||
Grasscutter.getGameServer().getDropManager().load();
|
||||
Grasscutter.getGameServer().getShopManager().load();
|
||||
Grasscutter.getDispatchServer().loadQueries();
|
||||
CommandHandler.sendMessage(sender, "Reload complete.");
|
||||
|
||||
CommandHandler.sendMessage(sender, translate("commands.reload.reload_done"));
|
||||
}
|
||||
}
|
||||
|
@ -2,41 +2,43 @@ package emu.grasscutter.command.commands;
|
||||
|
||||
import emu.grasscutter.command.Command;
|
||||
import emu.grasscutter.command.CommandHandler;
|
||||
import emu.grasscutter.game.GenshinPlayer;
|
||||
import emu.grasscutter.game.avatar.GenshinAvatar;
|
||||
import emu.grasscutter.game.avatar.Avatar;
|
||||
import emu.grasscutter.game.entity.EntityAvatar;
|
||||
import emu.grasscutter.game.player.Player;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import static emu.grasscutter.utils.Language.translate;
|
||||
|
||||
@Command(label = "resetconst", usage = "resetconst [all]",
|
||||
description = "Resets the constellation level on your current active character, will need to relog after using the command to see any changes.",
|
||||
aliases = {"resetconstellation"}, permission = "player.resetconstellation")
|
||||
public final class ResetConstCommand implements CommandHandler {
|
||||
|
||||
@Override
|
||||
public void execute(GenshinPlayer sender, List<String> args) {
|
||||
if (sender == null) {
|
||||
CommandHandler.sendMessage(null, "Run this command in-game.");
|
||||
public void execute(Player sender, Player targetPlayer, List<String> args) {
|
||||
if (targetPlayer == null) {
|
||||
CommandHandler.sendMessage(sender, translate("commands.execution.need_target"));
|
||||
return;
|
||||
}
|
||||
|
||||
if (args.size() > 0 && args.get(0).equalsIgnoreCase("all")) {
|
||||
sender.getAvatars().forEach(this::resetConstellation);
|
||||
sender.dropMessage("Reset all avatars' constellations.");
|
||||
targetPlayer.getAvatars().forEach(this::resetConstellation);
|
||||
CommandHandler.sendMessage(sender, translate("commands.resetConst.reset_all"));
|
||||
} else {
|
||||
EntityAvatar entity = sender.getTeamManager().getCurrentAvatarEntity();
|
||||
EntityAvatar entity = targetPlayer.getTeamManager().getCurrentAvatarEntity();
|
||||
if (entity == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
GenshinAvatar avatar = entity.getAvatar();
|
||||
Avatar avatar = entity.getAvatar();
|
||||
this.resetConstellation(avatar);
|
||||
|
||||
sender.dropMessage("Constellations for " + avatar.getAvatarData().getName() + " have been reset. Please relog to see changes.");
|
||||
CommandHandler.sendMessage(sender, translate("commands.resetConst.success", avatar.getAvatarData().getName()));
|
||||
}
|
||||
}
|
||||
|
||||
private void resetConstellation(GenshinAvatar avatar) {
|
||||
private void resetConstellation(Avatar avatar) {
|
||||
avatar.getTalentIdList().clear();
|
||||
avatar.setCoreProudSkillLevel(0);
|
||||
avatar.recalcStats();
|
||||
|
@ -0,0 +1,26 @@
|
||||
package emu.grasscutter.command.commands;
|
||||
|
||||
import emu.grasscutter.Grasscutter;
|
||||
import emu.grasscutter.command.Command;
|
||||
import emu.grasscutter.command.CommandHandler;
|
||||
import emu.grasscutter.game.player.Player;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import static emu.grasscutter.utils.Language.translate;
|
||||
|
||||
@Command(label = "resetshop", usage = "resetshop",
|
||||
description = "Reset target player's shop refresh time.", permission = "server.resetshop")
|
||||
public final class ResetShopLimitCommand implements CommandHandler {
|
||||
@Override
|
||||
public void execute(Player sender, Player targetPlayer, List<String> args) {
|
||||
if (targetPlayer == null) {
|
||||
CommandHandler.sendMessage(sender, translate("commands.execution.need_target"));
|
||||
return;
|
||||
}
|
||||
|
||||
targetPlayer.getShopLimit().forEach(x -> x.setNextRefreshTime(0));
|
||||
targetPlayer.save();
|
||||
CommandHandler.sendMessage(sender, translate("commands.status.success"));
|
||||
}
|
||||
}
|
@ -2,7 +2,7 @@ package emu.grasscutter.command.commands;
|
||||
|
||||
import emu.grasscutter.command.Command;
|
||||
import emu.grasscutter.command.CommandHandler;
|
||||
import emu.grasscutter.game.GenshinPlayer;
|
||||
import emu.grasscutter.game.player.Player;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@ -10,7 +10,10 @@ import java.util.List;
|
||||
public final class RestartCommand implements CommandHandler {
|
||||
|
||||
@Override
|
||||
public void execute(GenshinPlayer sender, List<String> args) {
|
||||
public void execute(Player sender, Player targetPlayer, List<String> args) {
|
||||
if (sender == null) {
|
||||
return;
|
||||
}
|
||||
sender.getSession().close();
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,200 @@
|
||||
package emu.grasscutter.command.commands;
|
||||
|
||||
import emu.grasscutter.Grasscutter;
|
||||
import emu.grasscutter.command.Command;
|
||||
import emu.grasscutter.command.CommandHandler;
|
||||
import emu.grasscutter.database.DatabaseHelper;
|
||||
import emu.grasscutter.game.mail.Mail;
|
||||
import emu.grasscutter.game.player.Player;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
|
||||
import static emu.grasscutter.utils.Language.translate;
|
||||
|
||||
@SuppressWarnings("ConstantConditions")
|
||||
@Command(label = "sendmail", usage = "sendmail <userId|all|help> [templateId]",
|
||||
description = "Sends mail to the specified user. The usage of this command changes based on it's composition state.", permission = "server.sendmail")
|
||||
public final class SendMailCommand implements CommandHandler {
|
||||
|
||||
// TODO: You should be able to do /sendmail and then just send subsequent messages until you finish
|
||||
// However, due to the current nature of the command system, I don't think this is possible without rewriting
|
||||
// the command system (again). For now this will do
|
||||
|
||||
// Key = User that is constructing the mail.
|
||||
private static final HashMap<Integer, MailBuilder> mailBeingConstructed = new HashMap<Integer, MailBuilder>();
|
||||
|
||||
// Yes this is awful and I hate it.
|
||||
@Override
|
||||
public void execute(Player sender, Player targetPlayer, List<String> args) {
|
||||
int senderId;
|
||||
if(sender != null) {
|
||||
senderId = sender.getUid();
|
||||
} else {
|
||||
senderId = -1;
|
||||
}
|
||||
|
||||
if (!mailBeingConstructed.containsKey(senderId)) {
|
||||
switch (args.size()) {
|
||||
case 1 -> {
|
||||
MailBuilder mailBuilder;
|
||||
switch (args.get(0).toLowerCase()) {
|
||||
case "help" -> {
|
||||
CommandHandler.sendMessage(sender, this.getClass().getAnnotation(Command.class).description() + "\nUsage: " + this.getClass().getAnnotation(Command.class).usage());
|
||||
return;
|
||||
}
|
||||
case "all" -> mailBuilder = new MailBuilder(true, new Mail());
|
||||
default -> {
|
||||
if (DatabaseHelper.getPlayerById(Integer.parseInt(args.get(0))) != null) {
|
||||
mailBuilder = new MailBuilder(Integer.parseInt(args.get(0)), new Mail());
|
||||
} else {
|
||||
CommandHandler.sendMessage(sender, translate("commands.sendMail.user_not_exist", args.get(0)));
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
mailBeingConstructed.put(senderId, mailBuilder);
|
||||
CommandHandler.sendMessage(sender, translate("commands.sendMail.start_composition"));
|
||||
}
|
||||
case 2 -> CommandHandler.sendMessage(sender, translate("commands.sendMail.templates"));
|
||||
default -> CommandHandler.sendMessage(sender, translate("commands.sendMail.invalid_arguments"));
|
||||
}
|
||||
} else {
|
||||
MailBuilder mailBuilder = mailBeingConstructed.get(senderId);
|
||||
|
||||
if (args.size() >= 1) {
|
||||
switch (args.get(0).toLowerCase()) {
|
||||
case "stop" -> {
|
||||
mailBeingConstructed.remove(senderId);
|
||||
CommandHandler.sendMessage(sender, translate("commands.sendMail.sendCancel"));
|
||||
return;
|
||||
}
|
||||
case "finish" -> {
|
||||
if (mailBuilder.constructionStage == 3) {
|
||||
if (!mailBuilder.sendToAll) {
|
||||
Grasscutter.getGameServer().getPlayerByUid(mailBuilder.recipient, true).sendMail(mailBuilder.mail);
|
||||
CommandHandler.sendMessage(sender, translate("commands.sendMail.send_done", Integer.toString(mailBuilder.recipient)));
|
||||
} else {
|
||||
for (Player player : DatabaseHelper.getAllPlayers()) {
|
||||
Grasscutter.getGameServer().getPlayerByUid(player.getUid(), true).sendMail(mailBuilder.mail);
|
||||
}
|
||||
CommandHandler.sendMessage(sender, translate("commands.sendMail.send_all_done"));
|
||||
}
|
||||
mailBeingConstructed.remove(senderId);
|
||||
} else {
|
||||
CommandHandler.sendMessage(sender, translate("commands.sendMail.not_composition_end", getConstructionArgs(mailBuilder.constructionStage)));
|
||||
}
|
||||
return;
|
||||
}
|
||||
case "help" -> {
|
||||
CommandHandler.sendMessage(sender, translate("commands.sendMail.please_use", getConstructionArgs(mailBuilder.constructionStage)));
|
||||
return;
|
||||
}
|
||||
default -> {
|
||||
switch (mailBuilder.constructionStage) {
|
||||
case 0 -> {
|
||||
String title = String.join(" ", args.subList(0, args.size()));
|
||||
mailBuilder.mail.mailContent.title = title;
|
||||
CommandHandler.sendMessage(sender, translate("commands.sendMail.set_title", title));
|
||||
mailBuilder.constructionStage++;
|
||||
}
|
||||
case 1 -> {
|
||||
String contents = String.join(" ", args.subList(0, args.size()));
|
||||
mailBuilder.mail.mailContent.content = contents;
|
||||
CommandHandler.sendMessage(sender, translate("commands.sendMail.set_contents", contents));
|
||||
mailBuilder.constructionStage++;
|
||||
}
|
||||
case 2 -> {
|
||||
String msgSender = String.join(" ", args.subList(0, args.size()));
|
||||
mailBuilder.mail.mailContent.sender = msgSender;
|
||||
CommandHandler.sendMessage(sender, translate("commands.sendMail.set_message_sender", msgSender));
|
||||
mailBuilder.constructionStage++;
|
||||
}
|
||||
case 3 -> {
|
||||
int item;
|
||||
int lvl = 1;
|
||||
int amount = 1;
|
||||
int refinement = 0;
|
||||
switch (args.size()) {
|
||||
case 4: // <itemId|itemName> [amount] [level] [refinement] // TODO: this requires Mail support but there's no harm leaving it here for now
|
||||
try {
|
||||
refinement = Integer.parseInt(args.get(3));
|
||||
} catch (NumberFormatException ignored) {
|
||||
CommandHandler.sendMessage(sender, translate("commands.generic.invalid.itemRefinement"));
|
||||
return;
|
||||
} // Fallthrough
|
||||
case 3: // <itemId|itemName> [amount] [level]
|
||||
try {
|
||||
lvl = Integer.parseInt(args.get(2));
|
||||
} catch (NumberFormatException ignored) {
|
||||
CommandHandler.sendMessage(sender, translate("commands.generic.invalid.itemLevel"));
|
||||
return;
|
||||
} // Fallthrough
|
||||
case 2: // <itemId|itemName> [amount]
|
||||
try {
|
||||
amount = Integer.parseInt(args.get(1));
|
||||
} catch (NumberFormatException ignored) {
|
||||
CommandHandler.sendMessage(sender, translate("commands.generic.invalid.amount"));
|
||||
return;
|
||||
} // Fallthrough
|
||||
case 1: // <itemId|itemName>
|
||||
try {
|
||||
item = Integer.parseInt(args.get(0));
|
||||
} catch (NumberFormatException ignored) {
|
||||
// TODO: Parse from item name using GM Handbook.
|
||||
CommandHandler.sendMessage(sender, translate("commands.generic.invalid.itemId"));
|
||||
return;
|
||||
}
|
||||
break;
|
||||
default: // *No args*
|
||||
CommandHandler.sendMessage(sender, translate("commands.give.usage"));
|
||||
return;
|
||||
}
|
||||
mailBuilder.mail.itemList.add(new Mail.MailItem(item, amount, lvl));
|
||||
CommandHandler.sendMessage(sender, translate("commands.sendMail.send", Integer.toString(amount), Integer.toString(item), Integer.toString(lvl)));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
CommandHandler.sendMessage(sender, translate("commands.sendMail.invalid_arguments_please_use", getConstructionArgs(mailBuilder.constructionStage)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private String getConstructionArgs(int stage) {
|
||||
return switch(stage) {
|
||||
case 0 -> translate("commands.sendMail.title");
|
||||
case 1 -> translate("commands.sendMail.message");
|
||||
case 2 -> translate("commands.sendMail.sender");
|
||||
case 3 -> translate("commands.sendMail.arguments");
|
||||
default -> translate("commands.sendMail.error", Integer.toString(stage));
|
||||
};
|
||||
}
|
||||
|
||||
public static class MailBuilder {
|
||||
public int recipient;
|
||||
public boolean sendToAll;
|
||||
public int constructionStage;
|
||||
public Mail mail;
|
||||
|
||||
public MailBuilder(int recipient, Mail mail) {
|
||||
this.recipient = recipient;
|
||||
this.sendToAll = false;
|
||||
this.constructionStage = 0;
|
||||
this.mail = mail;
|
||||
}
|
||||
|
||||
public MailBuilder(boolean sendToAll, Mail mail) {
|
||||
if (sendToAll) {
|
||||
this.recipient = 0;
|
||||
this.sendToAll = true;
|
||||
this.constructionStage = 0;
|
||||
this.mail = mail;
|
||||
} else {
|
||||
Grasscutter.getLogger().error("Please use MailBuilder(int, mail) when not sending to all");
|
||||
Thread.dumpStack();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -1,37 +1,30 @@
|
||||
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.player.Player;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@Command(label = "say", usage = "say <player> <message>", description = "Sends a message to a player as the server",
|
||||
import static emu.grasscutter.utils.Language.translate;
|
||||
|
||||
@Command(label = "say", usage = "say <message>", description = "Sends a message to a player as the server",
|
||||
aliases = {"sendservmsg", "sendservermessage", "sendmessage"}, permission = "server.sendmessage")
|
||||
public final class SendMessageCommand implements CommandHandler {
|
||||
|
||||
@Override
|
||||
public void execute(GenshinPlayer sender, List<String> args) {
|
||||
if (args.size() < 2) {
|
||||
CommandHandler.sendMessage(null, "Usage: sendmessage <player> <message>");
|
||||
public void execute(Player sender, Player targetPlayer, List<String> args) {
|
||||
if (targetPlayer == null) {
|
||||
CommandHandler.sendMessage(sender, translate("commands.execution.need_target"));
|
||||
return;
|
||||
}
|
||||
if (args.size() == 0) {
|
||||
CommandHandler.sendMessage(null, translate("commands.sendMessage.usage"));
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
int target = Integer.parseInt(args.get(0));
|
||||
String message = String.join(" ", args.subList(1, args.size()));
|
||||
|
||||
GenshinPlayer targetPlayer = Grasscutter.getGameServer().getPlayerByUid(target);
|
||||
if (targetPlayer == null) {
|
||||
CommandHandler.sendMessage(sender, "Player not found.");
|
||||
return;
|
||||
}
|
||||
|
||||
CommandHandler.sendMessage(targetPlayer, message);
|
||||
CommandHandler.sendMessage(sender, "Message sent.");
|
||||
} catch (NumberFormatException ignored) {
|
||||
CommandHandler.sendMessage(sender, "Invalid player ID.");
|
||||
}
|
||||
String message = String.join(" ", args);
|
||||
CommandHandler.sendMessage(targetPlayer, message);
|
||||
CommandHandler.sendMessage(sender, translate("commands.sendMessage.success"));
|
||||
}
|
||||
}
|
@ -0,0 +1,52 @@
|
||||
package emu.grasscutter.command.commands;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import emu.grasscutter.command.Command;
|
||||
import emu.grasscutter.command.CommandHandler;
|
||||
import emu.grasscutter.data.GameData;
|
||||
import emu.grasscutter.game.avatar.Avatar;
|
||||
import emu.grasscutter.game.player.Player;
|
||||
import emu.grasscutter.server.packet.send.PacketAvatarFetterDataNotify;
|
||||
|
||||
import static emu.grasscutter.utils.Language.translate;
|
||||
|
||||
@Command(label = "setfetterlevel", usage = "setfetterlevel <level>",
|
||||
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(Player sender, Player targetPlayer, List<String> args) {
|
||||
if (targetPlayer == null) {
|
||||
CommandHandler.sendMessage(sender, translate("commands.execution.need_target"));
|
||||
return;
|
||||
}
|
||||
|
||||
if (args.size() != 1) {
|
||||
CommandHandler.sendMessage(sender, translate("commands.setFetterLevel.usage"));
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
int fetterLevel = Integer.parseInt(args.get(0));
|
||||
if (fetterLevel < 0 || fetterLevel > 10) {
|
||||
CommandHandler.sendMessage(sender, translate("commands.setFetterLevel.range_error"));
|
||||
return;
|
||||
}
|
||||
Avatar avatar = targetPlayer.getTeamManager().getCurrentAvatarEntity().getAvatar();
|
||||
|
||||
avatar.setFetterLevel(fetterLevel);
|
||||
if (fetterLevel != 10) {
|
||||
avatar.setFetterExp(GameData.getAvatarFetterLevelDataMap().get(fetterLevel).getExp());
|
||||
}
|
||||
avatar.save();
|
||||
|
||||
targetPlayer.sendPacket(new PacketAvatarFetterDataNotify(avatar));
|
||||
CommandHandler.sendMessage(sender, translate("commands.setFetterLevel.success", fetterLevel));
|
||||
} catch (NumberFormatException ignored) {
|
||||
CommandHandler.sendMessage(sender, translate("commands.setFetterLevel.level_error"));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -1,239 +1,231 @@
|
||||
package emu.grasscutter.command.commands;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
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.player.Player;
|
||||
import emu.grasscutter.game.props.FightProperty;
|
||||
import emu.grasscutter.server.packet.send.PacketEntityFightPropUpdateNotify;
|
||||
import emu.grasscutter.utils.Language;
|
||||
|
||||
import java.util.List;
|
||||
import static emu.grasscutter.utils.Language.translate;
|
||||
|
||||
@Command(label = "setstats", usage = "setstats|stats <stat> <value>",
|
||||
description = "Set fight property for your current active character", aliases = {"stats"}, permission = "player.setstats")
|
||||
public final class SetStatsCommand implements CommandHandler {
|
||||
static class Stat {
|
||||
String name;
|
||||
FightProperty prop;
|
||||
boolean percent;
|
||||
|
||||
@Override
|
||||
public void execute(GenshinPlayer sender, List<String> args) {
|
||||
if (sender == null) {
|
||||
CommandHandler.sendMessage(null, "Run this command in-game.");
|
||||
return;
|
||||
}
|
||||
|
||||
if (args.size() < 2){
|
||||
CommandHandler.sendMessage(sender, "Usage: setstats|stats <stat> <value>");
|
||||
return;
|
||||
}
|
||||
|
||||
String stat = args.get(0);
|
||||
switch (stat) {
|
||||
default:
|
||||
CommandHandler.sendMessage(sender, "Usage: /setstats|stats <hp | def | atk | em | er | crate | cdmg> <value> for basic stats");
|
||||
CommandHandler.sendMessage(sender, "Usage: /stats <epyro | ecryo | ehydro | egeo | edend | eelec | ephys> <amount> for elemental bonus");
|
||||
return;
|
||||
case "hp":
|
||||
try {
|
||||
int health = Integer.parseInt(args.get(1));
|
||||
EntityAvatar entity = sender.getTeamManager().getCurrentAvatarEntity();
|
||||
entity.setFightProperty(FightProperty.FIGHT_PROP_CUR_HP, health);
|
||||
entity.getWorld().broadcastPacket(new PacketEntityFightPropUpdateNotify(entity, FightProperty.FIGHT_PROP_CUR_HP));
|
||||
CommandHandler.sendMessage(sender, "HP set to " + health + ".");
|
||||
} catch (NumberFormatException ignored) {
|
||||
CommandHandler.sendMessage(sender, "Invalid HP value.");
|
||||
return;
|
||||
}
|
||||
break;
|
||||
case "def":
|
||||
try {
|
||||
int def = Integer.parseInt(args.get(1));
|
||||
EntityAvatar entity = sender.getTeamManager().getCurrentAvatarEntity();
|
||||
entity.setFightProperty(FightProperty.FIGHT_PROP_CUR_DEFENSE, def);
|
||||
entity.getWorld().broadcastPacket(new PacketEntityFightPropUpdateNotify(entity, FightProperty.FIGHT_PROP_CUR_DEFENSE));
|
||||
CommandHandler.sendMessage(sender, "DEF set to " + def + ".");
|
||||
} catch (NumberFormatException ignored) {
|
||||
CommandHandler.sendMessage(sender, "Invalid DEF value.");
|
||||
return;
|
||||
}
|
||||
break;
|
||||
case "atk":
|
||||
try {
|
||||
int atk = Integer.parseInt(args.get(1));
|
||||
EntityAvatar entity = sender.getTeamManager().getCurrentAvatarEntity();
|
||||
entity.setFightProperty(FightProperty.FIGHT_PROP_CUR_ATTACK, atk);
|
||||
entity.getWorld().broadcastPacket(new PacketEntityFightPropUpdateNotify(entity, FightProperty.FIGHT_PROP_CUR_ATTACK));
|
||||
CommandHandler.sendMessage(sender, "ATK set to " + atk + ".");
|
||||
} catch (NumberFormatException ignored) {
|
||||
CommandHandler.sendMessage(sender, "Invalid ATK value.");
|
||||
return;
|
||||
}
|
||||
break;
|
||||
case "em":
|
||||
try {
|
||||
int em = Integer.parseInt(args.get(1));
|
||||
EntityAvatar entity = sender.getTeamManager().getCurrentAvatarEntity();
|
||||
entity.setFightProperty(FightProperty.FIGHT_PROP_ELEMENT_MASTERY, em);
|
||||
entity.getWorld().broadcastPacket(new PacketEntityFightPropUpdateNotify(entity, FightProperty.FIGHT_PROP_ELEMENT_MASTERY));
|
||||
CommandHandler.sendMessage(sender, "Elemental Mastery set to " + em + ".");
|
||||
} catch (NumberFormatException ignored) {
|
||||
CommandHandler.sendMessage(sender, "Invalid EM value.");
|
||||
return;
|
||||
}
|
||||
break;
|
||||
case "er":
|
||||
try {
|
||||
float er = Integer.parseInt(args.get(1));
|
||||
EntityAvatar entity = sender.getTeamManager().getCurrentAvatarEntity();
|
||||
float erecharge = er / 10000;
|
||||
entity.setFightProperty(FightProperty.FIGHT_PROP_CHARGE_EFFICIENCY, erecharge);
|
||||
entity.getWorld().broadcastPacket(new PacketEntityFightPropUpdateNotify(entity, FightProperty.FIGHT_PROP_CHARGE_EFFICIENCY));
|
||||
float iger = erecharge * 100;
|
||||
CommandHandler.sendMessage(sender, "Energy recharge set to " + iger + "%.");
|
||||
} catch (NumberFormatException ignored) {
|
||||
CommandHandler.sendMessage(sender, "Invalid ER value.");
|
||||
return;
|
||||
}
|
||||
break;
|
||||
case "crate":
|
||||
try {
|
||||
float cr = Integer.parseInt(args.get(1));
|
||||
EntityAvatar entity = sender.getTeamManager().getCurrentAvatarEntity();
|
||||
float crate = cr / 10000;
|
||||
entity.setFightProperty(FightProperty.FIGHT_PROP_CRITICAL, crate);
|
||||
entity.getWorld().broadcastPacket(new PacketEntityFightPropUpdateNotify(entity, FightProperty.FIGHT_PROP_CRITICAL));
|
||||
float igcrate = crate * 100;
|
||||
CommandHandler.sendMessage(sender, "Crit Rate set to " + igcrate + "%.");
|
||||
} catch (NumberFormatException ignored) {
|
||||
CommandHandler.sendMessage(sender, "Invalid Crit Rate value.");
|
||||
return;
|
||||
}
|
||||
break;
|
||||
case "cdmg":
|
||||
try {
|
||||
float cdmg = Integer.parseInt(args.get(1));
|
||||
EntityAvatar entity = sender.getTeamManager().getCurrentAvatarEntity();
|
||||
float cdamage = cdmg / 10000;
|
||||
entity.setFightProperty(FightProperty.FIGHT_PROP_CRITICAL_HURT, cdamage);
|
||||
entity.getWorld().broadcastPacket(new PacketEntityFightPropUpdateNotify(entity, FightProperty.FIGHT_PROP_CRITICAL_HURT));
|
||||
float igcdmg = cdamage * 100;
|
||||
CommandHandler.sendMessage(sender, "Crit DMG set to " + igcdmg + "%");
|
||||
} catch (NumberFormatException ignored) {
|
||||
CommandHandler.sendMessage(sender, "Invalid Crit DMG value.");
|
||||
return;
|
||||
}
|
||||
break;
|
||||
case "epyro":
|
||||
try {
|
||||
float epyro = Integer.parseInt(args.get(1));
|
||||
EntityAvatar entity = sender.getTeamManager().getCurrentAvatarEntity();
|
||||
float pyro = epyro / 10000;
|
||||
entity.setFightProperty(FightProperty.FIGHT_PROP_FIRE_ADD_HURT, pyro);
|
||||
entity.getWorld().broadcastPacket(new PacketEntityFightPropUpdateNotify(entity, FightProperty.FIGHT_PROP_FIRE_ADD_HURT));
|
||||
float igpyro = pyro * 100;
|
||||
CommandHandler.sendMessage(sender, "Pyro DMG Bonus set to " + igpyro + "%");
|
||||
} catch (NumberFormatException ignored) {
|
||||
CommandHandler.sendMessage(sender, "Invalid Pyro DMG Bonus value.");
|
||||
return;
|
||||
}
|
||||
break;
|
||||
case "ecryo":
|
||||
try {
|
||||
float ecryo = Integer.parseInt(args.get(1));
|
||||
EntityAvatar entity = sender.getTeamManager().getCurrentAvatarEntity();
|
||||
float cryo = ecryo / 10000;
|
||||
entity.setFightProperty(FightProperty.FIGHT_PROP_ICE_ADD_HURT, cryo);
|
||||
entity.getWorld().broadcastPacket(new PacketEntityFightPropUpdateNotify(entity, FightProperty.FIGHT_PROP_ICE_ADD_HURT));
|
||||
float igcyro = cryo * 100;
|
||||
CommandHandler.sendMessage(sender, "Cyro DMG Bonus set to " + igcyro + "%");
|
||||
} catch (NumberFormatException ignored) {
|
||||
CommandHandler.sendMessage(sender, "Invalid Cryo DMG Bonus value.");
|
||||
return;
|
||||
}
|
||||
break;
|
||||
case "ehydro":
|
||||
try {
|
||||
float ehydro = Integer.parseInt(args.get(1));
|
||||
EntityAvatar entity = sender.getTeamManager().getCurrentAvatarEntity();
|
||||
float hydro = ehydro / 10000;
|
||||
entity.setFightProperty(FightProperty.FIGHT_PROP_WATER_ADD_HURT, hydro);
|
||||
entity.getWorld().broadcastPacket(new PacketEntityFightPropUpdateNotify(entity, FightProperty.FIGHT_PROP_WATER_ADD_HURT));
|
||||
float ighydro = hydro * 100;
|
||||
CommandHandler.sendMessage(sender, "Hydro DMG Bonus set to " + ighydro + "%");
|
||||
} catch (NumberFormatException ignored) {
|
||||
CommandHandler.sendMessage(sender, "Invalid Hydro DMG Bonus value.");
|
||||
return;
|
||||
}
|
||||
break;
|
||||
case "eanemo":
|
||||
try {
|
||||
float eanemo = Integer.parseInt(args.get(1));
|
||||
EntityAvatar entity = sender.getTeamManager().getCurrentAvatarEntity();
|
||||
float anemo = eanemo / 10000;
|
||||
entity.setFightProperty(FightProperty.FIGHT_PROP_WIND_ADD_HURT, anemo);
|
||||
entity.getWorld().broadcastPacket(new PacketEntityFightPropUpdateNotify(entity, FightProperty.FIGHT_PROP_WIND_ADD_HURT));
|
||||
float iganemo = anemo * 100;
|
||||
CommandHandler.sendMessage(sender, "Anemo DMG Bonus set to " + iganemo + "%");
|
||||
} catch (NumberFormatException ignored) {
|
||||
CommandHandler.sendMessage(sender, "Invalid Anemo DMG Bonus value.");
|
||||
return;
|
||||
}
|
||||
break;
|
||||
case "egeo":
|
||||
try {
|
||||
float egeo = Integer.parseInt(args.get(1));
|
||||
EntityAvatar entity = sender.getTeamManager().getCurrentAvatarEntity();
|
||||
float geo = egeo / 10000;
|
||||
entity.setFightProperty(FightProperty.FIGHT_PROP_ROCK_ADD_HURT, geo);
|
||||
entity.getWorld().broadcastPacket(new PacketEntityFightPropUpdateNotify(entity, FightProperty.FIGHT_PROP_ROCK_ADD_HURT));
|
||||
float iggeo = geo * 100;
|
||||
CommandHandler.sendMessage(sender, "Geo DMG Bonus set to " + iggeo + "%");
|
||||
} catch (NumberFormatException ignored) {
|
||||
CommandHandler.sendMessage(sender, "Invalid Geo DMG Bonus value.");
|
||||
return;
|
||||
}
|
||||
break;
|
||||
case "ethunder":
|
||||
case "eelec":
|
||||
try {
|
||||
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));
|
||||
float igelec = elec * 100;
|
||||
CommandHandler.sendMessage(sender, "Electro DMG Bonus set to " + igelec + "%");
|
||||
} catch (NumberFormatException ignored) {
|
||||
CommandHandler.sendMessage(sender, "Invalid Electro DMG Bonus value.");
|
||||
return;
|
||||
}
|
||||
break;
|
||||
case "ephys":
|
||||
try {
|
||||
float ephys = Integer.parseInt(args.get(1));
|
||||
EntityAvatar entity = sender.getTeamManager().getCurrentAvatarEntity();
|
||||
float phys = ephys / 10000;
|
||||
entity.setFightProperty(FightProperty.FIGHT_PROP_PHYSICAL_ADD_HURT, phys);
|
||||
entity.getWorld().broadcastPacket(new PacketEntityFightPropUpdateNotify(entity, FightProperty.FIGHT_PROP_PHYSICAL_ADD_HURT));
|
||||
float igphys = phys * 100;
|
||||
CommandHandler.sendMessage(sender, "Physical DMG Bonus set to " + igphys + "%");
|
||||
} catch (NumberFormatException ignored) {
|
||||
CommandHandler.sendMessage(sender, "Invalid Physical DMG Bonus value.");
|
||||
return;
|
||||
}
|
||||
break;
|
||||
case "edend":
|
||||
try {
|
||||
float edend = Integer.parseInt(args.get(1));
|
||||
EntityAvatar entity = sender.getTeamManager().getCurrentAvatarEntity();
|
||||
float dend = edend / 10000;
|
||||
entity.setFightProperty(FightProperty.FIGHT_PROP_GRASS_ADD_HURT, dend);
|
||||
entity.getWorld().broadcastPacket(new PacketEntityFightPropUpdateNotify(entity, FightProperty.FIGHT_PROP_GRASS_ADD_HURT));
|
||||
float igdend = dend * 100;
|
||||
CommandHandler.sendMessage(sender, "Dendro DMG Bonus set to " + igdend + "%");
|
||||
} catch (NumberFormatException ignored) {
|
||||
CommandHandler.sendMessage(sender, "Invalid Dendro DMG Bonus value.");
|
||||
return;
|
||||
}
|
||||
break;
|
||||
public Stat(String name, FightProperty prop, boolean percent) {
|
||||
this.name = name;
|
||||
this.prop = prop;
|
||||
this.percent = percent;
|
||||
}
|
||||
}
|
||||
|
||||
Map<String, Stat> stats = new HashMap<>();
|
||||
|
||||
public SetStatsCommand() {
|
||||
// Default stats
|
||||
stats.put("maxhp", new Stat(FightProperty.FIGHT_PROP_MAX_HP.toString(), FightProperty.FIGHT_PROP_MAX_HP, false));
|
||||
stats.put("hp", new Stat(FightProperty.FIGHT_PROP_CUR_HP.toString(), FightProperty.FIGHT_PROP_CUR_HP, false));
|
||||
stats.put("atk", new Stat(FightProperty.FIGHT_PROP_CUR_ATTACK.toString(), FightProperty.FIGHT_PROP_CUR_ATTACK, false));
|
||||
stats.put("atkb", new Stat(FightProperty.FIGHT_PROP_BASE_ATTACK.toString(), FightProperty.FIGHT_PROP_BASE_ATTACK, false)); // This doesn't seem to get used to recalculate ATK, so it's only useful for stuff like Bennett's buff.
|
||||
stats.put("def", new Stat(FightProperty.FIGHT_PROP_DEFENSE.toString(), FightProperty.FIGHT_PROP_DEFENSE, false));
|
||||
stats.put("em", new Stat(FightProperty.FIGHT_PROP_ELEMENT_MASTERY.toString(), FightProperty.FIGHT_PROP_ELEMENT_MASTERY, false));
|
||||
stats.put("er", new Stat(FightProperty.FIGHT_PROP_CHARGE_EFFICIENCY.toString(), FightProperty.FIGHT_PROP_CHARGE_EFFICIENCY, true));
|
||||
stats.put("crate", new Stat(FightProperty.FIGHT_PROP_CRITICAL.toString(), FightProperty.FIGHT_PROP_CRITICAL, true));
|
||||
stats.put("cdmg", new Stat(FightProperty.FIGHT_PROP_CRITICAL_HURT.toString(), FightProperty.FIGHT_PROP_CRITICAL_HURT, true));
|
||||
stats.put("dmg", new Stat(FightProperty.FIGHT_PROP_ADD_HURT.toString(), FightProperty.FIGHT_PROP_ADD_HURT, true)); // This seems to get reset after attacks
|
||||
stats.put("eanemo", new Stat(FightProperty.FIGHT_PROP_WIND_ADD_HURT.toString(), FightProperty.FIGHT_PROP_WIND_ADD_HURT, true));
|
||||
stats.put("ecryo", new Stat(FightProperty.FIGHT_PROP_ICE_ADD_HURT.toString(), FightProperty.FIGHT_PROP_ICE_ADD_HURT, true));
|
||||
stats.put("edendro", new Stat(FightProperty.FIGHT_PROP_GRASS_ADD_HURT.toString(), FightProperty.FIGHT_PROP_GRASS_ADD_HURT, true));
|
||||
stats.put("eelectro", new Stat(FightProperty.FIGHT_PROP_ELEC_ADD_HURT.toString(), FightProperty.FIGHT_PROP_ELEC_ADD_HURT, true));
|
||||
stats.put("egeo", new Stat(FightProperty.FIGHT_PROP_ROCK_ADD_HURT.toString(), FightProperty.FIGHT_PROP_ROCK_ADD_HURT, true));
|
||||
stats.put("ehydro", new Stat(FightProperty.FIGHT_PROP_WATER_ADD_HURT.toString(), FightProperty.FIGHT_PROP_WATER_ADD_HURT, true));
|
||||
stats.put("epyro", new Stat(FightProperty.FIGHT_PROP_FIRE_ADD_HURT.toString(), FightProperty.FIGHT_PROP_FIRE_ADD_HURT, true));
|
||||
stats.put("ephys", new Stat(FightProperty.FIGHT_PROP_PHYSICAL_ADD_HURT.toString(), FightProperty.FIGHT_PROP_PHYSICAL_ADD_HURT, true));
|
||||
stats.put("resall", new Stat(FightProperty.FIGHT_PROP_SUB_HURT.toString(), FightProperty.FIGHT_PROP_SUB_HURT, true)); // This seems to get reset after attacks
|
||||
stats.put("resanemo", new Stat(FightProperty.FIGHT_PROP_WIND_SUB_HURT.toString(), FightProperty.FIGHT_PROP_WIND_SUB_HURT, true));
|
||||
stats.put("rescryo", new Stat(FightProperty.FIGHT_PROP_ICE_SUB_HURT.toString(), FightProperty.FIGHT_PROP_ICE_SUB_HURT, true));
|
||||
stats.put("resdendro", new Stat(FightProperty.FIGHT_PROP_GRASS_SUB_HURT.toString(), FightProperty.FIGHT_PROP_GRASS_SUB_HURT, true));
|
||||
stats.put("reselectro", new Stat(FightProperty.FIGHT_PROP_ELEC_SUB_HURT.toString(), FightProperty.FIGHT_PROP_ELEC_SUB_HURT, true));
|
||||
stats.put("resgeo", new Stat(FightProperty.FIGHT_PROP_ROCK_SUB_HURT.toString(), FightProperty.FIGHT_PROP_ROCK_SUB_HURT, true));
|
||||
stats.put("reshydro", new Stat(FightProperty.FIGHT_PROP_WATER_SUB_HURT.toString(), FightProperty.FIGHT_PROP_WATER_SUB_HURT, true));
|
||||
stats.put("respyro", new Stat(FightProperty.FIGHT_PROP_FIRE_SUB_HURT.toString(), FightProperty.FIGHT_PROP_FIRE_SUB_HURT, true));
|
||||
stats.put("resphys", new Stat(FightProperty.FIGHT_PROP_PHYSICAL_SUB_HURT.toString(), FightProperty.FIGHT_PROP_PHYSICAL_SUB_HURT, true));
|
||||
stats.put("cdr", new Stat(FightProperty.FIGHT_PROP_SKILL_CD_MINUS_RATIO.toString(), FightProperty.FIGHT_PROP_SKILL_CD_MINUS_RATIO, true));
|
||||
stats.put("heal", new Stat(FightProperty.FIGHT_PROP_HEAL_ADD.toString(), FightProperty.FIGHT_PROP_HEAL_ADD, true));
|
||||
stats.put("heali", new Stat(FightProperty.FIGHT_PROP_HEALED_ADD.toString(), FightProperty.FIGHT_PROP_HEALED_ADD, true));
|
||||
stats.put("shield", new Stat(FightProperty.FIGHT_PROP_SHIELD_COST_MINUS_RATIO.toString(), FightProperty.FIGHT_PROP_SHIELD_COST_MINUS_RATIO, true));
|
||||
stats.put("defi", new Stat(FightProperty.FIGHT_PROP_DEFENCE_IGNORE_RATIO.toString(), FightProperty.FIGHT_PROP_DEFENCE_IGNORE_RATIO, true));
|
||||
// Compatibility aliases
|
||||
stats.put("mhp", stats.get("maxhp"));
|
||||
stats.put("cr", stats.get("crate"));
|
||||
stats.put("cd", stats.get("cdmg"));
|
||||
stats.put("edend", stats.get("edendro"));
|
||||
stats.put("eelec", stats.get("eelectro"));
|
||||
stats.put("ethunder", stats.get("eelectro"));
|
||||
|
||||
// Full FightProperty enum that won't be advertised but can be used by devs
|
||||
// They have a prefix to avoid the "hp" clash
|
||||
stats.put("_none", new Stat("NONE", FightProperty.FIGHT_PROP_NONE, true));
|
||||
stats.put("_base_hp", new Stat("BASE_HP", FightProperty.FIGHT_PROP_BASE_HP, false));
|
||||
stats.put("_hp", new Stat("HP", FightProperty.FIGHT_PROP_HP, false));
|
||||
stats.put("_hp_percent", new Stat("HP_PERCENT", FightProperty.FIGHT_PROP_HP_PERCENT, true));
|
||||
stats.put("_base_attack", new Stat("BASE_ATTACK", FightProperty.FIGHT_PROP_BASE_ATTACK, false));
|
||||
stats.put("_attack", new Stat("ATTACK", FightProperty.FIGHT_PROP_ATTACK, false));
|
||||
stats.put("_attack_percent", new Stat("ATTACK_PERCENT", FightProperty.FIGHT_PROP_ATTACK_PERCENT, true));
|
||||
stats.put("_base_defense", new Stat("BASE_DEFENSE", FightProperty.FIGHT_PROP_BASE_DEFENSE, false));
|
||||
stats.put("_defense", new Stat("DEFENSE", FightProperty.FIGHT_PROP_DEFENSE, false));
|
||||
stats.put("_defense_percent", new Stat("DEFENSE_PERCENT", FightProperty.FIGHT_PROP_DEFENSE_PERCENT, true));
|
||||
stats.put("_base_speed", new Stat("BASE_SPEED", FightProperty.FIGHT_PROP_BASE_SPEED, true));
|
||||
stats.put("_speed_percent", new Stat("SPEED_PERCENT", FightProperty.FIGHT_PROP_SPEED_PERCENT, true));
|
||||
stats.put("_hp_mp_percent", new Stat("HP_MP_PERCENT", FightProperty.FIGHT_PROP_HP_MP_PERCENT, true));
|
||||
stats.put("_attack_mp_percent", new Stat("ATTACK_MP_PERCENT", FightProperty.FIGHT_PROP_ATTACK_MP_PERCENT, true));
|
||||
stats.put("_critical", new Stat("CRITICAL", FightProperty.FIGHT_PROP_CRITICAL, true));
|
||||
stats.put("_anti_critical", new Stat("ANTI_CRITICAL", FightProperty.FIGHT_PROP_ANTI_CRITICAL, true));
|
||||
stats.put("_critical_hurt", new Stat("CRITICAL_HURT", FightProperty.FIGHT_PROP_CRITICAL_HURT, true));
|
||||
stats.put("_charge_efficiency", new Stat("CHARGE_EFFICIENCY", FightProperty.FIGHT_PROP_CHARGE_EFFICIENCY, true));
|
||||
stats.put("_add_hurt", new Stat("ADD_HURT", FightProperty.FIGHT_PROP_ADD_HURT, true));
|
||||
stats.put("_sub_hurt", new Stat("SUB_HURT", FightProperty.FIGHT_PROP_SUB_HURT, true));
|
||||
stats.put("_heal_add", new Stat("HEAL_ADD", FightProperty.FIGHT_PROP_HEAL_ADD, true));
|
||||
stats.put("_healed_add", new Stat("HEALED_ADD", FightProperty.FIGHT_PROP_HEALED_ADD, false));
|
||||
stats.put("_element_mastery", new Stat("ELEMENT_MASTERY", FightProperty.FIGHT_PROP_ELEMENT_MASTERY, true));
|
||||
stats.put("_physical_sub_hurt", new Stat("PHYSICAL_SUB_HURT", FightProperty.FIGHT_PROP_PHYSICAL_SUB_HURT, true));
|
||||
stats.put("_physical_add_hurt", new Stat("PHYSICAL_ADD_HURT", FightProperty.FIGHT_PROP_PHYSICAL_ADD_HURT, true));
|
||||
stats.put("_defence_ignore_ratio", new Stat("DEFENCE_IGNORE_RATIO", FightProperty.FIGHT_PROP_DEFENCE_IGNORE_RATIO, true));
|
||||
stats.put("_defence_ignore_delta", new Stat("DEFENCE_IGNORE_DELTA", FightProperty.FIGHT_PROP_DEFENCE_IGNORE_DELTA, true));
|
||||
stats.put("_fire_add_hurt", new Stat("FIRE_ADD_HURT", FightProperty.FIGHT_PROP_FIRE_ADD_HURT, true));
|
||||
stats.put("_elec_add_hurt", new Stat("ELEC_ADD_HURT", FightProperty.FIGHT_PROP_ELEC_ADD_HURT, true));
|
||||
stats.put("_water_add_hurt", new Stat("WATER_ADD_HURT", FightProperty.FIGHT_PROP_WATER_ADD_HURT, true));
|
||||
stats.put("_grass_add_hurt", new Stat("GRASS_ADD_HURT", FightProperty.FIGHT_PROP_GRASS_ADD_HURT, true));
|
||||
stats.put("_wind_add_hurt", new Stat("WIND_ADD_HURT", FightProperty.FIGHT_PROP_WIND_ADD_HURT, true));
|
||||
stats.put("_rock_add_hurt", new Stat("ROCK_ADD_HURT", FightProperty.FIGHT_PROP_ROCK_ADD_HURT, true));
|
||||
stats.put("_ice_add_hurt", new Stat("ICE_ADD_HURT", FightProperty.FIGHT_PROP_ICE_ADD_HURT, true));
|
||||
stats.put("_hit_head_add_hurt", new Stat("HIT_HEAD_ADD_HURT", FightProperty.FIGHT_PROP_HIT_HEAD_ADD_HURT, true));
|
||||
stats.put("_fire_sub_hurt", new Stat("FIRE_SUB_HURT", FightProperty.FIGHT_PROP_FIRE_SUB_HURT, true));
|
||||
stats.put("_elec_sub_hurt", new Stat("ELEC_SUB_HURT", FightProperty.FIGHT_PROP_ELEC_SUB_HURT, true));
|
||||
stats.put("_water_sub_hurt", new Stat("WATER_SUB_HURT", FightProperty.FIGHT_PROP_WATER_SUB_HURT, true));
|
||||
stats.put("_grass_sub_hurt", new Stat("GRASS_SUB_HURT", FightProperty.FIGHT_PROP_GRASS_SUB_HURT, true));
|
||||
stats.put("_wind_sub_hurt", new Stat("WIND_SUB_HURT", FightProperty.FIGHT_PROP_WIND_SUB_HURT, true));
|
||||
stats.put("_rock_sub_hurt", new Stat("ROCK_SUB_HURT", FightProperty.FIGHT_PROP_ROCK_SUB_HURT, true));
|
||||
stats.put("_ice_sub_hurt", new Stat("ICE_SUB_HURT", FightProperty.FIGHT_PROP_ICE_SUB_HURT, true));
|
||||
stats.put("_effect_hit", new Stat("EFFECT_HIT", FightProperty.FIGHT_PROP_EFFECT_HIT, true));
|
||||
stats.put("_effect_resist", new Stat("EFFECT_RESIST", FightProperty.FIGHT_PROP_EFFECT_RESIST, true));
|
||||
stats.put("_freeze_resist", new Stat("FREEZE_RESIST", FightProperty.FIGHT_PROP_FREEZE_RESIST, true));
|
||||
stats.put("_torpor_resist", new Stat("TORPOR_RESIST", FightProperty.FIGHT_PROP_TORPOR_RESIST, true));
|
||||
stats.put("_dizzy_resist", new Stat("DIZZY_RESIST", FightProperty.FIGHT_PROP_DIZZY_RESIST, true));
|
||||
stats.put("_freeze_shorten", new Stat("FREEZE_SHORTEN", FightProperty.FIGHT_PROP_FREEZE_SHORTEN, true));
|
||||
stats.put("_torpor_shorten", new Stat("TORPOR_SHORTEN", FightProperty.FIGHT_PROP_TORPOR_SHORTEN, true));
|
||||
stats.put("_dizzy_shorten", new Stat("DIZZY_SHORTEN", FightProperty.FIGHT_PROP_DIZZY_SHORTEN, true));
|
||||
stats.put("_max_fire_energy", new Stat("MAX_FIRE_ENERGY", FightProperty.FIGHT_PROP_MAX_FIRE_ENERGY, true));
|
||||
stats.put("_max_elec_energy", new Stat("MAX_ELEC_ENERGY", FightProperty.FIGHT_PROP_MAX_ELEC_ENERGY, true));
|
||||
stats.put("_max_water_energy", new Stat("MAX_WATER_ENERGY", FightProperty.FIGHT_PROP_MAX_WATER_ENERGY, true));
|
||||
stats.put("_max_grass_energy", new Stat("MAX_GRASS_ENERGY", FightProperty.FIGHT_PROP_MAX_GRASS_ENERGY, true));
|
||||
stats.put("_max_wind_energy", new Stat("MAX_WIND_ENERGY", FightProperty.FIGHT_PROP_MAX_WIND_ENERGY, true));
|
||||
stats.put("_max_ice_energy", new Stat("MAX_ICE_ENERGY", FightProperty.FIGHT_PROP_MAX_ICE_ENERGY, true));
|
||||
stats.put("_max_rock_energy", new Stat("MAX_ROCK_ENERGY", FightProperty.FIGHT_PROP_MAX_ROCK_ENERGY, true));
|
||||
stats.put("_skill_cd_minus_ratio", new Stat("SKILL_CD_MINUS_RATIO", FightProperty.FIGHT_PROP_SKILL_CD_MINUS_RATIO, true));
|
||||
stats.put("_shield_cost_minus_ratio", new Stat("SHIELD_COST_MINUS_RATIO", FightProperty.FIGHT_PROP_SHIELD_COST_MINUS_RATIO, true));
|
||||
stats.put("_cur_fire_energy", new Stat("CUR_FIRE_ENERGY", FightProperty.FIGHT_PROP_CUR_FIRE_ENERGY, false));
|
||||
stats.put("_cur_elec_energy", new Stat("CUR_ELEC_ENERGY", FightProperty.FIGHT_PROP_CUR_ELEC_ENERGY, false));
|
||||
stats.put("_cur_water_energy", new Stat("CUR_WATER_ENERGY", FightProperty.FIGHT_PROP_CUR_WATER_ENERGY, false));
|
||||
stats.put("_cur_grass_energy", new Stat("CUR_GRASS_ENERGY", FightProperty.FIGHT_PROP_CUR_GRASS_ENERGY, false));
|
||||
stats.put("_cur_wind_energy", new Stat("CUR_WIND_ENERGY", FightProperty.FIGHT_PROP_CUR_WIND_ENERGY, false));
|
||||
stats.put("_cur_ice_energy", new Stat("CUR_ICE_ENERGY", FightProperty.FIGHT_PROP_CUR_ICE_ENERGY, false));
|
||||
stats.put("_cur_rock_energy", new Stat("CUR_ROCK_ENERGY", FightProperty.FIGHT_PROP_CUR_ROCK_ENERGY, false));
|
||||
stats.put("_cur_hp", new Stat("CUR_HP", FightProperty.FIGHT_PROP_CUR_HP, false));
|
||||
stats.put("_max_hp", new Stat("MAX_HP", FightProperty.FIGHT_PROP_MAX_HP, false));
|
||||
stats.put("_cur_attack", new Stat("CUR_ATTACK", FightProperty.FIGHT_PROP_CUR_ATTACK, false));
|
||||
stats.put("_cur_defense", new Stat("CUR_DEFENSE", FightProperty.FIGHT_PROP_CUR_DEFENSE, false));
|
||||
stats.put("_cur_speed", new Stat("CUR_SPEED", FightProperty.FIGHT_PROP_CUR_SPEED, true));
|
||||
stats.put("_nonextra_attack", new Stat("NONEXTRA_ATTACK", FightProperty.FIGHT_PROP_NONEXTRA_ATTACK, true));
|
||||
stats.put("_nonextra_defense", new Stat("NONEXTRA_DEFENSE", FightProperty.FIGHT_PROP_NONEXTRA_DEFENSE, true));
|
||||
stats.put("_nonextra_critical", new Stat("NONEXTRA_CRITICAL", FightProperty.FIGHT_PROP_NONEXTRA_CRITICAL, true));
|
||||
stats.put("_nonextra_anti_critical", new Stat("NONEXTRA_ANTI_CRITICAL", FightProperty.FIGHT_PROP_NONEXTRA_ANTI_CRITICAL, true));
|
||||
stats.put("_nonextra_critical_hurt", new Stat("NONEXTRA_CRITICAL_HURT", FightProperty.FIGHT_PROP_NONEXTRA_CRITICAL_HURT, true));
|
||||
stats.put("_nonextra_charge_efficiency", new Stat("NONEXTRA_CHARGE_EFFICIENCY", FightProperty.FIGHT_PROP_NONEXTRA_CHARGE_EFFICIENCY, true));
|
||||
stats.put("_nonextra_element_mastery", new Stat("NONEXTRA_ELEMENT_MASTERY", FightProperty.FIGHT_PROP_NONEXTRA_ELEMENT_MASTERY, true));
|
||||
stats.put("_nonextra_physical_sub_hurt", new Stat("NONEXTRA_PHYSICAL_SUB_HURT", FightProperty.FIGHT_PROP_NONEXTRA_PHYSICAL_SUB_HURT, true));
|
||||
stats.put("_nonextra_fire_add_hurt", new Stat("NONEXTRA_FIRE_ADD_HURT", FightProperty.FIGHT_PROP_NONEXTRA_FIRE_ADD_HURT, true));
|
||||
stats.put("_nonextra_elec_add_hurt", new Stat("NONEXTRA_ELEC_ADD_HURT", FightProperty.FIGHT_PROP_NONEXTRA_ELEC_ADD_HURT, true));
|
||||
stats.put("_nonextra_water_add_hurt", new Stat("NONEXTRA_WATER_ADD_HURT", FightProperty.FIGHT_PROP_NONEXTRA_WATER_ADD_HURT, true));
|
||||
stats.put("_nonextra_grass_add_hurt", new Stat("NONEXTRA_GRASS_ADD_HURT", FightProperty.FIGHT_PROP_NONEXTRA_GRASS_ADD_HURT, true));
|
||||
stats.put("_nonextra_wind_add_hurt", new Stat("NONEXTRA_WIND_ADD_HURT", FightProperty.FIGHT_PROP_NONEXTRA_WIND_ADD_HURT, true));
|
||||
stats.put("_nonextra_rock_add_hurt", new Stat("NONEXTRA_ROCK_ADD_HURT", FightProperty.FIGHT_PROP_NONEXTRA_ROCK_ADD_HURT, true));
|
||||
stats.put("_nonextra_ice_add_hurt", new Stat("NONEXTRA_ICE_ADD_HURT", FightProperty.FIGHT_PROP_NONEXTRA_ICE_ADD_HURT, true));
|
||||
stats.put("_nonextra_fire_sub_hurt", new Stat("NONEXTRA_FIRE_SUB_HURT", FightProperty.FIGHT_PROP_NONEXTRA_FIRE_SUB_HURT, true));
|
||||
stats.put("_nonextra_elec_sub_hurt", new Stat("NONEXTRA_ELEC_SUB_HURT", FightProperty.FIGHT_PROP_NONEXTRA_ELEC_SUB_HURT, true));
|
||||
stats.put("_nonextra_water_sub_hurt", new Stat("NONEXTRA_WATER_SUB_HURT", FightProperty.FIGHT_PROP_NONEXTRA_WATER_SUB_HURT, true));
|
||||
stats.put("_nonextra_grass_sub_hurt", new Stat("NONEXTRA_GRASS_SUB_HURT", FightProperty.FIGHT_PROP_NONEXTRA_GRASS_SUB_HURT, true));
|
||||
stats.put("_nonextra_wind_sub_hurt", new Stat("NONEXTRA_WIND_SUB_HURT", FightProperty.FIGHT_PROP_NONEXTRA_WIND_SUB_HURT, true));
|
||||
stats.put("_nonextra_rock_sub_hurt", new Stat("NONEXTRA_ROCK_SUB_HURT", FightProperty.FIGHT_PROP_NONEXTRA_ROCK_SUB_HURT, true));
|
||||
stats.put("_nonextra_ice_sub_hurt", new Stat("NONEXTRA_ICE_SUB_HURT", FightProperty.FIGHT_PROP_NONEXTRA_ICE_SUB_HURT, true));
|
||||
stats.put("_nonextra_skill_cd_minus_ratio", new Stat("NONEXTRA_SKILL_CD_MINUS_RATIO", FightProperty.FIGHT_PROP_NONEXTRA_SKILL_CD_MINUS_RATIO, true));
|
||||
stats.put("_nonextra_shield_cost_minus_ratio", new Stat("NONEXTRA_SHIELD_COST_MINUS_RATIO", FightProperty.FIGHT_PROP_NONEXTRA_SHIELD_COST_MINUS_RATIO, true));
|
||||
stats.put("_nonextra_physical_add_hurt", new Stat("NONEXTRA_PHYSICAL_ADD_HURT", FightProperty.FIGHT_PROP_NONEXTRA_PHYSICAL_ADD_HURT, true));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void execute(Player sender, Player targetPlayer, List<String> args) {
|
||||
String syntax = sender == null ? translate("commands.setStats.usage_console") : translate("commands.setStats.ingame");
|
||||
String usage = syntax + translate("commands.setStats.help_message");
|
||||
String statStr;
|
||||
String valueStr;
|
||||
|
||||
if (targetPlayer == null) {
|
||||
CommandHandler.sendMessage(sender, translate("commands.execution.need_target"));
|
||||
return;
|
||||
}
|
||||
|
||||
if (args.size() == 2) {
|
||||
statStr = args.get(0).toLowerCase();
|
||||
valueStr = args.get(1);
|
||||
} else {
|
||||
CommandHandler.sendMessage(sender, usage);
|
||||
return;
|
||||
}
|
||||
|
||||
EntityAvatar entity = targetPlayer.getTeamManager().getCurrentAvatarEntity();
|
||||
|
||||
float value;
|
||||
try {
|
||||
if (valueStr.endsWith("%")) {
|
||||
value = Float.parseFloat(valueStr.substring(0, valueStr.length()-1))/100f;
|
||||
} else {
|
||||
value = Float.parseFloat(valueStr);
|
||||
}
|
||||
} catch (NumberFormatException ignored) {
|
||||
CommandHandler.sendMessage(sender, translate("commands.setStats.value_error"));
|
||||
return;
|
||||
}
|
||||
|
||||
if (stats.containsKey(statStr)) {
|
||||
Stat stat = stats.get(statStr);
|
||||
entity.setFightProperty(stat.prop, value);
|
||||
entity.getWorld().broadcastPacket(new PacketEntityFightPropUpdateNotify(entity, stat.prop));
|
||||
if (stat.percent) {
|
||||
valueStr = String.format("%.1f%%", value*100f);
|
||||
} else {
|
||||
valueStr = String.format("%.0f", value);
|
||||
}
|
||||
if (targetPlayer == sender) {
|
||||
CommandHandler.sendMessage(sender, translate("commands.setStats.set_self", stat.name, valueStr));
|
||||
} else {
|
||||
String uidStr = targetPlayer.getAccount().getId();
|
||||
CommandHandler.sendMessage(sender, translate("commands.setStats.set_self", stat.name, uidStr, valueStr));
|
||||
}
|
||||
} else {
|
||||
CommandHandler.sendMessage(sender, usage);
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
@ -1,39 +1,45 @@
|
||||
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.props.PlayerProperty;
|
||||
import emu.grasscutter.game.player.Player;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import static emu.grasscutter.utils.Language.translate;
|
||||
|
||||
@Command(label = "setworldlevel", usage = "setworldlevel <level>",
|
||||
description = "Sets your world level (Relog to see proper effects)",
|
||||
aliases = {"setworldlvl"}, permission = "player.setworldlevel")
|
||||
public final class SetWorldLevelCommand implements CommandHandler {
|
||||
|
||||
@Override
|
||||
public void execute(GenshinPlayer sender, List<String> args) {
|
||||
if (sender == null) {
|
||||
CommandHandler.sendMessage(null, "Run this command in-game.");
|
||||
return; // TODO: set player's world level from console or other players
|
||||
public void execute(Player sender, Player targetPlayer, List<String> args) {
|
||||
if (targetPlayer == null) {
|
||||
CommandHandler.sendMessage(sender, translate("commands.execution.need_target"));
|
||||
return;
|
||||
}
|
||||
|
||||
if (args.size() < 1) {
|
||||
CommandHandler.sendMessage(sender, "Usage: setworldlevel <level>");
|
||||
CommandHandler.sendMessage(sender, translate("commands.setWorldLevel.usage"));
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
int level = Integer.parseInt(args.get(0));
|
||||
if (level > 8 || level < 0) {
|
||||
CommandHandler.sendMessage(sender, translate("commands.setWorldLevel.value_error"));
|
||||
return;
|
||||
}
|
||||
|
||||
// Set in both world and player props
|
||||
sender.getWorld().setWorldLevel(level);
|
||||
sender.setProperty(PlayerProperty.PROP_PLAYER_WORLD_LEVEL, level);
|
||||
targetPlayer.getWorld().setWorldLevel(level);
|
||||
targetPlayer.setWorldLevel(level);
|
||||
|
||||
sender.dropMessage("World level set to " + level + ".");
|
||||
CommandHandler.sendMessage(sender, translate("commands.setWorldLevel.success", Integer.toString(level)));
|
||||
} catch (NumberFormatException ignored) {
|
||||
CommandHandler.sendMessage(null, "Invalid world level.");
|
||||
CommandHandler.sendMessage(null, translate("commands.setWorldLevel.invalid_world_level"));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,51 +1,114 @@
|
||||
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.GameData;
|
||||
import emu.grasscutter.data.def.AvatarData;
|
||||
import emu.grasscutter.data.def.GadgetData;
|
||||
import emu.grasscutter.data.def.ItemData;
|
||||
import emu.grasscutter.data.def.MonsterData;
|
||||
import emu.grasscutter.game.GenshinPlayer;
|
||||
import emu.grasscutter.game.entity.EntityMonster;
|
||||
import emu.grasscutter.game.avatar.Avatar;
|
||||
import emu.grasscutter.game.entity.*;
|
||||
import emu.grasscutter.game.player.Player;
|
||||
import emu.grasscutter.game.props.EntityType;
|
||||
import emu.grasscutter.game.props.FightProperty;
|
||||
import emu.grasscutter.utils.Position;
|
||||
import emu.grasscutter.game.world.Scene;
|
||||
|
||||
import javax.swing.text.html.parser.Entity;
|
||||
import java.util.List;
|
||||
import java.util.Random;
|
||||
|
||||
@Command(label = "spawn", usage = "spawn <entityId|entityName> [level] [amount]",
|
||||
import static emu.grasscutter.utils.Language.translate;
|
||||
|
||||
@Command(label = "spawn", usage = "spawn <entityId> [amount] [level(monster only)]",
|
||||
description = "Spawns an entity near you", permission = "server.spawn")
|
||||
public final class SpawnCommand implements CommandHandler {
|
||||
|
||||
@Override
|
||||
public void execute(GenshinPlayer sender, List<String> args) {
|
||||
if (sender == null) {
|
||||
CommandHandler.sendMessage(null, "Run this command in-game.");
|
||||
public void execute(Player sender, Player targetPlayer, List<String> args) {
|
||||
if (targetPlayer == null) {
|
||||
CommandHandler.sendMessage(sender, translate("commands.execution.need_target"));
|
||||
return;
|
||||
}
|
||||
|
||||
if (args.size() < 1) {
|
||||
CommandHandler.sendMessage(sender, "Usage: spawn <entityId|entityName> [amount]");
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
int entity = Integer.parseInt(args.get(0));
|
||||
int level = args.size() > 1 ? Integer.parseInt(args.get(1)) : 1;
|
||||
int amount = args.size() > 2 ? Integer.parseInt(args.get(2)) : 1;
|
||||
|
||||
MonsterData entityData = GenshinData.getMonsterDataMap().get(entity);
|
||||
if (entityData == null) {
|
||||
CommandHandler.sendMessage(sender, "Invalid entity id.");
|
||||
int id = 0; // This is just to shut up the linter, it's not a real default
|
||||
int amount = 1;
|
||||
int level = 1;
|
||||
switch (args.size()) {
|
||||
case 3:
|
||||
try {
|
||||
level = Integer.parseInt(args.get(2));
|
||||
} catch (NumberFormatException ignored) {
|
||||
CommandHandler.sendMessage(sender, translate("commands.execution.argument_error"));
|
||||
} // Fallthrough
|
||||
case 2:
|
||||
try {
|
||||
amount = Integer.parseInt(args.get(1));
|
||||
} catch (NumberFormatException ignored) {
|
||||
CommandHandler.sendMessage(sender, translate("commands.generic.error.amount"));
|
||||
} // Fallthrough
|
||||
case 1:
|
||||
try {
|
||||
id = Integer.parseInt(args.get(0));
|
||||
} catch (NumberFormatException ignored) {
|
||||
CommandHandler.sendMessage(sender, translate("commands.generic.error.entityId"));
|
||||
}
|
||||
break;
|
||||
default:
|
||||
CommandHandler.sendMessage(sender, translate("commands.spawn.usage"));
|
||||
return;
|
||||
}
|
||||
|
||||
MonsterData monsterData = GameData.getMonsterDataMap().get(id);
|
||||
GadgetData gadgetData = GameData.getGadgetDataMap().get(id);
|
||||
ItemData itemData = GameData.getItemDataMap().get(id);
|
||||
if (monsterData == null && gadgetData == null && itemData == null) {
|
||||
CommandHandler.sendMessage(sender, translate("commands.generic.error.entityId"));
|
||||
return;
|
||||
}
|
||||
Scene scene = targetPlayer.getScene();
|
||||
|
||||
double maxRadius = Math.sqrt(amount * 0.2 / Math.PI);
|
||||
for (int i = 0; i < amount; i++) {
|
||||
Position pos = GetRandomPositionInCircle(targetPlayer.getPos(), maxRadius).addY(3);
|
||||
GameEntity entity = null;
|
||||
if (itemData != null) {
|
||||
entity = new EntityItem(scene, null, itemData, pos, 1, true);
|
||||
}
|
||||
if (gadgetData != null) {
|
||||
entity = new EntityVehicle(scene, targetPlayer.getSession().getPlayer(), gadgetData.getId(), 0, pos, targetPlayer.getRotation()); // TODO: does targetPlayer.getSession().getPlayer() have some meaning?
|
||||
int gadgetId = gadgetData.getId();
|
||||
switch (gadgetId) {
|
||||
// TODO: Not hardcode this. Waverider (skiff)
|
||||
case 45001001, 45001002 -> {
|
||||
entity.addFightProperty(FightProperty.FIGHT_PROP_BASE_HP, 10000);
|
||||
entity.addFightProperty(FightProperty.FIGHT_PROP_BASE_ATTACK, 100);
|
||||
entity.addFightProperty(FightProperty.FIGHT_PROP_CUR_ATTACK, 100);
|
||||
entity.addFightProperty(FightProperty.FIGHT_PROP_CUR_HP, 10000);
|
||||
entity.addFightProperty(FightProperty.FIGHT_PROP_CUR_DEFENSE, 0);
|
||||
entity.addFightProperty(FightProperty.FIGHT_PROP_CUR_SPEED, 0);
|
||||
entity.addFightProperty(FightProperty.FIGHT_PROP_CHARGE_EFFICIENCY, 0);
|
||||
entity.addFightProperty(FightProperty.FIGHT_PROP_MAX_HP, 10000);
|
||||
}
|
||||
default -> {}
|
||||
}
|
||||
}
|
||||
if (monsterData != null) {
|
||||
entity = new EntityMonster(scene, monsterData, pos, level);
|
||||
}
|
||||
|
||||
float range = (5f + (.1f * amount));
|
||||
for (int i = 0; i < amount; i++) {
|
||||
Position pos = sender.getPos().clone().addX((float) (Math.random() * range) - (range / 2)).addY(3f).addZ((float) (Math.random() * range) - (range / 2));
|
||||
EntityMonster monster = new EntityMonster(sender.getScene(), entityData, pos, level);
|
||||
sender.getScene().addEntity(monster);
|
||||
}
|
||||
CommandHandler.sendMessage(sender, String.format("Spawned %s of %s.", amount, entity));
|
||||
} catch (NumberFormatException ignored) {
|
||||
CommandHandler.sendMessage(sender, "Invalid item or player ID.");
|
||||
scene.addEntity(entity);
|
||||
}
|
||||
CommandHandler.sendMessage(sender, translate("commands.spawn.success", Integer.toString(amount), Integer.toString(id)));
|
||||
}
|
||||
|
||||
private Position GetRandomPositionInCircle(Position origin, double radius){
|
||||
Position target = origin.clone();
|
||||
double angle = Math.random() * 360;
|
||||
double r = Math.sqrt(Math.random() * radius * radius);
|
||||
target.addX((float) (r * Math.cos(angle))).addZ((float) (r * Math.sin(angle)));
|
||||
return target;
|
||||
}
|
||||
}
|
||||
|
@ -3,21 +3,23 @@ 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.player.Player;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import static emu.grasscutter.utils.Language.translate;
|
||||
|
||||
@Command(label = "stop", usage = "stop",
|
||||
description = "Stops the server", permission = "server.stop")
|
||||
public final class StopCommand implements CommandHandler {
|
||||
|
||||
@Override
|
||||
public void execute(GenshinPlayer sender, List<String> args) {
|
||||
CommandHandler.sendMessage(null, "Server shutting down...");
|
||||
for (GenshinPlayer p : Grasscutter.getGameServer().getPlayers().values()) {
|
||||
CommandHandler.sendMessage(p, "Server shutting down...");
|
||||
public void execute(Player sender, Player targetPlayer, List<String> args) {
|
||||
CommandHandler.sendMessage(null, translate("commands.stop.success"));
|
||||
for (Player p : Grasscutter.getGameServer().getPlayers().values()) {
|
||||
CommandHandler.sendMessage(p, translate("commands.stop.success"));
|
||||
}
|
||||
|
||||
System.exit(1);
|
||||
System.exit(1000);
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,115 @@
|
||||
package emu.grasscutter.command.commands;
|
||||
|
||||
import emu.grasscutter.Grasscutter;
|
||||
import emu.grasscutter.command.Command;
|
||||
import emu.grasscutter.command.CommandHandler;
|
||||
import emu.grasscutter.data.def.AvatarSkillDepotData;
|
||||
import emu.grasscutter.game.avatar.Avatar;
|
||||
import emu.grasscutter.game.entity.EntityAvatar;
|
||||
import emu.grasscutter.game.player.Player;
|
||||
import emu.grasscutter.server.packet.send.PacketAvatarSkillChangeNotify;
|
||||
import emu.grasscutter.server.packet.send.PacketAvatarSkillUpgradeRsp;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import static emu.grasscutter.utils.Language.translate;
|
||||
|
||||
@Command(label = "talent", usage = "talent <talentID> <value>",
|
||||
description = "Set talent level for your current active character", permission = "player.settalent")
|
||||
public final class TalentCommand implements CommandHandler {
|
||||
private void setTalentLevel(Player sender, Player player, Avatar avatar, int talentId, int talentLevel) {
|
||||
int oldLevel = avatar.getSkillLevelMap().get(talentId);
|
||||
if (talentLevel < 0 || talentLevel > 15) {
|
||||
CommandHandler.sendMessage(sender, translate("commands.talent.lower_16"));
|
||||
return;
|
||||
}
|
||||
|
||||
// Upgrade skill
|
||||
avatar.getSkillLevelMap().put(talentLevel, talentLevel);
|
||||
avatar.save();
|
||||
|
||||
// Packet
|
||||
player.sendPacket(new PacketAvatarSkillChangeNotify(avatar, talentId, oldLevel, talentLevel));
|
||||
player.sendPacket(new PacketAvatarSkillUpgradeRsp(avatar, talentId, oldLevel, talentLevel));
|
||||
|
||||
String successMessage = "commands.talent.set_id";
|
||||
AvatarSkillDepotData depot = avatar.getData().getSkillDepot();
|
||||
if (talentId == depot.getSkills().get(0)) {
|
||||
successMessage = "commands.talent.set_atk";
|
||||
} else if (talentId == depot.getSkills().get(1)) {
|
||||
successMessage = "commands.talent.set_e";
|
||||
} else if (talentId == depot.getEnergySkill()) {
|
||||
successMessage = "commands.talent.set_q";
|
||||
}
|
||||
CommandHandler.sendMessage(sender, translate(successMessage, talentLevel));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void execute(Player sender, Player targetPlayer, List<String> args) {
|
||||
if (targetPlayer == null) {
|
||||
CommandHandler.sendMessage(sender, translate("commands.execution.need_target"));
|
||||
return;
|
||||
}
|
||||
|
||||
if (args.size() < 1){
|
||||
CommandHandler.sendMessage(sender, translate("commands.talent.usage_1"));
|
||||
CommandHandler.sendMessage(sender, translate("commands.talent.usage_2"));
|
||||
CommandHandler.sendMessage(sender, translate("commands.talent.usage_3"));
|
||||
return;
|
||||
}
|
||||
|
||||
EntityAvatar entity = targetPlayer.getTeamManager().getCurrentAvatarEntity();
|
||||
Avatar avatar = entity.getAvatar();
|
||||
String cmdSwitch = args.get(0);
|
||||
switch (cmdSwitch) {
|
||||
default -> {
|
||||
CommandHandler.sendMessage(sender, translate("commands.talent.usage_1"));
|
||||
CommandHandler.sendMessage(sender, translate("commands.talent.usage_2"));
|
||||
CommandHandler.sendMessage(sender, translate("commands.talent.usage_3"));
|
||||
return;
|
||||
}
|
||||
case "set" -> {
|
||||
if (args.size() < 3) {
|
||||
CommandHandler.sendMessage(sender, translate("commands.talent.usage_1"));
|
||||
CommandHandler.sendMessage(sender, translate("commands.talent.usage_3"));
|
||||
return;
|
||||
}
|
||||
try {
|
||||
int skillId = Integer.parseInt(args.get(1));
|
||||
int newLevel = Integer.parseInt(args.get(2));
|
||||
setTalentLevel(sender, targetPlayer, avatar, skillId, newLevel);
|
||||
} catch (NumberFormatException ignored) {
|
||||
CommandHandler.sendMessage(sender, translate("commands.talent.invalid_skill_id"));
|
||||
return;
|
||||
}
|
||||
}
|
||||
case "n", "e", "q" -> {
|
||||
if (args.size() < 2) {
|
||||
CommandHandler.sendMessage(sender, translate("commands.talent.usage_2"));
|
||||
return;
|
||||
}
|
||||
AvatarSkillDepotData SkillDepot = avatar.getData().getSkillDepot();
|
||||
int skillId = switch (cmdSwitch) {
|
||||
default -> SkillDepot.getSkills().get(0);
|
||||
case "e" -> SkillDepot.getSkills().get(1);
|
||||
case "q" -> SkillDepot.getEnergySkill();
|
||||
};
|
||||
try {
|
||||
int newLevel = Integer.parseInt(args.get(1));
|
||||
setTalentLevel(sender, targetPlayer, avatar, skillId, newLevel);
|
||||
} catch (NumberFormatException ignored) {
|
||||
CommandHandler.sendMessage(sender, translate("commands.talent.invalid_level"));
|
||||
return;
|
||||
}
|
||||
}
|
||||
case "getid" -> {
|
||||
int skillIdNorAtk = avatar.getData().getSkillDepot().getSkills().get(0);
|
||||
int skillIdE = avatar.getData().getSkillDepot().getSkills().get(1);
|
||||
int skillIdQ = avatar.getData().getSkillDepot().getEnergySkill();
|
||||
CommandHandler.sendMessage(sender, translate("commands.talent.normal_attack_id", Integer.toString(skillIdNorAtk)));
|
||||
CommandHandler.sendMessage(sender, translate("commands.talent.e_skill_id", Integer.toString(skillIdE)));
|
||||
CommandHandler.sendMessage(sender, translate("commands.talent.q_skill_id", Integer.toString(skillIdQ)));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,38 @@
|
||||
package emu.grasscutter.command.commands;
|
||||
|
||||
import emu.grasscutter.Grasscutter;
|
||||
import emu.grasscutter.command.Command;
|
||||
import emu.grasscutter.command.CommandHandler;
|
||||
import emu.grasscutter.game.player.Player;
|
||||
import emu.grasscutter.utils.Position;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import static emu.grasscutter.utils.Language.translate;
|
||||
|
||||
@Command(label = "tpall", usage = "tpall",
|
||||
description = "Teleports all players in your world to your position", permission = "player.tpall")
|
||||
public final class TeleportAllCommand implements CommandHandler {
|
||||
@Override
|
||||
public void execute(Player sender, Player targetPlayer, List<String> args) {
|
||||
if (targetPlayer == null) {
|
||||
CommandHandler.sendMessage(sender, translate("commands.execution.need_target"));
|
||||
return;
|
||||
}
|
||||
|
||||
if (!targetPlayer.getWorld().isMultiplayer()) {
|
||||
CommandHandler.sendMessage(sender, translate("commands.teleportAll.error"));
|
||||
return;
|
||||
}
|
||||
|
||||
for (Player player : targetPlayer.getWorld().getPlayers()) {
|
||||
if (player.equals(targetPlayer))
|
||||
continue;
|
||||
Position pos = targetPlayer.getPos();
|
||||
|
||||
player.getWorld().transferPlayerToScene(player, targetPlayer.getSceneId(), pos);
|
||||
}
|
||||
|
||||
CommandHandler.sendMessage(sender, translate("commands.teleportAll.success"));
|
||||
}
|
||||
}
|
@ -0,0 +1,74 @@
|
||||
package emu.grasscutter.command.commands;
|
||||
|
||||
import emu.grasscutter.Grasscutter;
|
||||
import emu.grasscutter.command.Command;
|
||||
import emu.grasscutter.command.CommandHandler;
|
||||
import emu.grasscutter.game.player.Player;
|
||||
import emu.grasscutter.utils.Position;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import static emu.grasscutter.utils.Language.translate;
|
||||
|
||||
@Command(label = "teleport", usage = "teleport <x> <y> <z> [scene id]", aliases = {"tp"},
|
||||
description = "Change the player's position.", permission = "player.teleport")
|
||||
public final class TeleportCommand implements CommandHandler {
|
||||
|
||||
private float parseRelative(String input, Float current) { // TODO: Maybe this will be useful elsewhere later
|
||||
if (input.contains("~")) { // Relative
|
||||
if (!input.equals("~")) { // Relative with offset
|
||||
current += Float.parseFloat(input.replace("~", ""));
|
||||
} // Else no offset, no modification
|
||||
} else { // Absolute
|
||||
current = Float.parseFloat(input);
|
||||
}
|
||||
return current;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void execute(Player sender, Player targetPlayer, List<String> args) {
|
||||
if (targetPlayer == null) {
|
||||
CommandHandler.sendMessage(sender, translate("commands.execution.need_target"));
|
||||
return;
|
||||
}
|
||||
|
||||
Position pos = targetPlayer.getPos();
|
||||
float x = pos.getX();
|
||||
float y = pos.getY();
|
||||
float z = pos.getZ();
|
||||
int sceneId = targetPlayer.getSceneId();
|
||||
|
||||
switch (args.size()) {
|
||||
case 4:
|
||||
try {
|
||||
sceneId = Integer.parseInt(args.get(3));
|
||||
}catch (NumberFormatException ignored) {
|
||||
CommandHandler.sendMessage(sender, translate("commands.execution.argument_error"));
|
||||
} // Fallthrough
|
||||
case 3:
|
||||
try {
|
||||
x = parseRelative(args.get(0), x);
|
||||
y = parseRelative(args.get(1), y);
|
||||
z = parseRelative(args.get(2), z);
|
||||
} catch (NumberFormatException ignored) {
|
||||
CommandHandler.sendMessage(sender, translate("commands.teleport.invalid_position"));
|
||||
}
|
||||
break;
|
||||
default:
|
||||
CommandHandler.sendMessage(sender, translate("commands.teleport.usage"));
|
||||
return;
|
||||
}
|
||||
|
||||
Position target_pos = new Position(x, y, z);
|
||||
boolean result = targetPlayer.getWorld().transferPlayerToScene(targetPlayer, sceneId, target_pos);
|
||||
if (!result) {
|
||||
CommandHandler.sendMessage(sender, translate("commands.teleport.invalid_position"));
|
||||
} else {
|
||||
CommandHandler.sendMessage(sender, translate("commands.teleport.success",
|
||||
targetPlayer.getNickname(), Float.toString(x), Float.toString(y),
|
||||
Float.toString(z), Integer.toString(sceneId))
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
@ -1,41 +1,53 @@
|
||||
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.player.Player;
|
||||
import emu.grasscutter.game.props.ClimateType;
|
||||
import emu.grasscutter.server.packet.send.PacketSceneAreaWeatherNotify;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import static emu.grasscutter.utils.Language.translate;
|
||||
|
||||
@Command(label = "weather", usage = "weather <weatherId> [climateId]",
|
||||
description = "Changes the weather.", aliases = {"w"}, permission = "player.weather")
|
||||
public final class WeatherCommand implements CommandHandler {
|
||||
|
||||
@Override
|
||||
public void execute(GenshinPlayer sender, List<String> args) {
|
||||
if (sender == null) {
|
||||
CommandHandler.sendMessage(null, "Run this command in-game.");
|
||||
public void execute(Player sender, Player targetPlayer, List<String> args) {
|
||||
if (targetPlayer == null) {
|
||||
CommandHandler.sendMessage(sender, translate("commands.execution.need_target"));
|
||||
return;
|
||||
}
|
||||
|
||||
if (args.size() < 1) {
|
||||
CommandHandler.sendMessage(sender, "Usage: weather <weatherId> [climateId]");
|
||||
return;
|
||||
int weatherId = 0;
|
||||
int climateId = 1;
|
||||
switch (args.size()) {
|
||||
case 2:
|
||||
try {
|
||||
climateId = Integer.parseInt(args.get(1));
|
||||
} catch (NumberFormatException ignored) {
|
||||
CommandHandler.sendMessage(sender, translate("commands.weather.invalid_id"));
|
||||
}
|
||||
case 1:
|
||||
try {
|
||||
weatherId = Integer.parseInt(args.get(0));
|
||||
} catch (NumberFormatException ignored) {
|
||||
CommandHandler.sendMessage(sender, translate("commands.weather.invalid_id"));
|
||||
}
|
||||
break;
|
||||
default:
|
||||
CommandHandler.sendMessage(sender, translate("commands.weather.usage"));
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
int weatherId = Integer.parseInt(args.get(0));
|
||||
int climateId = args.size() > 1 ? Integer.parseInt(args.get(1)) : 1;
|
||||
ClimateType climate = ClimateType.getTypeByValue(climateId);
|
||||
|
||||
ClimateType climate = ClimateType.getTypeByValue(climateId);
|
||||
|
||||
sender.getScene().setWeather(weatherId);
|
||||
sender.getScene().setClimate(climate);
|
||||
sender.getScene().broadcastPacket(new PacketSceneAreaWeatherNotify(sender));
|
||||
CommandHandler.sendMessage(sender, "Changed weather to " + weatherId + " with climate " + climateId);
|
||||
} catch (NumberFormatException ignored) {
|
||||
CommandHandler.sendMessage(sender, "Invalid ID.");
|
||||
}
|
||||
targetPlayer.getScene().setWeather(weatherId);
|
||||
targetPlayer.getScene().setClimate(climate);
|
||||
targetPlayer.getScene().broadcastPacket(new PacketSceneAreaWeatherNotify(targetPlayer));
|
||||
CommandHandler.sendMessage(sender, translate("commands.weather.success", Integer.toString(weatherId), Integer.toString(climateId)));
|
||||
}
|
||||
}
|
||||
|
@ -15,8 +15,10 @@ import emu.grasscutter.data.def.*;
|
||||
import it.unimi.dsi.fastutil.ints.Int2ObjectLinkedOpenHashMap;
|
||||
import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
|
||||
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
|
||||
import it.unimi.dsi.fastutil.ints.IntArrayList;
|
||||
import it.unimi.dsi.fastutil.ints.IntList;
|
||||
|
||||
public class GenshinData {
|
||||
public class GameData {
|
||||
// BinOutputs
|
||||
private static final Int2ObjectMap<String> abilityHashes = new Int2ObjectOpenHashMap<>();
|
||||
private static final Map<String, AbilityEmbryoEntry> abilityEmbryos = new HashMap<>();
|
||||
@ -31,6 +33,7 @@ public class GenshinData {
|
||||
private static final Int2ObjectMap<AvatarSkillDepotData> avatarSkillDepotDataMap = new Int2ObjectOpenHashMap<>();
|
||||
private static final Int2ObjectMap<AvatarSkillData> avatarSkillDataMap = new Int2ObjectOpenHashMap<>();
|
||||
private static final Int2ObjectMap<AvatarCurveData> avatarCurveDataMap = new Int2ObjectLinkedOpenHashMap<>();
|
||||
private static final Int2ObjectMap<AvatarFetterLevelData> avatarFetterLevelDataMap = new Int2ObjectOpenHashMap<>();
|
||||
private static final Int2ObjectMap<AvatarPromoteData> avatarPromoteDataMap = new Int2ObjectOpenHashMap<>();
|
||||
private static final Int2ObjectMap<AvatarTalentData> avatarTalentDataMap = new Int2ObjectOpenHashMap<>();
|
||||
private static final Int2ObjectMap<ProudSkillData> proudSkillDataMap = new Int2ObjectOpenHashMap<>();
|
||||
@ -57,15 +60,27 @@ public class GenshinData {
|
||||
|
||||
private static final Int2ObjectMap<SceneData> sceneDataMap = new Int2ObjectLinkedOpenHashMap<>();
|
||||
private static final Int2ObjectMap<FetterData> fetterDataMap = new Int2ObjectOpenHashMap<>();
|
||||
private static final Int2ObjectMap<FetterCharacterCardData> fetterCharacterCardDataMap = new Int2ObjectOpenHashMap<>();
|
||||
private static final Int2ObjectMap<RewardData> rewardDataMap = new Int2ObjectOpenHashMap<>();
|
||||
private static final Int2ObjectMap<WorldLevelData> worldLevelDataMap = new Int2ObjectOpenHashMap<>();
|
||||
private static final Int2ObjectMap<DailyDungeonData> dailyDungeonDataMap = new Int2ObjectOpenHashMap<>();
|
||||
private static final Int2ObjectMap<DungeonData> dungeonDataMap = new Int2ObjectOpenHashMap<>();
|
||||
private static final Int2ObjectMap<ShopGoodsData> shopGoodsDataMap = new Int2ObjectOpenHashMap<>();
|
||||
private static final Int2ObjectMap<CombineData> combineDataMap = new Int2ObjectOpenHashMap<>();
|
||||
private static final Int2ObjectMap<RewardPreviewData> rewardPreviewDataMap = new Int2ObjectOpenHashMap<>();
|
||||
private static final Int2ObjectMap<TowerFloorData> towerFloorDataMap = new Int2ObjectOpenHashMap<>();
|
||||
private static final Int2ObjectMap<TowerLevelData> towerLevelDataMap = new Int2ObjectOpenHashMap<>();
|
||||
|
||||
// Cache
|
||||
private static Map<Integer, List<Integer>> fetters = new HashMap<>();
|
||||
private static Map<Integer, List<ShopGoodsData>> shopGoods = new HashMap<>();
|
||||
private static final IntList scenePointIdList = new IntArrayList();
|
||||
|
||||
public static Int2ObjectMap<?> getMapByResourceDef(Class<?> resourceDefinition) {
|
||||
Int2ObjectMap<?> map = null;
|
||||
|
||||
try {
|
||||
Field field = GenshinData.class.getDeclaredField(Utils.lowerCaseFirstChar(resourceDefinition.getSimpleName()) + "Map");
|
||||
Field field = GameData.class.getDeclaredField(Utils.lowerCaseFirstChar(resourceDefinition.getSimpleName()) + "Map");
|
||||
field.setAccessible(true);
|
||||
|
||||
map = (Int2ObjectMap<?>) field.get(null);
|
||||
@ -93,6 +108,11 @@ public class GenshinData {
|
||||
public static Map<String, ScenePointEntry> getScenePointEntries() {
|
||||
return scenePointEntries;
|
||||
}
|
||||
|
||||
// TODO optimize
|
||||
public static ScenePointEntry getScenePointEntryById(int sceneId, int pointId) {
|
||||
return getScenePointEntries().get(sceneId + "_" + pointId);
|
||||
}
|
||||
|
||||
public static Int2ObjectMap<AvatarData> getAvatarDataMap() {
|
||||
return avatarDataMap;
|
||||
@ -114,6 +134,14 @@ public class GenshinData {
|
||||
return playerLevelDataMap;
|
||||
}
|
||||
|
||||
public static Int2ObjectMap<AvatarFetterLevelData> getAvatarFetterLevelDataMap() {
|
||||
return avatarFetterLevelDataMap;
|
||||
}
|
||||
|
||||
public static Int2ObjectMap<FetterCharacterCardData> getFetterCharacterCardDataMap() {
|
||||
return fetterCharacterCardDataMap;
|
||||
}
|
||||
|
||||
public static Int2ObjectMap<AvatarLevelData> getAvatarLevelDataMap() {
|
||||
return avatarLevelDataMap;
|
||||
}
|
||||
@ -175,6 +203,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<ProudSkillData> getProudSkillDataMap() {
|
||||
return proudSkillDataMap;
|
||||
@ -228,6 +261,10 @@ public class GenshinData {
|
||||
return sceneDataMap;
|
||||
}
|
||||
|
||||
public static Int2ObjectMap<RewardData> getRewardDataMap() {
|
||||
return rewardDataMap;
|
||||
}
|
||||
|
||||
public static Map<Integer, List<Integer>> getFetterDataEntries() {
|
||||
if (fetters.isEmpty()) {
|
||||
fetterDataMap.forEach((k, v) -> {
|
||||
@ -240,4 +277,47 @@ public class GenshinData {
|
||||
|
||||
return fetters;
|
||||
}
|
||||
|
||||
public static Int2ObjectMap<WorldLevelData> getWorldLevelDataMap() {
|
||||
return worldLevelDataMap;
|
||||
}
|
||||
|
||||
public static Int2ObjectMap<DungeonData> getDungeonDataMap() {
|
||||
return dungeonDataMap;
|
||||
}
|
||||
|
||||
public static Int2ObjectMap<DailyDungeonData> getDailyDungeonDataMap() {
|
||||
return dailyDungeonDataMap;
|
||||
}
|
||||
|
||||
public static Map<Integer, List<ShopGoodsData>> getShopGoodsDataEntries() {
|
||||
if (shopGoods.isEmpty()) {
|
||||
shopGoodsDataMap.forEach((k, v) -> {
|
||||
if (!shopGoods.containsKey(v.getShopType()))
|
||||
shopGoods.put(v.getShopType(), new ArrayList<>());
|
||||
shopGoods.get(v.getShopType()).add(v);
|
||||
});
|
||||
}
|
||||
|
||||
return shopGoods;
|
||||
}
|
||||
|
||||
public static Int2ObjectMap<RewardPreviewData> getRewardPreviewDataMap() {
|
||||
return rewardPreviewDataMap;
|
||||
}
|
||||
|
||||
public static IntList getScenePointIdList() {
|
||||
return scenePointIdList;
|
||||
}
|
||||
|
||||
public static Int2ObjectMap<CombineData> getCombineDataMap() {
|
||||
return combineDataMap;
|
||||
}
|
||||
|
||||
public static Int2ObjectMap<TowerFloorData> getTowerFloorDataMap(){
|
||||
return towerFloorDataMap;
|
||||
}
|
||||
public static Int2ObjectMap<TowerLevelData> getTowerLevelDataMap(){
|
||||
return towerLevelDataMap;
|
||||
}
|
||||
}
|
@ -3,26 +3,33 @@ package emu.grasscutter.data;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import org.danilopianini.util.FlexibleQuadTree;
|
||||
import org.danilopianini.util.SpatialIndex;
|
||||
|
||||
import emu.grasscutter.Grasscutter;
|
||||
import emu.grasscutter.data.def.ReliquaryAffixData;
|
||||
import emu.grasscutter.data.def.ReliquaryMainPropData;
|
||||
import emu.grasscutter.game.world.SpawnDataEntry;
|
||||
import emu.grasscutter.game.world.SpawnDataEntry.SpawnGroupEntry;
|
||||
import emu.grasscutter.utils.WeightedList;
|
||||
import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
|
||||
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
|
||||
|
||||
public class GenshinDepot {
|
||||
public class GameDepot {
|
||||
private static Int2ObjectMap<WeightedList<ReliquaryMainPropData>> relicMainPropDepot = new Int2ObjectOpenHashMap<>();
|
||||
private static Int2ObjectMap<List<ReliquaryAffixData>> relicAffixDepot = new Int2ObjectOpenHashMap<>();
|
||||
|
||||
private static Int2ObjectMap<SpatialIndex<SpawnGroupEntry>> spawnLists = new Int2ObjectOpenHashMap<>();
|
||||
|
||||
public static void load() {
|
||||
for (ReliquaryMainPropData data : GenshinData.getReliquaryMainPropDataMap().values()) {
|
||||
for (ReliquaryMainPropData data : GameData.getReliquaryMainPropDataMap().values()) {
|
||||
if (data.getWeight() <= 0 || data.getPropDepotId() <= 0) {
|
||||
continue;
|
||||
}
|
||||
WeightedList<ReliquaryMainPropData> list = relicMainPropDepot.computeIfAbsent(data.getPropDepotId(), k -> new WeightedList<>());
|
||||
list.add(data.getWeight(), data);
|
||||
}
|
||||
for (ReliquaryAffixData data : GenshinData.getReliquaryAffixDataMap().values()) {
|
||||
for (ReliquaryAffixData data : GameData.getReliquaryAffixDataMap().values()) {
|
||||
if (data.getWeight() <= 0 || data.getDepotId() <= 0) {
|
||||
continue;
|
||||
}
|
||||
@ -46,4 +53,12 @@ public class GenshinDepot {
|
||||
public static List<ReliquaryAffixData> getRandomRelicAffixList(int depot) {
|
||||
return relicAffixDepot.get(depot);
|
||||
}
|
||||
|
||||
public static Int2ObjectMap<SpatialIndex<SpawnGroupEntry>> getSpawnLists() {
|
||||
return spawnLists;
|
||||
}
|
||||
|
||||
public static SpatialIndex<SpawnGroupEntry> getSpawnListById(int sceneId) {
|
||||
return getSpawnLists().computeIfAbsent(sceneId, id -> new FlexibleQuadTree<>());
|
||||
}
|
||||
}
|
@ -1,6 +1,6 @@
|
||||
package emu.grasscutter.data;
|
||||
|
||||
public abstract class GenshinResource {
|
||||
public abstract class GameResource {
|
||||
|
||||
public int getId() {
|
||||
return 0;
|
@ -7,6 +7,7 @@ import java.util.Map.Entry;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import com.google.gson.Gson;
|
||||
import emu.grasscutter.utils.Utils;
|
||||
import org.reflections.Reflections;
|
||||
|
||||
@ -19,13 +20,15 @@ import emu.grasscutter.data.common.ScenePointConfig;
|
||||
import emu.grasscutter.data.custom.AbilityEmbryoEntry;
|
||||
import emu.grasscutter.data.custom.OpenConfigEntry;
|
||||
import emu.grasscutter.data.custom.ScenePointEntry;
|
||||
import emu.grasscutter.game.world.SpawnDataEntry;
|
||||
import emu.grasscutter.game.world.SpawnDataEntry.SpawnGroupEntry;
|
||||
import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
|
||||
|
||||
public class ResourceLoader {
|
||||
|
||||
public static List<Class<?>> getResourceDefClasses() {
|
||||
Reflections reflections = new Reflections(ResourceLoader.class.getPackage().getName());
|
||||
Set<?> classes = reflections.getSubTypesOf(GenshinResource.class);
|
||||
Set<?> classes = reflections.getSubTypesOf(GameResource.class);
|
||||
|
||||
List<Class<?>> classList = new ArrayList<>(classes.size());
|
||||
classes.forEach(o -> {
|
||||
@ -46,12 +49,15 @@ public class ResourceLoader {
|
||||
loadOpenConfig();
|
||||
// Load resources
|
||||
loadResources();
|
||||
loadScenePoints();
|
||||
// Process into depots
|
||||
GenshinDepot.load();
|
||||
GameDepot.load();
|
||||
// Load spawn data
|
||||
loadSpawnData();
|
||||
// Load scene points - must be done AFTER resources are loaded
|
||||
loadScenePoints();
|
||||
// Custom - TODO move this somewhere else
|
||||
try {
|
||||
GenshinData.getAvatarSkillDepotDataMap().get(504).setAbilities(
|
||||
GameData.getAvatarSkillDepotDataMap().get(504).setAbilities(
|
||||
new AbilityEmbryoEntry(
|
||||
"",
|
||||
new String[] {
|
||||
@ -64,7 +70,7 @@ public class ResourceLoader {
|
||||
"Avatar_Player_WindBreathe_CameraController"
|
||||
}
|
||||
));
|
||||
GenshinData.getAvatarSkillDepotDataMap().get(704).setAbilities(
|
||||
GameData.getAvatarSkillDepotDataMap().get(704).setAbilities(
|
||||
new AbilityEmbryoEntry(
|
||||
"",
|
||||
new String[] {
|
||||
@ -91,7 +97,7 @@ public class ResourceLoader {
|
||||
}
|
||||
|
||||
@SuppressWarnings("rawtypes")
|
||||
Int2ObjectMap map = GenshinData.getMapByResourceDef(resourceDefinition);
|
||||
Int2ObjectMap map = GameData.getMapByResourceDef(resourceDefinition);
|
||||
|
||||
if (map == null) {
|
||||
continue;
|
||||
@ -115,14 +121,15 @@ public class ResourceLoader {
|
||||
|
||||
@SuppressWarnings({"rawtypes", "unchecked"})
|
||||
protected static void loadFromResource(Class<?> c, String fileName, Int2ObjectMap map) throws Exception {
|
||||
try (FileReader fileReader = new FileReader(Grasscutter.getConfig().RESOURCE_FOLDER + "ExcelBinOutput/" + fileName)) {
|
||||
List list = Grasscutter.getGsonFactory().fromJson(fileReader, TypeToken.getParameterized(Collection.class, c).getType());
|
||||
FileReader fileReader = new FileReader(Grasscutter.getConfig().RESOURCE_FOLDER + "ExcelBinOutput/" + fileName);
|
||||
Gson gson = Grasscutter.getGsonFactory();
|
||||
List list = gson.fromJson(fileReader, List.class);
|
||||
|
||||
for (Object o : list) {
|
||||
GenshinResource res = (GenshinResource) o;
|
||||
res.onLoad();
|
||||
map.put(res.getId(), res);
|
||||
}
|
||||
for (Object o : list) {
|
||||
Map<String, Object> tempMap = Utils.switchPropertiesUpperLowerCase((Map<String, Object>) o, c);
|
||||
GameResource res = gson.fromJson(gson.toJson(tempMap), TypeToken.get(c).getType());
|
||||
res.onLoad();
|
||||
map.put(res.getId(), res);
|
||||
}
|
||||
}
|
||||
|
||||
@ -136,7 +143,7 @@ public class ResourceLoader {
|
||||
}
|
||||
|
||||
List<ScenePointEntry> scenePointList = new ArrayList<>();
|
||||
for (File file : folder.listFiles()) {
|
||||
for (File file : Objects.requireNonNull(folder.listFiles())) {
|
||||
ScenePointConfig config = null;
|
||||
Integer sceneId = null;
|
||||
|
||||
@ -160,13 +167,17 @@ public class ResourceLoader {
|
||||
|
||||
for (Map.Entry<String, JsonElement> entry : config.points.entrySet()) {
|
||||
PointData pointData = Grasscutter.getGsonFactory().fromJson(entry.getValue(), PointData.class);
|
||||
pointData.setId(Integer.parseInt(entry.getKey()));
|
||||
|
||||
ScenePointEntry sl = new ScenePointEntry(sceneId + "_" + entry.getKey(), pointData);
|
||||
scenePointList.add(sl);
|
||||
GameData.getScenePointIdList().add(pointData.getId());
|
||||
|
||||
pointData.updateDailyDungeon();
|
||||
}
|
||||
|
||||
for (ScenePointEntry entry : scenePointList) {
|
||||
GenshinData.getScenePointEntries().put(entry.getName(), entry);
|
||||
GameData.getScenePointEntries().put(entry.getName(), entry);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -186,7 +197,7 @@ public class ResourceLoader {
|
||||
} else {
|
||||
// Load from BinOutput
|
||||
Pattern pattern = Pattern.compile("(?<=ConfigAvatar_)(.*?)(?=.json)");
|
||||
|
||||
|
||||
embryoList = new LinkedList<>();
|
||||
File folder = new File(Utils.toFilePath(Grasscutter.getConfig().RESOURCE_FOLDER + "BinOutput/Avatar/"));
|
||||
File[] files = folder.listFiles();
|
||||
@ -194,29 +205,29 @@ public class ResourceLoader {
|
||||
Grasscutter.getLogger().error("Error loading ability embryos: no files found in " + folder.getAbsolutePath());
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
for (File file : files) {
|
||||
AvatarConfig config;
|
||||
String avatarName;
|
||||
|
||||
|
||||
Matcher matcher = pattern.matcher(file.getName());
|
||||
if (matcher.find()) {
|
||||
avatarName = matcher.group(0);
|
||||
} else {
|
||||
continue;
|
||||
}
|
||||
|
||||
|
||||
try (FileReader fileReader = new FileReader(file)) {
|
||||
config = Grasscutter.getGsonFactory().fromJson(fileReader, AvatarConfig.class);
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
continue;
|
||||
}
|
||||
|
||||
|
||||
if (config.abilities == null) {
|
||||
continue;
|
||||
}
|
||||
|
||||
|
||||
int s = config.abilities.size();
|
||||
AbilityEmbryoEntry al = new AbilityEmbryoEntry(avatarName, config.abilities.stream().map(Object::toString).toArray(size -> new String[s]));
|
||||
embryoList.add(al);
|
||||
@ -229,7 +240,34 @@ public class ResourceLoader {
|
||||
}
|
||||
|
||||
for (AbilityEmbryoEntry entry : embryoList) {
|
||||
GenshinData.getAbilityEmbryoInfo().put(entry.getName(), entry);
|
||||
GameData.getAbilityEmbryoInfo().put(entry.getName(), entry);
|
||||
}
|
||||
}
|
||||
|
||||
private static void loadSpawnData() {
|
||||
// Read from cached file if exists
|
||||
File spawnDataEntries = new File(Grasscutter.getConfig().DATA_FOLDER + "Spawns.json");
|
||||
List<SpawnGroupEntry> spawnEntryList = null;
|
||||
|
||||
if (spawnDataEntries.exists()) {
|
||||
// Load from cache
|
||||
try (FileReader fileReader = new FileReader(spawnDataEntries)) {
|
||||
spawnEntryList = Grasscutter.getGsonFactory().fromJson(fileReader, TypeToken.getParameterized(Collection.class, SpawnGroupEntry.class).getType());
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
if (spawnEntryList == null || spawnEntryList.isEmpty()) {
|
||||
Grasscutter.getLogger().error("No spawn data loaded!");
|
||||
return;
|
||||
}
|
||||
|
||||
for (SpawnGroupEntry entry : spawnEntryList) {
|
||||
entry.getSpawns().stream().forEach(s -> {
|
||||
s.setGroup(entry);
|
||||
});
|
||||
GameDepot.getSpawnListById(entry.getSceneId()).insert(entry, entry.getPos().getX(), entry.getPos().getZ());
|
||||
}
|
||||
}
|
||||
|
||||
@ -271,18 +309,7 @@ public class ResourceLoader {
|
||||
}
|
||||
|
||||
for (Entry<String, OpenConfigData[]> e : config.entrySet()) {
|
||||
List<String> abilityList = new ArrayList<>();
|
||||
int extraTalentIndex = 0;
|
||||
|
||||
for (OpenConfigData entry : e.getValue()) {
|
||||
if (entry.$type.contains("AddAbility")) {
|
||||
abilityList.add(entry.abilityName);
|
||||
} else if (entry.talentIndex > 0) {
|
||||
extraTalentIndex = entry.talentIndex;
|
||||
}
|
||||
}
|
||||
|
||||
OpenConfigEntry entry = new OpenConfigEntry(e.getKey(), abilityList, extraTalentIndex);
|
||||
OpenConfigEntry entry = new OpenConfigEntry(e.getKey(), e.getValue());
|
||||
map.put(entry.getName(), entry);
|
||||
}
|
||||
}
|
||||
@ -297,10 +324,10 @@ public class ResourceLoader {
|
||||
}
|
||||
|
||||
for (OpenConfigEntry entry : list) {
|
||||
GenshinData.getOpenConfigEntries().put(entry.getName(), entry);
|
||||
GameData.getOpenConfigEntries().put(entry.getName(), entry);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// BinOutput configs
|
||||
|
||||
private static class AvatarConfig {
|
||||
@ -318,9 +345,11 @@ public class ResourceLoader {
|
||||
public OpenConfigData[] data;
|
||||
}
|
||||
|
||||
private static class OpenConfigData {
|
||||
public static class OpenConfigData {
|
||||
public String $type;
|
||||
public String abilityName;
|
||||
public int talentIndex;
|
||||
public int skillID;
|
||||
public int pointDelta;
|
||||
}
|
||||
}
|
||||
|
@ -3,6 +3,12 @@ package emu.grasscutter.data.common;
|
||||
public class ItemParamData {
|
||||
private int Id;
|
||||
private int Count;
|
||||
|
||||
public ItemParamData() {}
|
||||
public ItemParamData(int id, int count) {
|
||||
this.Id = id;
|
||||
this.Count = count;
|
||||
}
|
||||
|
||||
public int getId() {
|
||||
return Id;
|
||||
|
@ -0,0 +1,26 @@
|
||||
package emu.grasscutter.data.common;
|
||||
|
||||
public class ItemParamStringData {
|
||||
private int Id;
|
||||
private String Count;
|
||||
|
||||
public ItemParamStringData() {}
|
||||
|
||||
public int getId() {
|
||||
return Id;
|
||||
}
|
||||
|
||||
public String getCount() {
|
||||
return Count;
|
||||
}
|
||||
|
||||
public ItemParamData toItemParamData() {
|
||||
if (Count.contains(";")) {
|
||||
String[] split = Count.split(";");
|
||||
Count = Count.split(";")[split.length - 1];
|
||||
} else if (Count.contains(".")) {
|
||||
return new ItemParamData(Id, (int) Math.ceil(Double.parseDouble(Count)));
|
||||
}
|
||||
return new ItemParamData(Id, Integer.parseInt(Count));
|
||||
}
|
||||
}
|
24
src/main/java/emu/grasscutter/data/common/OpenCondData.java
Normal file
24
src/main/java/emu/grasscutter/data/common/OpenCondData.java
Normal file
@ -0,0 +1,24 @@
|
||||
package emu.grasscutter.data.common;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public class OpenCondData {
|
||||
private String CondType;
|
||||
private List<Integer> ParamList;
|
||||
|
||||
public String getCondType() {
|
||||
return CondType;
|
||||
}
|
||||
|
||||
public void setCondType(String condType) {
|
||||
CondType = condType;
|
||||
}
|
||||
|
||||
public List<Integer> getParamList() {
|
||||
return ParamList;
|
||||
}
|
||||
|
||||
public void setParamList(List<Integer> paramList) {
|
||||
ParamList = paramList;
|
||||
}
|
||||
}
|
@ -1,43 +1,72 @@
|
||||
package emu.grasscutter.data.common;
|
||||
|
||||
public class PointData {
|
||||
private pos tranPos;
|
||||
import emu.grasscutter.Grasscutter;
|
||||
import emu.grasscutter.data.GameData;
|
||||
import emu.grasscutter.data.def.DailyDungeonData;
|
||||
import emu.grasscutter.utils.Position;
|
||||
import it.unimi.dsi.fastutil.ints.IntArrayList;
|
||||
import it.unimi.dsi.fastutil.ints.IntList;
|
||||
|
||||
public pos getTranPos() {
|
||||
public class PointData {
|
||||
private int id;
|
||||
private String $type;
|
||||
private Position tranPos;
|
||||
private int[] dungeonIds;
|
||||
private int[] dungeonRandomList;
|
||||
|
||||
private int tranSceneId;
|
||||
public int getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(int id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public String getType() {
|
||||
return $type;
|
||||
}
|
||||
|
||||
public Position getTranPos() {
|
||||
return tranPos;
|
||||
}
|
||||
|
||||
public void setTranPos(pos tranPos) {
|
||||
this.tranPos = tranPos;
|
||||
}
|
||||
public int[] getDungeonIds() {
|
||||
return dungeonIds;
|
||||
}
|
||||
|
||||
public class pos {
|
||||
private float x;
|
||||
private float y;
|
||||
private float z;
|
||||
public int[] getDungeonRandomList() {
|
||||
return dungeonRandomList;
|
||||
}
|
||||
|
||||
public float getX() {
|
||||
return x;
|
||||
}
|
||||
public int getTranSceneId() {
|
||||
return tranSceneId;
|
||||
}
|
||||
|
||||
public void setX(float x) {
|
||||
this.x = x;
|
||||
}
|
||||
public void setTranSceneId(int tranSceneId) {
|
||||
this.tranSceneId = tranSceneId;
|
||||
}
|
||||
|
||||
public float getY() {
|
||||
return y;
|
||||
}
|
||||
|
||||
public void setY(float y) {
|
||||
this.y = y;
|
||||
}
|
||||
|
||||
public float getZ() {
|
||||
return z;
|
||||
}
|
||||
|
||||
public void setZ(float z) {
|
||||
this.z = z;
|
||||
}
|
||||
}
|
||||
public void updateDailyDungeon() {
|
||||
if (getDungeonRandomList() == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
IntList newDungeons = new IntArrayList();
|
||||
int day = Grasscutter.getCurrentDayOfWeek();
|
||||
|
||||
for (int randomId : getDungeonRandomList()) {
|
||||
DailyDungeonData data = GameData.getDailyDungeonDataMap().get(randomId);
|
||||
|
||||
if (data != null) {
|
||||
int[] addDungeons = data.getDungeonsByDay(day);
|
||||
|
||||
for (int d : addDungeons) {
|
||||
newDungeons.add(d);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
this.dungeonIds = newDungeons.toIntArray();
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
@ -1,18 +1,39 @@
|
||||
package emu.grasscutter.data.custom;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import emu.grasscutter.data.ResourceLoader.OpenConfigData;
|
||||
|
||||
public class OpenConfigEntry {
|
||||
private String name;
|
||||
private String[] addAbilities;
|
||||
private int extraTalentIndex;
|
||||
|
||||
public OpenConfigEntry(String name, List<String> abilityList, int extraTalentIndex) {
|
||||
private SkillPointModifier[] skillPointModifiers;
|
||||
|
||||
public OpenConfigEntry(String name, OpenConfigData[] data) {
|
||||
this.name = name;
|
||||
this.extraTalentIndex = extraTalentIndex;
|
||||
|
||||
List<String> abilityList = new ArrayList<>();
|
||||
List<SkillPointModifier> modList = new ArrayList<>();
|
||||
|
||||
for (OpenConfigData entry : data) {
|
||||
if (entry.$type.contains("AddAbility")) {
|
||||
abilityList.add(entry.abilityName);
|
||||
} else if (entry.talentIndex > 0) {
|
||||
this.extraTalentIndex = entry.talentIndex;
|
||||
} else if (entry.$type.contains("ModifySkillPoint")) {
|
||||
modList.add(new SkillPointModifier(entry.skillID, entry.pointDelta));
|
||||
}
|
||||
}
|
||||
|
||||
if (abilityList.size() > 0) {
|
||||
this.addAbilities = abilityList.toArray(new String[0]);
|
||||
}
|
||||
|
||||
if (modList.size() > 0) {
|
||||
this.skillPointModifiers = modList.toArray(new SkillPointModifier[0]);
|
||||
}
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
@ -26,4 +47,26 @@ public class OpenConfigEntry {
|
||||
public int getExtraTalentIndex() {
|
||||
return extraTalentIndex;
|
||||
}
|
||||
|
||||
public SkillPointModifier[] getSkillPointModifiers() {
|
||||
return skillPointModifiers;
|
||||
}
|
||||
|
||||
public static class SkillPointModifier {
|
||||
private int skillId;
|
||||
private int delta;
|
||||
|
||||
public SkillPointModifier(int skillId, int delta) {
|
||||
this.skillId = skillId;
|
||||
this.delta = delta;
|
||||
}
|
||||
|
||||
public int getSkillId() {
|
||||
return skillId;
|
||||
}
|
||||
|
||||
public int getDelta() {
|
||||
return delta;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,11 +1,11 @@
|
||||
package emu.grasscutter.data.def;
|
||||
|
||||
import emu.grasscutter.data.GenshinData;
|
||||
import emu.grasscutter.data.GenshinResource;
|
||||
import emu.grasscutter.data.GameData;
|
||||
import emu.grasscutter.data.GameResource;
|
||||
import emu.grasscutter.data.ResourceType;
|
||||
|
||||
@ResourceType(name = "AvatarCostumeExcelConfigData.json")
|
||||
public class AvatarCostumeData extends GenshinResource {
|
||||
public class AvatarCostumeData extends GameResource {
|
||||
private int CostumeId;
|
||||
private int ItemId;
|
||||
private int AvatarId;
|
||||
@ -25,6 +25,6 @@ public class AvatarCostumeData extends GenshinResource {
|
||||
|
||||
@Override
|
||||
public void onLoad() {
|
||||
GenshinData.getAvatarCostumeDataItemIdMap().put(this.getItemId(), this);
|
||||
GameData.getAvatarCostumeDataItemIdMap().put(this.getItemId(), this);
|
||||
}
|
||||
}
|
||||
|
@ -4,12 +4,12 @@ import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import emu.grasscutter.data.GenshinResource;
|
||||
import emu.grasscutter.data.GameResource;
|
||||
import emu.grasscutter.data.ResourceType;
|
||||
import emu.grasscutter.data.common.CurveInfo;
|
||||
|
||||
@ResourceType(name = "AvatarCurveExcelConfigData.json")
|
||||
public class AvatarCurveData extends GenshinResource {
|
||||
public class AvatarCurveData extends GameResource {
|
||||
private int Level;
|
||||
private CurveInfo[] CurveInfos;
|
||||
|
||||
|
@ -2,8 +2,8 @@ package emu.grasscutter.data.def;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import emu.grasscutter.data.GenshinData;
|
||||
import emu.grasscutter.data.GenshinResource;
|
||||
import emu.grasscutter.data.GameData;
|
||||
import emu.grasscutter.data.GameResource;
|
||||
import emu.grasscutter.data.ResourceType;
|
||||
import emu.grasscutter.data.ResourceType.LoadPriority;
|
||||
import emu.grasscutter.data.common.PropGrowCurve;
|
||||
@ -15,7 +15,7 @@ import it.unimi.dsi.fastutil.ints.IntArrayList;
|
||||
import it.unimi.dsi.fastutil.ints.IntList;
|
||||
|
||||
@ResourceType(name = "AvatarExcelConfigData.json", loadPriority = LoadPriority.LOW)
|
||||
public class AvatarData extends GenshinResource {
|
||||
public class AvatarData extends GameResource {
|
||||
|
||||
private String name;
|
||||
private String IconName;
|
||||
@ -57,6 +57,8 @@ public class AvatarData extends GenshinResource {
|
||||
private IntList abilities;
|
||||
|
||||
private List<Integer> fetters;
|
||||
private int nameCardRewardId;
|
||||
private int nameCardId;
|
||||
|
||||
@Override
|
||||
public int getId(){
|
||||
@ -176,7 +178,7 @@ public class AvatarData extends GenshinResource {
|
||||
if (growCurve == null) {
|
||||
return 1f;
|
||||
}
|
||||
AvatarCurveData curveData = GenshinData.getAvatarCurveDataMap().get(level);
|
||||
AvatarCurveData curveData = GameData.getAvatarCurveDataMap().get(level);
|
||||
if (curveData == null) {
|
||||
return 1f;
|
||||
}
|
||||
@ -199,18 +201,34 @@ 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);
|
||||
this.skillDepot = GameData.getAvatarSkillDepotDataMap().get(this.SkillDepotId);
|
||||
|
||||
// Get fetters from GenshinData
|
||||
this.fetters = GenshinData.getFetterDataEntries().get(this.Id);
|
||||
// Get fetters from GameData
|
||||
this.fetters = GameData.getFetterDataEntries().get(this.Id);
|
||||
|
||||
if (GameData.getFetterCharacterCardDataMap().get(this.Id) != null) {
|
||||
this.nameCardRewardId = GameData.getFetterCharacterCardDataMap().get(this.Id).getRewardId();
|
||||
}
|
||||
|
||||
if (GameData.getRewardDataMap().get(this.nameCardRewardId) != null) {
|
||||
this.nameCardId = GameData.getRewardDataMap().get(this.nameCardRewardId).getRewardItemList().get(0).getItemId();
|
||||
}
|
||||
|
||||
int size = GenshinData.getAvatarCurveDataMap().size();
|
||||
int size = GameData.getAvatarCurveDataMap().size();
|
||||
this.hpGrowthCurve = new float[size];
|
||||
this.attackGrowthCurve = new float[size];
|
||||
this.defenseGrowthCurve = new float[size];
|
||||
for (AvatarCurveData curveData : GenshinData.getAvatarCurveDataMap().values()) {
|
||||
for (AvatarCurveData curveData : GameData.getAvatarCurveDataMap().values()) {
|
||||
int level = curveData.getLevel() - 1;
|
||||
for (PropGrowCurve growCurve : this.PropGrowCurves) {
|
||||
FightProperty prop = FightProperty.getPropByName(growCurve.getType());
|
||||
@ -242,7 +260,7 @@ public class AvatarData extends GenshinResource {
|
||||
if (split.length > 0) {
|
||||
this.name = split[split.length - 1];
|
||||
|
||||
AbilityEmbryoEntry info = GenshinData.getAbilityEmbryoInfo().get(this.name);
|
||||
AbilityEmbryoEntry info = GameData.getAbilityEmbryoInfo().get(this.name);
|
||||
if (info != null) {
|
||||
this.abilities = new IntArrayList(info.getAbilities().length);
|
||||
for (String ability : info.getAbilities()) {
|
||||
|
@ -0,0 +1,23 @@
|
||||
package emu.grasscutter.data.def;
|
||||
|
||||
import emu.grasscutter.data.GameResource;
|
||||
import emu.grasscutter.data.ResourceType;
|
||||
|
||||
@ResourceType(name = "AvatarFettersLevelExcelConfigData.json")
|
||||
public class AvatarFetterLevelData extends GameResource {
|
||||
private int FetterLevel;
|
||||
private int NeedExp;
|
||||
|
||||
@Override
|
||||
public int getId() {
|
||||
return this.FetterLevel;
|
||||
}
|
||||
|
||||
public int getLevel() {
|
||||
return FetterLevel;
|
||||
}
|
||||
|
||||
public int getExp() {
|
||||
return NeedExp;
|
||||
}
|
||||
}
|
@ -1,10 +1,10 @@
|
||||
package emu.grasscutter.data.def;
|
||||
|
||||
import emu.grasscutter.data.GenshinResource;
|
||||
import emu.grasscutter.data.GameResource;
|
||||
import emu.grasscutter.data.ResourceType;
|
||||
|
||||
@ResourceType(name = "AvatarFlycloakExcelConfigData.json")
|
||||
public class AvatarFlycloakData extends GenshinResource {
|
||||
public class AvatarFlycloakData extends GameResource {
|
||||
private int FlycloakId;
|
||||
private long NameTextMapHash;
|
||||
|
||||
|
@ -1,10 +1,10 @@
|
||||
package emu.grasscutter.data.def;
|
||||
|
||||
import emu.grasscutter.data.GenshinResource;
|
||||
import emu.grasscutter.data.GameResource;
|
||||
import emu.grasscutter.data.ResourceType;
|
||||
|
||||
@ResourceType(name = "AvatarLevelExcelConfigData.json")
|
||||
public class AvatarLevelData extends GenshinResource {
|
||||
public class AvatarLevelData extends GameResource {
|
||||
private int Level;
|
||||
private int Exp;
|
||||
|
||||
|
@ -1,13 +1,13 @@
|
||||
package emu.grasscutter.data.def;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import emu.grasscutter.data.GenshinResource;
|
||||
import emu.grasscutter.data.GameResource;
|
||||
import emu.grasscutter.data.ResourceType;
|
||||
import emu.grasscutter.data.common.FightPropData;
|
||||
import emu.grasscutter.data.common.ItemParamData;
|
||||
|
||||
@ResourceType(name = "AvatarPromoteExcelConfigData.json")
|
||||
public class AvatarPromoteData extends GenshinResource {
|
||||
public class AvatarPromoteData extends GameResource {
|
||||
|
||||
private int AvatarPromoteId;
|
||||
private int PromoteLevel;
|
||||
|
@ -2,12 +2,12 @@ package emu.grasscutter.data.def;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import emu.grasscutter.data.GenshinResource;
|
||||
import emu.grasscutter.data.GameResource;
|
||||
import emu.grasscutter.data.ResourceType;
|
||||
import emu.grasscutter.data.ResourceType.LoadPriority;
|
||||
|
||||
@ResourceType(name = "AvatarSkillExcelConfigData.json", loadPriority = LoadPriority.HIGHEST)
|
||||
public class AvatarSkillData extends GenshinResource {
|
||||
public class AvatarSkillData extends GameResource {
|
||||
private int Id;
|
||||
private float CdTime;
|
||||
private int CostElemVal;
|
||||
|
@ -2,8 +2,8 @@ package emu.grasscutter.data.def;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import emu.grasscutter.data.GenshinData;
|
||||
import emu.grasscutter.data.GenshinResource;
|
||||
import emu.grasscutter.data.GameData;
|
||||
import emu.grasscutter.data.GameResource;
|
||||
import emu.grasscutter.data.ResourceType;
|
||||
import emu.grasscutter.data.ResourceType.LoadPriority;
|
||||
import emu.grasscutter.data.custom.AbilityEmbryoEntry;
|
||||
@ -13,7 +13,7 @@ import it.unimi.dsi.fastutil.ints.IntArrayList;
|
||||
import it.unimi.dsi.fastutil.ints.IntList;
|
||||
|
||||
@ResourceType(name = "AvatarSkillDepotExcelConfigData.json", loadPriority = LoadPriority.HIGH)
|
||||
public class AvatarSkillDepotData extends GenshinResource {
|
||||
public class AvatarSkillDepotData extends GameResource {
|
||||
|
||||
private int Id;
|
||||
private int EnergySkill;
|
||||
@ -94,7 +94,7 @@ public class AvatarSkillDepotData extends GenshinResource {
|
||||
|
||||
@Override
|
||||
public void onLoad() {
|
||||
this.energySkillData = GenshinData.getAvatarSkillDataMap().get(this.EnergySkill);
|
||||
this.energySkillData = GameData.getAvatarSkillDataMap().get(this.EnergySkill);
|
||||
if (getEnergySkillData() != null) {
|
||||
this.elementType = ElementType.getTypeByName(getEnergySkillData().getCostElemType());
|
||||
} else {
|
||||
|
@ -1,13 +1,13 @@
|
||||
package emu.grasscutter.data.def;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import emu.grasscutter.data.GenshinResource;
|
||||
import emu.grasscutter.data.GameResource;
|
||||
import emu.grasscutter.data.ResourceType;
|
||||
import emu.grasscutter.data.ResourceType.LoadPriority;
|
||||
import emu.grasscutter.data.common.FightPropData;
|
||||
|
||||
@ResourceType(name = "AvatarTalentExcelConfigData.json", loadPriority = LoadPriority.HIGHEST)
|
||||
public class AvatarTalentData extends GenshinResource {
|
||||
public class AvatarTalentData extends GameResource {
|
||||
private int TalentId;
|
||||
private int PrevTalent;
|
||||
private long NameTextMapHash;
|
||||
|
173
src/main/java/emu/grasscutter/data/def/CombineData.java
Normal file
173
src/main/java/emu/grasscutter/data/def/CombineData.java
Normal file
@ -0,0 +1,173 @@
|
||||
package emu.grasscutter.data.def;
|
||||
|
||||
import emu.grasscutter.data.GameResource;
|
||||
import emu.grasscutter.data.ResourceType;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
@ResourceType(name = "CombineExcelConfigData.json")
|
||||
public class CombineData extends GameResource {
|
||||
|
||||
private int CombineId;
|
||||
|
||||
private int PlayerLevel;
|
||||
|
||||
private boolean IsDefaultShow;
|
||||
|
||||
private int CombineType;
|
||||
|
||||
private int SubCombineType;
|
||||
|
||||
private int ResultItemId;
|
||||
|
||||
private int ResultItemCount;
|
||||
|
||||
private int ScoinCost;
|
||||
|
||||
private List<CombineItemPair> RandomItems;
|
||||
|
||||
private List<CombineItemPair> MaterialItems;
|
||||
|
||||
private long EffectDescTextMapHash;
|
||||
|
||||
private String RecipeType;
|
||||
|
||||
@Override
|
||||
public int getId() {
|
||||
return this.CombineId;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onLoad() {
|
||||
super.onLoad();
|
||||
// clean data
|
||||
RandomItems = RandomItems.stream().filter(item -> item.Id > 0).collect(Collectors.toList());
|
||||
MaterialItems = MaterialItems.stream().filter(item -> item.Id > 0).collect(Collectors.toList());
|
||||
}
|
||||
|
||||
public static class CombineItemPair {
|
||||
|
||||
private int Id;
|
||||
|
||||
private int Count;
|
||||
|
||||
public CombineItemPair(int id, int count) {
|
||||
Id = id;
|
||||
Count = count;
|
||||
}
|
||||
|
||||
public int getId() {
|
||||
return Id;
|
||||
}
|
||||
|
||||
public void setId(int id) {
|
||||
Id = id;
|
||||
}
|
||||
|
||||
public int getCount() {
|
||||
return Count;
|
||||
}
|
||||
|
||||
public void setCount(int count) {
|
||||
Count = count;
|
||||
}
|
||||
}
|
||||
|
||||
public int getCombineId() {
|
||||
return CombineId;
|
||||
}
|
||||
|
||||
public void setCombineId(int combineId) {
|
||||
CombineId = combineId;
|
||||
}
|
||||
|
||||
public int getPlayerLevel() {
|
||||
return PlayerLevel;
|
||||
}
|
||||
|
||||
public void setPlayerLevel(int playerLevel) {
|
||||
PlayerLevel = playerLevel;
|
||||
}
|
||||
|
||||
public boolean isDefaultShow() {
|
||||
return IsDefaultShow;
|
||||
}
|
||||
|
||||
public void setDefaultShow(boolean defaultShow) {
|
||||
IsDefaultShow = defaultShow;
|
||||
}
|
||||
|
||||
public int getCombineType() {
|
||||
return CombineType;
|
||||
}
|
||||
|
||||
public void setCombineType(int combineType) {
|
||||
CombineType = combineType;
|
||||
}
|
||||
|
||||
public int getSubCombineType() {
|
||||
return SubCombineType;
|
||||
}
|
||||
|
||||
public void setSubCombineType(int subCombineType) {
|
||||
SubCombineType = subCombineType;
|
||||
}
|
||||
|
||||
public int getResultItemId() {
|
||||
return ResultItemId;
|
||||
}
|
||||
|
||||
public void setResultItemId(int resultItemId) {
|
||||
ResultItemId = resultItemId;
|
||||
}
|
||||
|
||||
public int getResultItemCount() {
|
||||
return ResultItemCount;
|
||||
}
|
||||
|
||||
public void setResultItemCount(int resultItemCount) {
|
||||
ResultItemCount = resultItemCount;
|
||||
}
|
||||
|
||||
public int getScoinCost() {
|
||||
return ScoinCost;
|
||||
}
|
||||
|
||||
public void setScoinCost(int scoinCost) {
|
||||
ScoinCost = scoinCost;
|
||||
}
|
||||
|
||||
public List<CombineItemPair> getRandomItems() {
|
||||
return RandomItems;
|
||||
}
|
||||
|
||||
public void setRandomItems(List<CombineItemPair> randomItems) {
|
||||
RandomItems = randomItems;
|
||||
}
|
||||
|
||||
public List<CombineItemPair> getMaterialItems() {
|
||||
return MaterialItems;
|
||||
}
|
||||
|
||||
public void setMaterialItems(List<CombineItemPair> materialItems) {
|
||||
MaterialItems = materialItems;
|
||||
}
|
||||
|
||||
public long getEffectDescTextMapHash() {
|
||||
return EffectDescTextMapHash;
|
||||
}
|
||||
|
||||
public void setEffectDescTextMapHash(long effectDescTextMapHash) {
|
||||
EffectDescTextMapHash = effectDescTextMapHash;
|
||||
}
|
||||
|
||||
public String getRecipeType() {
|
||||
return RecipeType;
|
||||
}
|
||||
|
||||
public void setRecipeType(String recipeType) {
|
||||
RecipeType = recipeType;
|
||||
}
|
||||
}
|
||||
|
50
src/main/java/emu/grasscutter/data/def/DailyDungeonData.java
Normal file
50
src/main/java/emu/grasscutter/data/def/DailyDungeonData.java
Normal file
@ -0,0 +1,50 @@
|
||||
package emu.grasscutter.data.def;
|
||||
|
||||
import java.util.Calendar;
|
||||
|
||||
import emu.grasscutter.data.GameData;
|
||||
import emu.grasscutter.data.GameResource;
|
||||
import emu.grasscutter.data.ResourceType;
|
||||
|
||||
import emu.grasscutter.game.props.SceneType;
|
||||
import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
|
||||
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
|
||||
|
||||
@ResourceType(name = "DailyDungeonConfigData.json")
|
||||
public class DailyDungeonData extends GameResource {
|
||||
private int Id;
|
||||
private int[] Monday;
|
||||
private int[] Tuesday;
|
||||
private int[] Wednesday;
|
||||
private int[] Thursday;
|
||||
private int[] Friday;
|
||||
private int[] Saturday;
|
||||
private int[] Sunday;
|
||||
|
||||
private static final int[] empty = new int[0];
|
||||
private final Int2ObjectMap<int[]> map;
|
||||
|
||||
public DailyDungeonData() {
|
||||
this.map = new Int2ObjectOpenHashMap<>();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getId() {
|
||||
return this.Id;
|
||||
}
|
||||
|
||||
public int[] getDungeonsByDay(int day) {
|
||||
return map.getOrDefault(day, empty);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onLoad() {
|
||||
map.put(Calendar.MONDAY, Monday);
|
||||
map.put(Calendar.TUESDAY, Tuesday);
|
||||
map.put(Calendar.WEDNESDAY, Wednesday);
|
||||
map.put(Calendar.THURSDAY, Thursday);
|
||||
map.put(Calendar.FRIDAY, Friday);
|
||||
map.put(Calendar.SATURDAY, Saturday);
|
||||
map.put(Calendar.SUNDAY, Sunday);
|
||||
}
|
||||
}
|
42
src/main/java/emu/grasscutter/data/def/DungeonData.java
Normal file
42
src/main/java/emu/grasscutter/data/def/DungeonData.java
Normal file
@ -0,0 +1,42 @@
|
||||
package emu.grasscutter.data.def;
|
||||
|
||||
import emu.grasscutter.data.GameData;
|
||||
import emu.grasscutter.data.GameResource;
|
||||
import emu.grasscutter.data.ResourceType;
|
||||
|
||||
import emu.grasscutter.game.props.SceneType;
|
||||
|
||||
@ResourceType(name = "DungeonExcelConfigData.json")
|
||||
public class DungeonData extends GameResource {
|
||||
private int Id;
|
||||
private int SceneId;
|
||||
private int ShowLevel;
|
||||
private int PassRewardPreviewID;
|
||||
private String InvolveType; // TODO enum
|
||||
|
||||
private RewardPreviewData previewData;
|
||||
|
||||
@Override
|
||||
public int getId() {
|
||||
return this.Id;
|
||||
}
|
||||
|
||||
public int getSceneId() {
|
||||
return SceneId;
|
||||
}
|
||||
|
||||
public int getShowLevel() {
|
||||
return ShowLevel;
|
||||
}
|
||||
|
||||
public RewardPreviewData getRewardPreview() {
|
||||
return previewData;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onLoad() {
|
||||
if (this.PassRewardPreviewID > 0) {
|
||||
this.previewData = GameData.getRewardPreviewDataMap().get(this.PassRewardPreviewID);
|
||||
}
|
||||
}
|
||||
}
|
@ -1,12 +1,12 @@
|
||||
package emu.grasscutter.data.def;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import emu.grasscutter.data.GenshinResource;
|
||||
import emu.grasscutter.data.GameResource;
|
||||
import emu.grasscutter.data.ResourceType;
|
||||
import emu.grasscutter.data.common.FightPropData;
|
||||
|
||||
@ResourceType(name = "EquipAffixExcelConfigData.json")
|
||||
public class EquipAffixData extends GenshinResource {
|
||||
public class EquipAffixData extends GameResource {
|
||||
|
||||
private int AffixId;
|
||||
private int Id;
|
||||
|
@ -0,0 +1,24 @@
|
||||
package emu.grasscutter.data.def;
|
||||
|
||||
import emu.grasscutter.data.GameResource;
|
||||
import emu.grasscutter.data.ResourceType;
|
||||
import emu.grasscutter.data.ResourceType.LoadPriority;
|
||||
|
||||
@ResourceType(name = "FetterCharacterCardExcelConfigData.json", loadPriority = LoadPriority.HIGHEST)
|
||||
public class FetterCharacterCardData extends GameResource {
|
||||
private int AvatarId;
|
||||
private int RewardId;
|
||||
|
||||
@Override
|
||||
public int getId() {
|
||||
return AvatarId;
|
||||
}
|
||||
|
||||
public int getRewardId() {
|
||||
return RewardId;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onLoad() {
|
||||
}
|
||||
}
|
@ -1,13 +1,17 @@
|
||||
package emu.grasscutter.data.def;
|
||||
|
||||
import emu.grasscutter.data.GenshinResource;
|
||||
import java.util.List;
|
||||
|
||||
import emu.grasscutter.data.GameResource;
|
||||
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 {
|
||||
@ResourceType(name = {"FetterInfoExcelConfigData.json", "FettersExcelConfigData.json", "FetterStoryExcelConfigData.json", "PhotographExpressionExcelConfigData.json", "PhotographPosenameExcelConfigData.json"}, loadPriority = LoadPriority.HIGHEST)
|
||||
public class FetterData extends GameResource {
|
||||
private int AvatarId;
|
||||
private int FetterId;
|
||||
private List<OpenCondData> OpenCond;
|
||||
|
||||
@Override
|
||||
public int getId() {
|
||||
@ -18,6 +22,10 @@ public class FetterData extends GenshinResource {
|
||||
return AvatarId;
|
||||
}
|
||||
|
||||
public List<OpenCondData> getOpenConds() {
|
||||
return OpenCond;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onLoad() {
|
||||
}
|
||||
|
@ -1,27 +1,29 @@
|
||||
package emu.grasscutter.data.def;
|
||||
|
||||
import emu.grasscutter.data.GenshinResource;
|
||||
import emu.grasscutter.data.GameResource;
|
||||
import emu.grasscutter.data.ResourceType;
|
||||
import emu.grasscutter.game.props.EntityType;
|
||||
|
||||
@ResourceType(name = "GadgetExcelConfigData.json")
|
||||
public class GadgetData extends GenshinResource {
|
||||
public class GadgetData extends GameResource {
|
||||
private int Id;
|
||||
|
||||
private String Type;
|
||||
private String JsonName;
|
||||
private boolean IsInteractive;
|
||||
private String[] Tags;
|
||||
private String ItemJsonName;
|
||||
private String InteeIconName;
|
||||
private long NameTextMapHash;
|
||||
private int CampID;
|
||||
|
||||
|
||||
private EntityType Type;
|
||||
private String JsonName;
|
||||
private boolean IsInteractive;
|
||||
private String[] Tags;
|
||||
private String ItemJsonName;
|
||||
private String InteeIconName;
|
||||
private long NameTextMapHash;
|
||||
private int CampID;
|
||||
private String LODPatternName;
|
||||
|
||||
@Override
|
||||
public int getId() {
|
||||
return this.Id;
|
||||
}
|
||||
|
||||
public String getType() {
|
||||
public EntityType getType() {
|
||||
return Type;
|
||||
}
|
||||
|
||||
@ -53,6 +55,8 @@ public class GadgetData extends GenshinResource {
|
||||
return CampID;
|
||||
}
|
||||
|
||||
public String getLODPatternName() { return LODPatternName; }
|
||||
|
||||
@Override
|
||||
public void onLoad() {
|
||||
|
||||
|
@ -1,13 +1,13 @@
|
||||
package emu.grasscutter.data.def;
|
||||
|
||||
import emu.grasscutter.data.GenshinResource;
|
||||
import emu.grasscutter.data.GameResource;
|
||||
import emu.grasscutter.data.ResourceType;
|
||||
import emu.grasscutter.game.props.FightProperty;
|
||||
import it.unimi.dsi.fastutil.ints.IntOpenHashSet;
|
||||
import it.unimi.dsi.fastutil.ints.IntSet;
|
||||
|
||||
@ResourceType(name = {"MaterialExcelConfigData.json", "WeaponExcelConfigData.json", "ReliquaryExcelConfigData.json"})
|
||||
public class ItemData extends GenshinResource {
|
||||
public class ItemData extends GameResource {
|
||||
|
||||
private int Id;
|
||||
private int StackLimit = 1;
|
||||
|
@ -4,12 +4,12 @@ import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import emu.grasscutter.data.GenshinResource;
|
||||
import emu.grasscutter.data.GameResource;
|
||||
import emu.grasscutter.data.ResourceType;
|
||||
import emu.grasscutter.data.common.CurveInfo;
|
||||
|
||||
@ResourceType(name = "MonsterCurveExcelConfigData.json")
|
||||
public class MonsterCurveData extends GenshinResource {
|
||||
public class MonsterCurveData extends GameResource {
|
||||
private int Level;
|
||||
private CurveInfo[] CurveInfos;
|
||||
|
||||
|
@ -2,14 +2,14 @@ package emu.grasscutter.data.def;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import emu.grasscutter.data.GenshinData;
|
||||
import emu.grasscutter.data.GenshinResource;
|
||||
import emu.grasscutter.data.GameData;
|
||||
import emu.grasscutter.data.GameResource;
|
||||
import emu.grasscutter.data.ResourceType;
|
||||
import emu.grasscutter.data.ResourceType.LoadPriority;
|
||||
import emu.grasscutter.data.common.PropGrowCurve;
|
||||
|
||||
@ResourceType(name = "MonsterExcelConfigData.json", loadPriority = LoadPriority.LOW)
|
||||
public class MonsterData extends GenshinResource {
|
||||
public class MonsterData extends GameResource {
|
||||
private int Id;
|
||||
|
||||
private String MonsterName;
|
||||
@ -168,13 +168,13 @@ public class MonsterData extends GenshinResource {
|
||||
|
||||
@Override
|
||||
public void onLoad() {
|
||||
this.describeData = GenshinData.getMonsterDescribeDataMap().get(this.getDescribeId());
|
||||
this.describeData = GameData.getMonsterDescribeDataMap().get(this.getDescribeId());
|
||||
|
||||
for (int id : this.Equips) {
|
||||
if (id == 0) {
|
||||
continue;
|
||||
}
|
||||
GadgetData gadget = GenshinData.getGadgetDataMap().get(id);
|
||||
GadgetData gadget = GameData.getGadgetDataMap().get(id);
|
||||
if (gadget == null) {
|
||||
continue;
|
||||
}
|
||||
|
@ -1,11 +1,11 @@
|
||||
package emu.grasscutter.data.def;
|
||||
|
||||
import emu.grasscutter.data.GenshinResource;
|
||||
import emu.grasscutter.data.GameResource;
|
||||
import emu.grasscutter.data.ResourceType;
|
||||
import emu.grasscutter.data.ResourceType.LoadPriority;
|
||||
|
||||
@ResourceType(name = "MonsterDescribeExcelConfigData.json", loadPriority = LoadPriority.HIGH)
|
||||
public class MonsterDescribeData extends GenshinResource {
|
||||
public class MonsterDescribeData extends GameResource {
|
||||
private int Id;
|
||||
private long NameTextMapHash;
|
||||
private int TitleID;
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user