27 Commits

Author SHA1 Message Date
5c4c62c1ed Version 1.4.3
Update languages [skip actions]
Fix whitespace [skip actions]
Refactor Entity data
Clean up excels
Make PlayerBuffManager::removeBuff actually remove buff
Remove LinkedList usage https://twitter.com/joshbloch/status/583813919019573248
Add FIGHT_PROP_CUR_DEFENSE alias to setstats (closes #1929)
Update gradle wrapper (closes #1076)
Add warnings to ancient deprecated Banners.json fields Will probably turn these into runtime errors in 1.5.0, people need to stop sharing configs with dead fields and especially stop making PRs with them.
3.2 First Half Banners (closes #1932)
Reimplement namecard claiming (fixes #1882)
Kill console on IOError
Refactor command kwarg parsing
Update roomSceneId obfuscated names (#1926)
Add the new keys (3.2 Support) (#1923)
Update README_ja-JP.md (#1922)
Fix Mail SendToAll Freeze (#1913)
Remove deprecated constructor call
Debug system (#1894)
[Security][Bugfix] Fix directory traversal exploit (#1907)
Updated spanish translations (#1911)
Don't override en-us gacha strings with vietnamese
Fix gacha history internal server error
Fix CONTIBUTING typo in protect_files.yml
Remove compilation warning on Language.java
Version 1.4.3-dev [skip actions]
2022-11-05 12:49:35 +00:00
e2b59fbd8d Update languages [skip actions] 2022-11-05 12:49:35 +00:00
84bf783243 Fix whitespace [skip actions] 2022-11-05 12:49:35 +00:00
4d8caf5a8c Refactor Entity data 2022-11-05 23:18:33 +10:30
a30f16b0e1 Clean up excels 2022-11-05 23:12:03 +10:30
65915b7666 Make PlayerBuffManager::removeBuff actually remove buff 2022-11-05 17:26:20 +10:30
1ceda2a455 Remove LinkedList usage
https://twitter.com/joshbloch/status/583813919019573248
2022-11-05 17:04:15 +10:30
09f392bb5b Add FIGHT_PROP_CUR_DEFENSE alias to setstats (closes #1929) 2022-11-05 15:36:52 +10:30
944bfb76f3 Update gradle wrapper (closes #1076) 2022-11-05 15:03:00 +10:30
dc7b8d8ca6 Add warnings to ancient deprecated Banners.json fields
Will probably turn these into runtime errors in 1.5.0, people need to stop sharing configs with dead fields and especially stop making PRs with them.
2022-11-05 14:22:17 +10:30
9f00ea3573 3.2 First Half Banners
(closes #1932)
2022-11-05 13:56:27 +10:30
12316b36b1 Reimplement namecard claiming (fixes #1882) 2022-11-04 15:05:51 +10:30
0fb7d50acd Kill console on IOError 2022-11-04 13:55:39 +10:30
c926fe326c Refactor command kwarg parsing 2022-11-04 13:55:39 +10:30
e8930e92e1 Update roomSceneId obfuscated names (#1926) 2022-11-02 18:03:41 +10:30
4ed0b90c27 Add the new keys (3.2 Support) (#1923)
* Add the new keys

* Some improvements to the key loading system & Bump the game version
2022-11-01 23:28:04 +01:00
7bfb35ac39 Update README_ja-JP.md (#1922)
* Translate README.md into Japanese

Update README_ja-JP.md

* Update README_ja-JP.md

Update command link for account creation to take the reader closer to the actual command description.

Co-authored-by: GanyusLeftHorn <1244229+GanyusLeftHorn@users.noreply.github.com>
2022-11-01 03:43:19 +01:00
47911aa16e Fix Mail SendToAll Freeze (#1913)
* Avoid iterating over every offline player

* getAllPlayers by stream

* Use the new method without changing the old signature

* Annotate deprecated `getAllPlayers`
2022-10-30 20:03:30 +10:30
855d3182ab Remove deprecated constructor call 2022-10-30 00:40:00 +10:30
43b7d7a383 Debug system (#1894)
* Add build (compile) script: gradlew jar

* Move server and services log levels to ConfigContainer, ability to enable/disable show packet payload and loop packets

* Add some loop packets to known list
2022-10-29 23:31:28 +10:30
55928d9154 [Security][Bugfix] Fix directory traversal exploit (#1907)
* [Security][Bugfix] Fix directory traversal exploit

1.The first slash will act as root path when resolving local path, so directory traversal is possible
2.Filter the illegal payload to prevent directory traversal
3.This also fix the bug about not loading the files in data folder when querying  `/hk4e/announcement/`

* Fix formatting

* Update src/main/java/emu/grasscutter/server/http/handlers/AnnouncementsHandler.java
2022-10-29 23:19:46 +10:30
6219902e0f Updated spanish translations (#1911) 2022-10-29 23:06:44 +10:30
d7af52f94b Don't override en-us gacha strings with vietnamese 2022-10-29 00:11:44 +10:30
c21d216d8d Fix gacha history internal server error 2022-10-29 00:05:29 +10:30
60657b3b98 Fix CONTIBUTING typo in protect_files.yml 2022-10-28 22:23:13 +10:30
f07ce48b24 Remove compilation warning on Language.java 2022-10-28 22:13:13 +10:30
2dd644f28b Version 1.4.3-dev [skip actions] 2022-10-26 12:31:30 +00:00
94 changed files with 1336 additions and 2045 deletions

View File

@ -20,4 +20,4 @@ jobs:
steps: steps:
- uses: superbrothers/close-pull-request@v3 - uses: superbrothers/close-pull-request@v3
with: with:
comment: "This PR has been closed for modifying protected files. See `CONTIBUTING.md` for more information." comment: "This PR has been closed for modifying protected files. See `CONTRIBUTING.md` for more information."

View File

@ -6,7 +6,7 @@
[EN](README.md) | [简中](README_zh-CN.md) | [繁中](README_zh-TW.md) | [FR](README_fr-FR.md) | [ES](README_es-ES.md) | [HE](README_HE.md) | [RU](README_ru-RU.md) | [PL](README_pl-PL.md) | [ID](README_id-ID.md) | [KR](README_ko-KR.md) | [FIL/PH](README_fil-PH.md) | [NL](README_NL.md) | [JP](README_ja-JP.md) [EN](README.md) | [简中](README_zh-CN.md) | [繁中](README_zh-TW.md) | [FR](README_fr-FR.md) | [ES](README_es-ES.md) | [HE](README_HE.md) | [RU](README_ru-RU.md) | [PL](README_pl-PL.md) | [ID](README_id-ID.md) | [KR](README_ko-KR.md) | [FIL/PH](README_fil-PH.md) | [NL](README_NL.md) | [JP](README_ja-JP.md)
**:** 私たちはプロジェクトへの貢献者をいつでも歓迎します。 貢献を追加する前に我々の [行動規範](https://github.com/Grasscutters/Grasscutter/blob/stable/CONTRIBUTING.md)をよくお読みください. **:** 私たちはプロジェクトへの貢献者をいつでも歓迎します。貢献を追加する前に我々の [行動規範](https://github.com/Grasscutters/Grasscutter/blob/stable/CONTRIBUTING.md)をよくお読みください
## 現在機能している物 ## 現在機能している物
@ -21,39 +21,37 @@
## クイックセットアップガイド ## クイックセットアップガイド
**:** サポートが必要な場合はGrasscutterの[Discord](https://discord.gg/T5vZU6UyeG)に参加してください. **:** サポートが必要な場合はGrasscutterの[Discord](https://discord.gg/T5vZU6UyeG)に参加してください
### 動作環境 ### 動作環境
* [JAVAのバージョン17以降](https://www.oracle.com/java/technologies/javase/jdk17-archive-downloads.html) * [JAVAのバージョン17以降](https://www.oracle.com/java/technologies/javase/jdk17-archive-downloads.html)
**:** サーバーを動作させるだけならjreのみで十分です。 開発をしたい場合JDKが必要になるかもしれません **:** サーバーを動作させるだけならjreのみで十分です。 開発をしたい場合JDKが必要になるかもしれません
* [MongoDB](https://www.mongodb.com/try/download/community) (バージョン4.0以降を推奨) * [MongoDB](https://www.mongodb.com/try/download/community) (バージョン4.0以降を推奨)
* プロキシツール: [mitmproxy](https://mitmproxy.org/) (mitmdump, 推奨)[Fiddler Classic](https://telerik-fiddler.s3.amazonaws.com/fiddler/FiddlerSetup.exe)その他。 * プロキシツール: [mitmproxy](https://mitmproxy.org/) (mitmdump, 推奨)[Fiddler Classic](https://telerik-fiddler.s3.amazonaws.com/fiddler/FiddlerSetup.exe)その他。
### 起動方法 ### 起動方法
**:** もしサーバーをアップデートしたい場合は`config.json`を削除してから再生成してください **:** もしサーバーをアップデートしたい場合は`config.json`を削除してから再生成してください
1. `grasscutter.jar`を入手する 1. `grasscutter.jar`を入手する
- [action](https://github.com/Grasscutters/Grasscutter/actions) からダウンロードするか [自分でビルド](https://github.com/Grasscutters/Grasscutter#building)してください。 - [releases](https://github.com/Grasscutters/Grasscutter/releases/latest) か [action](https://github.com/Grasscutters/Grasscutter/actions) からダウンロードするか[自分でビルド](https://github.com/Grasscutters/Grasscutter#building)してください。
2. `grasscutter.jar` があるディレクトリに `resources` フォルダーを作成しそこに `BinOutput, ExcelBinOutput, Readables, Scripts, Subtitle, TextMap` を移動してください *(`resources` フォルダの中身の入手方法については [wiki](https://github.com/Grasscutters/Grasscutter/wiki) を参照してください.)* 2. `grasscutter.jar` があるディレクトリに `resources` フォルダーを作成しそこに `BinOutput, ExcelBinOutput, Readables, Scripts, Subtitle, TextMap` を移動してください *(`resources` フォルダの中身の入手方法については [wiki](https://github.com/Grasscutters/Grasscutter/wiki) を参照してください.)*
3. コマンドプロンプトに`java -jar grasscutter.jar`を入力しGrasscutterを起動してください**このときMongoDBも実行する必要があります** 3. コマンドプロンプトに`java -jar grasscutter.jar`を入力しGrasscutterを起動してください**このときMongoDBも実行する必要があります**
### クライアントとの接続 ### クライアントとの接続
½. [サーバーコンソールコマンド](https://github.com/Grasscutters/Grasscutter/wiki/Commands#targeting)を使用してアカウントを作成してください。 ½. [このコマンド](https://github.com/Grasscutters/Grasscutter/wiki/Commands#commands-for-server-admins)をサーバーコンソールから使用してアカウントを作成してください。
1. 通信内容をリダイレクトする: (どちらか一つを選択してください) 1. 通信内容をリダイレクトする: (どちらか一つを選択してください)
- mitmdump: `mitmdump -s proxy.py -k` - mitmdump: `mitmdump -s proxy.py -k`
CA証明書を信頼する: - CA証明書を信頼する:
**:** CA証明書は`%USERPROFILE%\.mitmproxy`に保存されているか、`http://mitm.it`からダウンロードできます。 - **:** CA証明書は`%USERPROFILE%\.mitmproxy`に保存されています。ダブルクリックして[インストール](https://docs.microsoft.com/en-us/skype-sdk/sdn/articles/installing-the-trusted-root-certificate#installing-a-trusted-root-certificate)するか...
ダブルクリックして[インストール](https://docs.microsoft.com/en-us/skype-sdk/sdn/articles/installing-the-trusted-root-certificate#installing-a-trusted-root-certificate)するか...
- コマンドライン経由でインストールします - コマンドライン経由でインストールします
@ -61,13 +59,14 @@
certutil -addstore root %USERPROFILE%\.mitmproxy\mitmproxy-ca-cert.cer certutil -addstore root %USERPROFILE%\.mitmproxy\mitmproxy-ca-cert.cer
``` ```
- Fiddler Classic: Fiddler Classicを起動し設定から`Decrypt https traffic`をオンにしてください。 (Tools -> Options -> Connections) に有るポート番号の設定を`8888`以外に設定してくださいその後この[スクリプト](https://github.com/Grasscutters/Grasscutter/wiki/Resources#fiddler-classic-jscript)をロードします - Fiddler Classic: Fiddler Classicを起動し(Tools -> Options -> HTTPS)から`Decrypt https traffic`をオンにしてください。 (Tools -> Options -> Connections) に有るポート番号の設定を`8888`以外に設定してくださいその後この[スクリプト](https://github.com/Grasscutters/Grasscutter/wiki/Resources#fiddler-classic-jscript)をFiddlerScriptタブにコピペしてロードします
- [ホストファイル](https://github.com/Grasscutters/Grasscutter/wiki/Resources#hosts-file) - [ホストファイル](https://github.com/Grasscutters/Grasscutter/wiki/Resources#hosts-file)
2. ネットワークプロキシを `127.0.0.1:(自分で設定したポート番号)` に設定してください。 2. ネットワークプロキシを `127.0.0.1:(自分で設定したポート番号)` に設定してください。
- mitmproxyを使用した場合プロキシの設定と証明書のインストールが終わった後、http://mitm.it/ でトラフィックがmitmproxyを通過しているか確認しましょう。
**`start_config.cmd`でJAVAのパスを指定している必要があります。 `start.cmd`でmitmdumpとサーバーをまとめて起動することが出来ます。** **`start.cmd`でmitmdumpとサーバーをまとめて起動することが出来ます。ただ、事前に`start_config.cmd`でJAVAのパスを指定している必要があります。**
### ビルド ### ビルド
@ -75,7 +74,7 @@ GrasscutterはGradleを使用して依存関係とビルドを処理していま
**要件:** **要件:**
- [Java SE 開発キット - 17以降](https://www.oracle.com/java/technologies/javase/jdk17-archive-downloads.html) - [Java SE Development Kits - 17以降](https://www.oracle.com/java/technologies/javase/jdk17-archive-downloads.html)
- [Git](https://git-scm.com/downloads) - [Git](https://git-scm.com/downloads)
##### Windows ##### Windows
@ -96,13 +95,13 @@ chmod +x gradlew
./gradlew jar # コンパイル ./gradlew jar # コンパイル
``` ```
コンパイルされたjarファイルはプロクトフォルダのルートに有ります 生成されたjarファイルはプロジェクトフォルダのルートに有ります
### コマンドリストは[wiki](https://github.com/Grasscutters/Grasscutter/wiki/Commands)へ移動しました。 ### コマンドリストは[wiki](https://github.com/Grasscutters/Grasscutter/wiki/Commands)へ移動しました。
# トラブルシューティング # トラブルシューティング
* コンパイルが失敗した場合JDKがインストールされているか確認してください (JDKの17以降と環境変数でJAVAのパスが設定されている必要があります) * コンパイルが失敗した場合JDKがインストールされているか確認してください(JDKのバージョンが17以降であることと、環境変数でJDKのパスが設定されている必要があります)
* クライアントが接続できないログインできないエラーコード4206、 その他... - ほとんどの場合、プロキシ デーモンの設定が問題です。 * クライアントが接続できないログインできないエラーコード4206・またその他場合、ほとんど、プロキシデーモンの設定が問題です。Fiddlerを使っている場合はデフォルトポートを8888以外の別のポートに変更してみてください。
Fiddlerを使用している場合はポートが8888以外に設定されていることを確認してください Fiddlerを使用している場合はポートが8888以外に設定されていることを確認してください
* 起動シーケンス(順番): MongoDB > Grasscutter > プロキシツール (mitmdumpかfiddlerその他) > ゲーム * 起動シーケンス(順番): MongoDB > Grasscutter > プロキシツール (mitmdumpかfiddlerその他) > ゲーム

View File

@ -43,7 +43,7 @@ sourceCompatibility = JavaVersion.VERSION_17
targetCompatibility = JavaVersion.VERSION_17 targetCompatibility = JavaVersion.VERSION_17
group = 'xyz.grasscutters' group = 'xyz.grasscutters'
version = '1.4.2' version = '1.4.3'
sourceCompatibility = 17 sourceCompatibility = 17
targetCompatibility = 17 targetCompatibility = 17

Binary file not shown.

270
gradlew vendored
View File

@ -1,7 +1,7 @@
#!/usr/bin/env sh #!/bin/sh
# #
# Copyright 2015 the original author or authors. # Copyright © 2015-2021 the original authors.
# #
# Licensed under the Apache License, Version 2.0 (the "License"); # Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License. # you may not use this file except in compliance with the License.
@ -17,78 +17,113 @@
# #
############################################################################## ##############################################################################
## #
## Gradle start up script for UN*X # Gradle start up script for POSIX generated by Gradle.
## #
# Important for running:
#
# (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is
# noncompliant, but you have some other compliant shell such as ksh or
# bash, then to run this script, type that shell name before the whole
# command line, like:
#
# ksh Gradle
#
# Busybox and similar reduced shells will NOT work, because this script
# requires all of these POSIX shell features:
# * functions;
# * expansions «$var», «${var}», «${var:-default}», «${var+SET}»,
# «${var#prefix}», «${var%suffix}», and «$( cmd )»;
# * compound commands having a testable exit status, especially «case»;
# * various built-in commands including «command», «set», and «ulimit».
#
# Important for patching:
#
# (2) This script targets any POSIX shell, so it avoids extensions provided
# by Bash, Ksh, etc; in particular arrays are avoided.
#
# The "traditional" practice of packing multiple parameters into a
# space-separated string is a well documented source of bugs and security
# problems, so this is (mostly) avoided, by progressively accumulating
# options in "$@", and eventually passing that to Java.
#
# Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS,
# and GRADLE_OPTS) rely on word-splitting, this is performed explicitly;
# see the in-line comments for details.
#
# There are tweaks for specific operating systems such as AIX, CygWin,
# Darwin, MinGW, and NonStop.
#
# (3) This script is generated from the Groovy template
# https://github.com/gradle/gradle/blob/master/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
# within the Gradle project.
#
# You can find Gradle at https://github.com/gradle/gradle/.
#
############################################################################## ##############################################################################
# Attempt to set APP_HOME # Attempt to set APP_HOME
# Resolve links: $0 may be a link # Resolve links: $0 may be a link
PRG="$0" app_path=$0
# Need this for relative symlinks.
while [ -h "$PRG" ] ; do # Need this for daisy-chained symlinks.
ls=`ls -ld "$PRG"` while
link=`expr "$ls" : '.*-> \(.*\)$'` APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path
if expr "$link" : '/.*' > /dev/null; then [ -h "$app_path" ]
PRG="$link" do
else ls=$( ls -ld "$app_path" )
PRG=`dirname "$PRG"`"/$link" link=${ls#*' -> '}
fi case $link in #(
/*) app_path=$link ;; #(
*) app_path=$APP_HOME$link ;;
esac
done done
SAVED="`pwd`"
cd "`dirname \"$PRG\"`/" >/dev/null APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit
APP_HOME="`pwd -P`"
cd "$SAVED" >/dev/null
APP_NAME="Gradle" APP_NAME="Gradle"
APP_BASE_NAME=`basename "$0"` APP_BASE_NAME=${0##*/}
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
# Use the maximum available, or set MAX_FD != -1 to use that value. # Use the maximum available, or set MAX_FD != -1 to use that value.
MAX_FD="maximum" MAX_FD=maximum
warn () { warn () {
echo "$*" echo "$*"
} } >&2
die () { die () {
echo echo
echo "$*" echo "$*"
echo echo
exit 1 exit 1
} } >&2
# OS specific support (must be 'true' or 'false'). # OS specific support (must be 'true' or 'false').
cygwin=false cygwin=false
msys=false msys=false
darwin=false darwin=false
nonstop=false nonstop=false
case "`uname`" in case "$( uname )" in #(
CYGWIN* ) CYGWIN* ) cygwin=true ;; #(
cygwin=true Darwin* ) darwin=true ;; #(
;; MSYS* | MINGW* ) msys=true ;; #(
Darwin* ) NONSTOP* ) nonstop=true ;;
darwin=true
;;
MINGW* )
msys=true
;;
NONSTOP* )
nonstop=true
;;
esac esac
CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
# Determine the Java command to use to start the JVM. # Determine the Java command to use to start the JVM.
if [ -n "$JAVA_HOME" ] ; then if [ -n "$JAVA_HOME" ] ; then
if [ -x "$JAVA_HOME/jre/sh/java" ] ; then if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
# IBM's JDK on AIX uses strange locations for the executables # IBM's JDK on AIX uses strange locations for the executables
JAVACMD="$JAVA_HOME/jre/sh/java" JAVACMD=$JAVA_HOME/jre/sh/java
else else
JAVACMD="$JAVA_HOME/bin/java" JAVACMD=$JAVA_HOME/bin/java
fi fi
if [ ! -x "$JAVACMD" ] ; then if [ ! -x "$JAVACMD" ] ; then
die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
@ -97,7 +132,7 @@ Please set the JAVA_HOME variable in your environment to match the
location of your Java installation." location of your Java installation."
fi fi
else else
JAVACMD="java" JAVACMD=java
which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
Please set the JAVA_HOME variable in your environment to match the Please set the JAVA_HOME variable in your environment to match the
@ -105,84 +140,95 @@ location of your Java installation."
fi fi
# Increase the maximum file descriptors if we can. # Increase the maximum file descriptors if we can.
if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then
MAX_FD_LIMIT=`ulimit -H -n` case $MAX_FD in #(
if [ $? -eq 0 ] ; then max*)
if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then MAX_FD=$( ulimit -H -n ) ||
MAX_FD="$MAX_FD_LIMIT" warn "Could not query maximum file descriptor limit"
fi esac
ulimit -n $MAX_FD case $MAX_FD in #(
if [ $? -ne 0 ] ; then '' | soft) :;; #(
warn "Could not set maximum file descriptor limit: $MAX_FD" *)
fi ulimit -n "$MAX_FD" ||
else warn "Could not set maximum file descriptor limit to $MAX_FD"
warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
fi
fi
# For Darwin, add options to specify how the application appears in the dock
if $darwin; then
GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
fi
# For Cygwin or MSYS, switch paths to Windows format before running java
if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then
APP_HOME=`cygpath --path --mixed "$APP_HOME"`
CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
JAVACMD=`cygpath --unix "$JAVACMD"`
# We build the pattern for arguments to be converted via cygpath
ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
SEP=""
for dir in $ROOTDIRSRAW ; do
ROOTDIRS="$ROOTDIRS$SEP$dir"
SEP="|"
done
OURCYGPATTERN="(^($ROOTDIRS))"
# Add a user-defined pattern to the cygpath arguments
if [ "$GRADLE_CYGPATTERN" != "" ] ; then
OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
fi
# Now convert the arguments - kludge to limit ourselves to /bin/sh
i=0
for arg in "$@" ; do
CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
else
eval `echo args$i`="\"$arg\""
fi
i=$((i+1))
done
case $i in
(0) set -- ;;
(1) set -- "$args0" ;;
(2) set -- "$args0" "$args1" ;;
(3) set -- "$args0" "$args1" "$args2" ;;
(4) set -- "$args0" "$args1" "$args2" "$args3" ;;
(5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
(6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
(7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
(8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
(9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
esac esac
fi fi
# Escape application args # Collect all arguments for the java command, stacking in reverse order:
save () { # * args from the command line
for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done # * the main class name
echo " " # * -classpath
} # * -D...appname settings
APP_ARGS=$(save "$@") # * --module-path (only if needed)
# * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables.
# Collect all arguments for the java command, following the shell quoting and substitution rules # For Cygwin or MSYS, switch paths to Windows format before running java
eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" if "$cygwin" || "$msys" ; then
APP_HOME=$( cygpath --path --mixed "$APP_HOME" )
CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" )
# by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong JAVACMD=$( cygpath --unix "$JAVACMD" )
if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then
cd "$(dirname "$0")" # Now convert the arguments - kludge to limit ourselves to /bin/sh
for arg do
if
case $arg in #(
-*) false ;; # don't mess with options #(
/?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath
[ -e "$t" ] ;; #(
*) false ;;
esac
then
arg=$( cygpath --path --ignore --mixed "$arg" )
fi
# Roll the args list around exactly as many times as the number of
# args, so each arg winds up back in the position where it started, but
# possibly modified.
#
# NB: a `for` loop captures its iteration list before it begins, so
# changing the positional parameters here affects neither the number of
# iterations, nor the values presented in `arg`.
shift # remove old arg
set -- "$@" "$arg" # push replacement arg
done
fi fi
# Collect all arguments for the java command;
# * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of
# shell script including quotes and variable substitutions, so put them in
# double quotes to make sure that they get re-expanded; and
# * put everything else in single quotes, so that it's not re-expanded.
set -- \
"-Dorg.gradle.appname=$APP_BASE_NAME" \
-classpath "$CLASSPATH" \
org.gradle.wrapper.GradleWrapperMain \
"$@"
# Use "xargs" to parse quoted args.
#
# With -n1 it outputs one arg per line, with the quotes and backslashes removed.
#
# In Bash we could simply go:
#
# readarray ARGS < <( xargs -n1 <<<"$var" ) &&
# set -- "${ARGS[@]}" "$@"
#
# but POSIX shell has neither arrays nor command substitution, so instead we
# post-process each arg (as a line of input to sed) to backslash-escape any
# character that might be a shell metacharacter, then use eval to reverse
# that process (while maintaining the separation between arguments), and wrap
# the whole thing up as a single "set" statement.
#
# This will of course break if any of these variables contains a newline or
# an unmatched quote.
#
eval "set -- $(
printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" |
xargs -n1 |
sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' |
tr '\n' ' '
)" '"$@"'
exec "$JAVACMD" "$@" exec "$JAVACMD" "$@"

2
gradlew-jar.bat Normal file
View File

@ -0,0 +1,2 @@
call .\gradlew jar
pause

25
gradlew.bat vendored
View File

@ -29,6 +29,9 @@ if "%DIRNAME%" == "" set DIRNAME=.
set APP_BASE_NAME=%~n0 set APP_BASE_NAME=%~n0
set APP_HOME=%DIRNAME% set APP_HOME=%DIRNAME%
@rem Resolve any "." and ".." in APP_HOME to make it shorter.
for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi
@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
@ -37,7 +40,7 @@ if defined JAVA_HOME goto findJavaFromJavaHome
set JAVA_EXE=java.exe set JAVA_EXE=java.exe
%JAVA_EXE% -version >NUL 2>&1 %JAVA_EXE% -version >NUL 2>&1
if "%ERRORLEVEL%" == "0" goto init if "%ERRORLEVEL%" == "0" goto execute
echo. echo.
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
@ -51,7 +54,7 @@ goto fail
set JAVA_HOME=%JAVA_HOME:"=% set JAVA_HOME=%JAVA_HOME:"=%
set JAVA_EXE=%JAVA_HOME%/bin/java.exe set JAVA_EXE=%JAVA_HOME%/bin/java.exe
if exist "%JAVA_EXE%" goto init if exist "%JAVA_EXE%" goto execute
echo. echo.
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
@ -61,28 +64,14 @@ echo location of your Java installation.
goto fail goto fail
:init
@rem Get command-line arguments, handling Windows variants
if not "%OS%" == "Windows_NT" goto win9xME_args
:win9xME_args
@rem Slurp the command line arguments.
set CMD_LINE_ARGS=
set _SKIP=2
:win9xME_args_slurp
if "x%~1" == "x" goto execute
set CMD_LINE_ARGS=%*
:execute :execute
@rem Setup the command line @rem Setup the command line
set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
@rem Execute Gradle @rem Execute Gradle
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %*
:end :end
@rem End local scope for the variables with windows NT shell @rem End local scope for the variables with windows NT shell

View File

@ -6,7 +6,7 @@ import emu.grasscutter.utils.Position;
import emu.grasscutter.utils.Utils; import emu.grasscutter.utils.Utils;
public final class GameConstants { public final class GameConstants {
public static String VERSION = "3.1.0"; public static String VERSION = "3.2.0";
public static final int DEFAULT_TEAMS = 4; public static final int DEFAULT_TEAMS = 4;
public static final int MAX_TEAMS = 10; public static final int MAX_TEAMS = 10;

View File

@ -293,8 +293,8 @@ public final class Grasscutter {
Grasscutter.getLogger().info("EOF detected."); Grasscutter.getLogger().info("EOF detected.");
continue; continue;
} catch (IOError e) { } catch (IOError e) {
Grasscutter.getLogger().error("An IO error occurred.", e); Grasscutter.getLogger().error("An IO error occurred while trying to read from console.", e);
continue; return;
} }
isLastInterrupted = false; isLastInterrupted = false;

View File

@ -33,21 +33,18 @@ public class CommandHelpers {
} }
public static <T> List<String> parseIntParameters(List<String> args, @Nonnull T params, Map<Pattern, BiConsumer<T, Integer>> map) { public static <T> List<String> parseIntParameters(List<String> args, @Nonnull T params, Map<Pattern, BiConsumer<T, Integer>> map) {
for (int i = args.size() - 1; i >= 0; i--) { args.removeIf(arg -> {
String arg = args.get(i).toLowerCase(); var argL = arg.toLowerCase();
boolean deleteArg = false; boolean deleteArg = false;
int argNum;
for (var entry : map.entrySet()) { for (var entry : map.entrySet()) {
if ((argNum = matchIntOrNeg(entry.getKey(), arg)) != -1) { int argNum = matchIntOrNeg(entry.getKey(), argL);
if (argNum != -1) {
entry.getValue().accept(params, argNum); entry.getValue().accept(params, argNum);
deleteArg = true; deleteArg = true;
break;
}
}
if (deleteArg) {
args.remove(i);
} }
} }
return deleteArg;
});
return args; return args;
} }
} }

View File

@ -89,7 +89,7 @@ public final class CommandMap {
} }
public List<Command> getAnnotationsAsList() { public List<Command> getAnnotationsAsList() {
return new LinkedList<>(this.annotations.values()); return new ArrayList<>(this.annotations.values());
} }
public Map<String, Command> getAnnotations() { public Map<String, Command> getAnnotations() {
@ -102,7 +102,7 @@ public final class CommandMap {
* @return All command handlers as a list. * @return All command handlers as a list.
*/ */
public List<CommandHandler> getHandlersAsList() { public List<CommandHandler> getHandlersAsList() {
return new LinkedList<>(this.commands.values()); return new ArrayList<>(this.commands.values());
} }
public Map<String, CommandHandler> getHandlers() { public Map<String, CommandHandler> getHandlers() {
@ -234,8 +234,8 @@ public final class CommandMap {
// Parse message. // Parse message.
String[] split = rawMessage.split(" "); String[] split = rawMessage.split(" ");
List<String> args = new LinkedList<>(Arrays.asList(split)); String label = split[0].toLowerCase();
String label = args.remove(0).toLowerCase(); List<String> args = new ArrayList<>(Arrays.asList(split).subList(1, split.length));
String playerId = (player == null) ? consoleId : player.getAccount().getId(); String playerId = (player == null) ? consoleId : player.getAccount().getId();
// Check for special cases - currently only target command. // Check for special cases - currently only target command.

View File

@ -9,6 +9,7 @@ import emu.grasscutter.game.player.Player;
import java.util.HashMap; import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Objects;
import static emu.grasscutter.utils.Language.translate; import static emu.grasscutter.utils.Language.translate;
@ -78,9 +79,10 @@ public final class SendMailCommand implements CommandHandler {
Grasscutter.getGameServer().getPlayerByUid(mailBuilder.recipient, true).sendMail(mailBuilder.mail); Grasscutter.getGameServer().getPlayerByUid(mailBuilder.recipient, true).sendMail(mailBuilder.mail);
CommandHandler.sendMessage(sender, translate(sender, "commands.sendMail.send_done", mailBuilder.recipient)); CommandHandler.sendMessage(sender, translate(sender, "commands.sendMail.send_done", mailBuilder.recipient));
} else { } else {
for (Player player : DatabaseHelper.getAllPlayers()) { DatabaseHelper.getByGameClass(Player.class).forEach(player -> {
Grasscutter.getGameServer().getPlayerByUid(player.getUid(), true).sendMail(mailBuilder.mail); var onlinePlayer = Grasscutter.getGameServer().getPlayerByUid(player.getUid(), false);
} Objects.requireNonNullElse(onlinePlayer, player).sendMail(mailBuilder.mail);
});
CommandHandler.sendMessage(sender, translate(sender, "commands.sendMail.send_all_done")); CommandHandler.sendMessage(sender, translate(sender, "commands.sendMail.send_all_done"));
} }
mailBeingConstructed.remove(senderId); mailBeingConstructed.remove(senderId);

View File

@ -67,9 +67,10 @@ public final class SetStatsCommand implements CommandHandler {
// Compatibility aliases // Compatibility aliases
this.stats.put("mhp", this.stats.get("maxhp")); this.stats.put("mhp", this.stats.get("maxhp"));
this.stats.put("hp", new Stat(FightProperty.FIGHT_PROP_CUR_HP)); // Overrides FIGHT_PROP_HP this.stats.put("hp", this.stats.get("_cur_hp")); // Overrides FIGHT_PROP_HP
this.stats.put("atk", new Stat(FightProperty.FIGHT_PROP_CUR_ATTACK)); // Overrides FIGHT_PROP_ATTACK this.stats.put("atk", this.stats.get("_cur_attack")); // Overrides FIGHT_PROP_ATTACK
this.stats.put("atkb", new Stat(FightProperty.FIGHT_PROP_BASE_ATTACK)); // This doesn't seem to get used to recalculate ATK, so it's only useful for stuff like Bennett's buff. this.stats.put("def", this.stats.get("_cur_defense")); // Overrides FIGHT_PROP_DEFENSE
this.stats.put("atkb", this.stats.get("_base_attack")); // This doesn't seem to get used to recalculate ATK, so it's only useful for stuff like Bennett's buff.
this.stats.put("eanemo", this.stats.get("anemo%")); this.stats.put("eanemo", this.stats.get("anemo%"));
this.stats.put("ecryo", this.stats.get("cryo%")); this.stats.put("ecryo", this.stats.get("cryo%"));
this.stats.put("edendro", this.stats.get("dendro%")); this.stats.put("edendro", this.stats.get("dendro%"));

View File

@ -1,5 +1,6 @@
package emu.grasscutter.config; package emu.grasscutter.config;
import ch.qos.logback.classic.Level;
import com.google.gson.JsonObject; import com.google.gson.JsonObject;
import emu.grasscutter.Grasscutter; import emu.grasscutter.Grasscutter;
import emu.grasscutter.Grasscutter.ServerDebugMode; import emu.grasscutter.Grasscutter.ServerDebugMode;
@ -101,6 +102,7 @@ public class ConfigContainer {
public Game game = new Game(); public Game game = new Game();
public Dispatch dispatch = new Dispatch(); public Dispatch dispatch = new Dispatch();
public DebugMode debugMode = new DebugMode();
} }
public static class Language { public static class Language {
@ -150,6 +152,10 @@ public class ConfigContainer {
public int kcpInterval = 20; public int kcpInterval = 20;
/* Controls whether packets should be logged in console or not */ /* Controls whether packets should be logged in console or not */
public ServerDebugMode logPackets = ServerDebugMode.NONE; public ServerDebugMode logPackets = ServerDebugMode.NONE;
/* Show packet payload in console or no (in any case the payload is shown in encrypted view) */
public Boolean isShowPacketPayload = false;
/* Show annoying loop packets or no */
public Boolean isShowLoopPackets = false;
public GameOptions gameOptions = new GameOptions(); public GameOptions gameOptions = new GameOptions();
public JoinOptions joinOptions = new JoinOptions(); public JoinOptions joinOptions = new JoinOptions();
@ -163,9 +169,33 @@ public class ConfigContainer {
public String defaultName = "Grasscutter"; public String defaultName = "Grasscutter";
/* Controls whether http requests should be logged in console or not */
public ServerDebugMode logRequests = ServerDebugMode.NONE; public ServerDebugMode logRequests = ServerDebugMode.NONE;
} }
/* Debug options container, used when jar launch argument is -debug | -debugall and override default values
* (see StartupArguments.enableDebug) */
public static class DebugMode {
/* Log level of the main server code (works only with -debug arg) */
public Level serverLoggerLevel = Level.DEBUG;
/* Log level of the third-party services (works only with -debug arg):
javalin, quartz, reflections, jetty, mongodb.driver*/
public Level servicesLoggersLevel = Level.INFO;
/* Controls whether packets should be logged in console or not */
public ServerDebugMode logPackets = ServerDebugMode.ALL;
/* Show packet payload in console or no (in any case the payload is shown in encrypted view) */
public Boolean isShowPacketPayload = false;
/* Show annoying loop packets or no */
public Boolean isShowLoopPackets = false;
/* Controls whether http requests should be logged in console or not */
public ServerDebugMode logRequests = ServerDebugMode.ALL;
}
public static class Encryption { public static class Encryption {
public boolean useEncryption = true; public boolean useEncryption = true;
/* Should 'https' be appended to URLs? */ /* Should 'https' be appended to URLs? */

View File

@ -38,6 +38,7 @@ public final class Configuration extends ConfigContainer {
public static final HTTP HTTP_INFO = config.server.http; public static final HTTP HTTP_INFO = config.server.http;
public static final Game GAME_INFO = config.server.game; public static final Game GAME_INFO = config.server.game;
public static final Dispatch DISPATCH_INFO = config.server.dispatch; public static final Dispatch DISPATCH_INFO = config.server.dispatch;
public static final DebugMode DEBUG_MODE_INFO = config.server.debugMode;
public static final Encryption HTTP_ENCRYPTION = config.server.http.encryption; public static final Encryption HTTP_ENCRYPTION = config.server.http.encryption;
public static final Policies HTTP_POLICIES = config.server.http.policies; public static final Policies HTTP_POLICIES = config.server.http.policies;

View File

@ -13,15 +13,12 @@ import java.util.List;
@Getter @Getter
@FieldDefaults(level = AccessLevel.PRIVATE) @FieldDefaults(level = AccessLevel.PRIVATE)
public class ActivityWatcherData extends GameResource { public class ActivityWatcherData extends GameResource {
@Getter(onMethod = @__(@Override))
int id; int id;
int rewardID; int rewardID;
int progress; int progress;
WatcherTrigger triggerConfig; WatcherTrigger triggerConfig;
@Override
public int getId() {
return this.id;
}
@Override @Override
public void onLoad() { public void onLoad() {
triggerConfig.paramList = triggerConfig.paramList.stream().filter(x -> !x.isBlank()).toList(); triggerConfig.paramList = triggerConfig.paramList.stream().filter(x -> !x.isBlank()).toList();

View File

@ -44,6 +44,7 @@ public class AvatarData extends GameResource {
private float criticalHurt; private float criticalHurt;
private List<PropGrowCurve> propGrowCurves; private List<PropGrowCurve> propGrowCurves;
@Getter(onMethod = @__(@Override))
private int id; private int id;
// Transient // Transient
@ -60,11 +61,6 @@ public class AvatarData extends GameResource {
@Getter private int nameCardRewardId; @Getter private int nameCardRewardId;
@Getter private int nameCardId; @Getter private int nameCardId;
@Override
public int getId() {
return this.id;
}
public float getBaseHp(int level) { public float getBaseHp(int level) {
try { try {
return this.hpBase * this.hpGrowthCurve[level - 1]; return this.hpBase * this.hpGrowthCurve[level - 1];

View File

@ -7,26 +7,18 @@ import emu.grasscutter.game.props.ElementType;
import lombok.Getter; import lombok.Getter;
@ResourceType(name = "AvatarSkillExcelConfigData.json", loadPriority = LoadPriority.HIGHEST) @ResourceType(name = "AvatarSkillExcelConfigData.json", loadPriority = LoadPriority.HIGHEST)
@Getter
public class AvatarSkillData extends GameResource { public class AvatarSkillData extends GameResource {
@Getter(onMethod = @__(@Override))
private int id; private int id;
@Getter private float cdTime; private float cdTime;
@Getter private int costElemVal; private int costElemVal;
@Getter private int maxChargeNum; private int maxChargeNum;
@Getter private int triggerID; private int triggerID;
@Getter private boolean isAttackCameraLock; private boolean isAttackCameraLock;
@Getter private int proudSkillGroupId; private int proudSkillGroupId;
@Getter private ElementType costElemType; private ElementType costElemType;
@Getter private long nameTextMapHash; private long nameTextMapHash;
@Getter private long descTextMapHash; private long descTextMapHash;
@Getter private String abilityName; private String abilityName;
@Override
public int getId() {
return this.id;
}
@Override
public void onLoad() {
}
} }

View File

@ -18,31 +18,27 @@ import it.unimi.dsi.fastutil.ints.IntList;
import lombok.Getter; import lombok.Getter;
@ResourceType(name = "AvatarSkillDepotExcelConfigData.json", loadPriority = LoadPriority.HIGH) @ResourceType(name = "AvatarSkillDepotExcelConfigData.json", loadPriority = LoadPriority.HIGH)
@Getter
public class AvatarSkillDepotData extends GameResource { public class AvatarSkillDepotData extends GameResource {
@Getter(onMethod = @__(@Override))
private int id; private int id;
@Getter private int energySkill; private int energySkill;
@Getter private int attackModeSkill; private int attackModeSkill;
@Getter private List<Integer> skills; private List<Integer> skills;
@Getter private List<Integer> subSkills; private List<Integer> subSkills;
@Getter private List<String> extraAbilities; private List<String> extraAbilities;
@Getter private List<Integer> talents; private List<Integer> talents;
@Getter private List<InherentProudSkillOpens> inherentProudSkillOpens; private List<InherentProudSkillOpens> inherentProudSkillOpens;
@Getter private String talentStarName; private String talentStarName;
@Getter private String skillDepotAbilityGroup; private String skillDepotAbilityGroup;
// Transient // Transient
@Getter private AvatarSkillData energySkillData; private AvatarSkillData energySkillData;
@Getter private ElementType elementType; private ElementType elementType;
@Getter private IntList abilities; private IntList abilities;
@Getter private int talentCostItemId; private int talentCostItemId;
@Override
public int getId() {
return this.id;
}
public void setAbilities(AbilityEmbryoEntry info) { public void setAbilities(AbilityEmbryoEntry info) {
this.abilities = new IntArrayList(info.getAbilities().length); this.abilities = new IntArrayList(info.getAbilities().length);
@ -77,9 +73,10 @@ public class AvatarSkillDepotData extends GameResource {
.ifPresent(itemId -> this.talentCostItemId = itemId); .ifPresent(itemId -> this.talentCostItemId = itemId);
} }
@Getter
public static class InherentProudSkillOpens { public static class InherentProudSkillOpens {
@Getter private int proudSkillGroupId; private int proudSkillGroupId;
@Getter private int needAvatarPromoteLevel; private int needAvatarPromoteLevel;
} }
public IntStream getSkillsAndEnergySkill() { public IntStream getSkillsAndEnergySkill() {

View File

@ -9,16 +9,14 @@ import emu.grasscutter.data.ResourceType;
import emu.grasscutter.game.props.BattlePassMissionRefreshType; import emu.grasscutter.game.props.BattlePassMissionRefreshType;
import emu.grasscutter.game.props.WatcherTriggerType; import emu.grasscutter.game.props.WatcherTriggerType;
import emu.grasscutter.net.proto.BattlePassMissionOuterClass.BattlePassMission.MissionStatus; import emu.grasscutter.net.proto.BattlePassMissionOuterClass.BattlePassMission.MissionStatus;
import lombok.AccessLevel;
import lombok.Getter; import lombok.Getter;
import lombok.Setter;
import lombok.experimental.FieldDefaults;
@ResourceType(name = {"BattlePassMissionExcelConfigData.json"}) @ResourceType(name = {"BattlePassMissionExcelConfigData.json"})
@Getter @Getter
public class BattlePassMissionData extends GameResource { public class BattlePassMissionData extends GameResource {
private int addPoint; @Getter(onMethod = @__(@Override))
private int id; private int id;
private int addPoint;
private int scheduleId; private int scheduleId;
private int progress; private int progress;
private TriggerConfig triggerConfig; private TriggerConfig triggerConfig;
@ -26,11 +24,6 @@ public class BattlePassMissionData extends GameResource {
private transient Set<Integer> mainParams; private transient Set<Integer> mainParams;
@Override
public int getId() {
return this.id;
}
public WatcherTriggerType getTriggerType() { public WatcherTriggerType getTriggerType() {
return this.getTriggerConfig().getTriggerType(); return this.getTriggerConfig().getTriggerType();
} }

View File

@ -7,40 +7,39 @@ import emu.grasscutter.data.ResourceType;
import lombok.Getter; import lombok.Getter;
@ResourceType(name = "BlossomRefreshExcelConfigData.json") @ResourceType(name = "BlossomRefreshExcelConfigData.json")
@Getter
public class BlossomRefreshExcelConfigData extends GameResource { public class BlossomRefreshExcelConfigData extends GameResource {
@Getter(onMethod = @__(@Override))
private int id; private int id;
// Map details // Map details
@Getter private long nameTextMapHash; private long nameTextMapHash;
@Getter private long descTextMapHash; private long descTextMapHash;
@Getter private String icon; private String icon;
@Getter private String clientShowType; // BLOSSOM_SHOWTYPE_CHALLENGE, BLOSSOM_SHOWTYPE_NPCTALK private String clientShowType; // BLOSSOM_SHOWTYPE_CHALLENGE, BLOSSOM_SHOWTYPE_NPCTALK
// Refresh details // Refresh details
@Getter private String refreshType; // Leyline blossoms, magical ore outcrops private String refreshType; // Leyline blossoms, magical ore outcrops
@Getter private int refreshCount; // Number of entries to spawn at refresh (1 for each leyline type for each city, 4 for magical ore for each city) private int refreshCount; // Number of entries to spawn at refresh (1 for each leyline type for each city, 4 for magical ore for each city)
@Getter private String refreshTime; // Server time-of-day to refresh at private String refreshTime; // Server time-of-day to refresh at
@Getter private RefreshCond[] refreshCondVec; // AR requirements etc. private RefreshCond[] refreshCondVec; // AR requirements etc.
@Getter private int cityId; private int cityId;
@Getter private int blossomChestId; // 1 for mora, 2 for exp private int blossomChestId; // 1 for mora, 2 for exp
@Getter private Drop[] dropVec; private Drop[] dropVec;
// Unknown details // Unknown details
// @Getter private int reviseLevel; // @Getter private int reviseLevel;
// @Getter private int campUpdateNeedCount; // Always 1 if specified // @Getter private int campUpdateNeedCount; // Always 1 if specified
@Override @Getter
public int getId() {
return id;
}
public static class Drop { public static class Drop {
@Getter int dropId; int dropId;
@Getter int previewReward; int previewReward;
} }
@Getter
public static class RefreshCond { public static class RefreshCond {
@Getter String type; String type;
@Getter List<Integer> param; List<Integer> param;
} }
} }

View File

@ -8,27 +8,23 @@ import lombok.Setter;
import lombok.experimental.FieldDefaults; import lombok.experimental.FieldDefaults;
import java.util.HashMap; import java.util.HashMap;
import java.util.List;
import java.util.Map; import java.util.Map;
@ResourceType(name = "ChapterExcelConfigData.json") @ResourceType(name = "ChapterExcelConfigData.json")
@Getter @Getter
@Setter @Setter // TODO: remove on next API break
@FieldDefaults(level = AccessLevel.PRIVATE) @FieldDefaults(level = AccessLevel.PRIVATE)
public class ChapterData extends GameResource { public class ChapterData extends GameResource {
@Getter(onMethod = @__(@Override))
int id; int id;
int beginQuestId; int beginQuestId;
int endQuestId; int endQuestId;
int needPlayerLevel; int needPlayerLevel;
// Why public? TODO: privatise next API break
public static final Map<Integer, ChapterData> beginQuestChapterMap = new HashMap<>(); public static final Map<Integer, ChapterData> beginQuestChapterMap = new HashMap<>();
public static final Map<Integer, ChapterData> endQuestChapterMap = new HashMap<>(); public static final Map<Integer, ChapterData> endQuestChapterMap = new HashMap<>();
@Override
public int getId() {
return this.id;
}
@Override @Override
public void onLoad() { public void onLoad() {
beginQuestChapterMap.put(beginQuestId, this); beginQuestChapterMap.put(beginQuestId, this);

View File

@ -22,9 +22,4 @@ public class CityData extends GameResource {
public int getId() { public int getId() {
return this.cityId; return this.cityId;
} }
@Override
public void onLoad() {
super.onLoad();
}
} }

View File

@ -7,18 +7,15 @@ import emu.grasscutter.data.ResourceType;
import lombok.Getter; import lombok.Getter;
@ResourceType(name = {"AnimalCodexExcelConfigData.json"}) @ResourceType(name = {"AnimalCodexExcelConfigData.json"})
@Getter
public class CodexAnimalData extends GameResource { public class CodexAnimalData extends GameResource {
@Getter(onMethod = @__(@Override))
private int Id; private int Id;
@Getter private String type; private String type;
@Getter private int describeId; private int describeId;
@Getter private int sortOrder; private int sortOrder;
@SerializedName(value="countType", alternate={"OCCLHPBCDGL"}) @SerializedName(value="countType", alternate={"OCCLHPBCDGL"})
@Getter private CountType countType; private CountType countType;
@Override
public int getId() {
return Id;
}
public enum CountType { public enum CountType {
CODEX_COUNT_TYPE_KILL, CODEX_COUNT_TYPE_KILL,

View File

@ -8,19 +8,15 @@ import lombok.Getter;
import java.util.List; import java.util.List;
@ResourceType(name = {"CompoundExcelConfigData.json"},loadPriority = ResourceType.LoadPriority.LOW) @ResourceType(name = {"CompoundExcelConfigData.json"},loadPriority = ResourceType.LoadPriority.LOW)
@Getter
public class CompoundData extends GameResource { public class CompoundData extends GameResource {
@Getter(onMethod = @__(@Override))
private int id; private int id;
private int groupId;
@Override private int rankLevel;
public int getId() {return this.id;} private boolean isDefaultUnlocked;
@Getter private int groupId; private int costTime;
@Getter private int rankLevel; private int queueSize;
@Getter private boolean isDefaultUnlocked; private List<ItemParamData> inputVec;
@Getter private int costTime; private List<ItemParamData> outputVec;
@Getter private int queueSize;
@Getter private List<ItemParamData> inputVec;
@Getter private List<ItemParamData> outputVec;
@Override
public void onLoad(){}
} }

View File

@ -9,21 +9,15 @@ import emu.grasscutter.data.common.ItemParamData;
import lombok.Getter; import lombok.Getter;
@ResourceType(name = {"CookRecipeExcelConfigData.json"}, loadPriority = LoadPriority.LOW) @ResourceType(name = {"CookRecipeExcelConfigData.json"}, loadPriority = LoadPriority.LOW)
@Getter
public class CookRecipeData extends GameResource { public class CookRecipeData extends GameResource {
@Getter(onMethod = @__(@Override))
private int id; private int id;
@Getter private int rankLevel; private int rankLevel;
@Getter boolean isDefaultUnlocked; private boolean isDefaultUnlocked;
@Getter int maxProficiency; private int maxProficiency;
@Getter List<ItemParamData> qualityOutputVec; private List<ItemParamData> qualityOutputVec;
@Getter List<ItemParamData> inputVec; private List<ItemParamData> inputVec;
@Override
public int getId() {
return this.id;
}
@Override
public void onLoad() {
}
} }

View File

@ -7,9 +7,11 @@ import emu.grasscutter.data.ResourceType;
import it.unimi.dsi.fastutil.ints.Int2ObjectMap; import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap; import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
import lombok.Getter;
@ResourceType(name = "DailyDungeonConfigData.json") @ResourceType(name = "DailyDungeonConfigData.json")
public class DailyDungeonData extends GameResource { public class DailyDungeonData extends GameResource {
@Getter(onMethod = @__(@Override))
private int id; private int id;
private int[] monday; private int[] monday;
private int[] tuesday; private int[] tuesday;
@ -26,11 +28,6 @@ public class DailyDungeonData extends GameResource {
this.map = new Int2ObjectOpenHashMap<>(); this.map = new Int2ObjectOpenHashMap<>();
} }
@Override
public int getId() {
return this.id;
}
public int[] getDungeonsByDay(int day) { public int[] getDungeonsByDay(int day) {
return map.getOrDefault(day, empty); return map.getOrDefault(day, empty);
} }

View File

@ -3,46 +3,23 @@ package emu.grasscutter.data.excels;
import emu.grasscutter.data.GameData; import emu.grasscutter.data.GameData;
import emu.grasscutter.data.GameResource; import emu.grasscutter.data.GameResource;
import emu.grasscutter.data.ResourceType; import emu.grasscutter.data.ResourceType;
import lombok.Getter;
import emu.grasscutter.game.props.SceneType;
@ResourceType(name = "DungeonExcelConfigData.json") @ResourceType(name = "DungeonExcelConfigData.json")
public class DungeonData extends GameResource { public class DungeonData extends GameResource {
@Getter(onMethod = @__(@Override))
private int id; private int id;
private int sceneId; @Getter private int sceneId;
private int showLevel; @Getter private int showLevel;
private int passRewardPreviewID; private int passRewardPreviewID;
private String involveType; // TODO enum private String involveType; // TODO enum
private RewardPreviewData previewData; private RewardPreviewData previewData;
private int statueCostID; @Getter private int statueCostID;
private int statueCostCount; @Getter private int statueCostCount;
@Override public RewardPreviewData getRewardPreview() {return previewData;}
public int getId() {
return this.id;
}
public int getSceneId() {
return sceneId;
}
public int getShowLevel() {
return showLevel;
}
public RewardPreviewData getRewardPreview() {
return previewData;
}
public int getStatueCostID() {
return statueCostID;
}
public int getStatueCostCount() {
return statueCostCount;
}
@Override @Override
public void onLoad() { public void onLoad() {

View File

@ -7,19 +7,10 @@ import lombok.Setter;
@ResourceType(name = "DungeonEntryExcelConfigData.json") @ResourceType(name = "DungeonEntryExcelConfigData.json")
@Getter @Getter
@Setter @Setter // TODO: remove this next API break
public class DungeonEntryData extends GameResource { public class DungeonEntryData extends GameResource {
@Getter(onMethod = @__(@Override))
private int id;
private int dungeonEntryId; private int dungeonEntryId;
private int sceneId; private int sceneId;
private int id;
@Override
public int getId() {
return this.id;
}
@Override
public void onLoad() {
}
} }

View File

@ -6,9 +6,12 @@ import emu.grasscutter.data.GameResource;
import emu.grasscutter.data.ResourceType; import emu.grasscutter.data.ResourceType;
import emu.grasscutter.data.ResourceType.LoadPriority; import emu.grasscutter.data.ResourceType.LoadPriority;
import emu.grasscutter.data.common.ItemParamData; import emu.grasscutter.data.common.ItemParamData;
import lombok.Getter;
@ResourceType(name = {"ForgeExcelConfigData.json"}, loadPriority = LoadPriority.HIGHEST) @ResourceType(name = {"ForgeExcelConfigData.json"}, loadPriority = LoadPriority.HIGHEST)
@Getter
public class ForgeData extends GameResource { public class ForgeData extends GameResource {
@Getter(onMethod = @__(@Override))
private int id; private int id;
private int playerLevel; private int playerLevel;
private int forgeType; private int forgeType;
@ -21,57 +24,4 @@ public class ForgeData extends GameResource {
private int priority; private int priority;
private int forgePoint; private int forgePoint;
private List<ItemParamData> materialItems; private List<ItemParamData> materialItems;
@Override
public int getId() {
return this.id;
}
public int getPlayerLevel() {
return playerLevel;
}
public int getForgeType() {
return forgeType;
}
public int getResultItemId() {
return resultItemId;
}
public int getResultItemCount() {
return resultItemCount;
}
public int getForgeTime() {
return forgeTime;
}
public int getQueueNum() {
return queueNum;
}
public int getScoinCost() {
return scoinCost;
}
public int getPriority() {
return priority;
}
public int getForgePoint() {
return forgePoint;
}
public List<ItemParamData> getMaterialItems() {
return materialItems;
}
public int getShowItemId() {
return showItemId;
}
@Override
public void onLoad() {
}
} }

View File

@ -3,9 +3,12 @@ package emu.grasscutter.data.excels;
import emu.grasscutter.data.GameResource; import emu.grasscutter.data.GameResource;
import emu.grasscutter.data.ResourceType; import emu.grasscutter.data.ResourceType;
import emu.grasscutter.game.props.EntityType; import emu.grasscutter.game.props.EntityType;
import lombok.Getter;
@ResourceType(name = "GadgetExcelConfigData.json") @ResourceType(name = "GadgetExcelConfigData.json")
@Getter
public class GadgetData extends GameResource { public class GadgetData extends GameResource {
@Getter(onMethod = @__(@Override))
private int id; private int id;
private EntityType type; private EntityType type;
@ -15,42 +18,4 @@ public class GadgetData extends GameResource {
private String itemJsonName; private String itemJsonName;
private long nameTextMapHash; private long nameTextMapHash;
private int campID; private int campID;
@Override
public int getId() {
return this.id;
}
public EntityType getType() {
return type;
}
public String getJsonName() {
return jsonName;
}
public boolean isInteractive() {
return isInteractive;
}
public String[] getTags() {
return tags;
}
public String getItemJsonName() {
return itemJsonName;
}
public long getNameTextMapHash() {
return nameTextMapHash;
}
public int getCampID() {
return campID;
}
@Override
public void onLoad() {
}
} }

View File

@ -29,9 +29,4 @@ public class HomeWorldLevelData extends GameResource {
public int getId() { public int getId() {
return level; return level;
} }
@Override
public void onLoad() {
super.onLoad();
}
} }

View File

@ -4,7 +4,6 @@ import emu.grasscutter.data.GameData;
import emu.grasscutter.data.GameResource; import emu.grasscutter.data.GameResource;
import emu.grasscutter.data.ResourceType; import emu.grasscutter.data.ResourceType;
import lombok.AccessLevel; import lombok.AccessLevel;
import lombok.Data;
import lombok.Getter; import lombok.Getter;
import lombok.experimental.FieldDefaults; import lombok.experimental.FieldDefaults;
@ -14,6 +13,7 @@ import java.util.List;
@Getter @Getter
@FieldDefaults(level = AccessLevel.PRIVATE) @FieldDefaults(level = AccessLevel.PRIVATE)
public class InvestigationMonsterData extends GameResource { public class InvestigationMonsterData extends GameResource {
@Getter(onMethod = @__(@Override))
int id; int id;
int cityId; int cityId;
List<Integer> monsterIdList; List<Integer> monsterIdList;
@ -23,10 +23,6 @@ public class InvestigationMonsterData extends GameResource {
String monsterCategory; String monsterCategory;
CityData cityData; CityData cityData;
@Override
public int getId() {
return this.id;
}
@Override @Override
public void onLoad() { public void onLoad() {

View File

@ -25,6 +25,7 @@ import lombok.Getter;
@Getter @Getter
public class ItemData extends GameResource { public class ItemData extends GameResource {
// Main // Main
@Getter(onMethod = @__(@Override))
private int id; private int id;
private int stackLimit = 1; private int stackLimit = 1;
private int maxUseCount; private int maxUseCount;
@ -81,17 +82,12 @@ public class ItemData extends GameResource {
private List<Integer> furnType; private List<Integer> furnType;
private List<Integer> furnitureGadgetID; private List<Integer> furnitureGadgetID;
@SerializedName(value="roomSceneId", alternate={"DANFGGLKLNO", "JFDLJGDFIGL", "OHIANNAEEAK"}) @SerializedName(value="roomSceneId", alternate={"BMEPAMCNABE", "DANFGGLKLNO", "JFDLJGDFIGL", "OHIANNAEEAK"})
private int roomSceneId; private int roomSceneId;
// Custom // Custom
private transient IntSet addPropLevelSet; private transient IntSet addPropLevelSet;
@Override
public int getId() {
return this.id;
}
public WeaponProperty[] getWeaponProperties() { public WeaponProperty[] getWeaponProperties() {
return this.weaponProp; return this.weaponProp;
} }

View File

@ -1,16 +1,25 @@
package emu.grasscutter.data.excels; package emu.grasscutter.data.excels;
import java.util.List; import java.util.List;
import java.util.Set;
import com.google.gson.annotations.SerializedName;
import emu.grasscutter.data.GameData; import emu.grasscutter.data.GameData;
import emu.grasscutter.data.GameResource; import emu.grasscutter.data.GameResource;
import emu.grasscutter.data.ResourceType; import emu.grasscutter.data.ResourceType;
import emu.grasscutter.data.ResourceType.LoadPriority; import emu.grasscutter.data.ResourceType.LoadPriority;
import emu.grasscutter.data.common.PropGrowCurve; import emu.grasscutter.data.common.PropGrowCurve;
import emu.grasscutter.game.props.FightProperty;
import emu.grasscutter.game.props.MonsterType; import emu.grasscutter.game.props.MonsterType;
import lombok.Getter;
@ResourceType(name = "MonsterExcelConfigData.json", loadPriority = LoadPriority.LOW) @ResourceType(name = "MonsterExcelConfigData.json", loadPriority = LoadPriority.LOW)
@Getter
public class MonsterData extends GameResource { public class MonsterData extends GameResource {
static public Set<FightProperty> definedFightProperties = Set.of(FightProperty.FIGHT_PROP_BASE_HP, FightProperty.FIGHT_PROP_BASE_ATTACK, FightProperty.FIGHT_PROP_BASE_DEFENSE, FightProperty.FIGHT_PROP_PHYSICAL_SUB_HURT, FightProperty.FIGHT_PROP_FIRE_SUB_HURT, FightProperty.FIGHT_PROP_ELEC_SUB_HURT, FightProperty.FIGHT_PROP_WATER_SUB_HURT, FightProperty.FIGHT_PROP_GRASS_SUB_HURT, FightProperty.FIGHT_PROP_WIND_SUB_HURT, FightProperty.FIGHT_PROP_ROCK_SUB_HURT, FightProperty.FIGHT_PROP_ICE_SUB_HURT);
@Getter(onMethod = @__(@Override))
private int id; private int id;
private String monsterName; private String monsterName;
@ -28,9 +37,14 @@ public class MonsterData extends GameResource {
private int describeId; private int describeId;
private int combatBGMLevel; private int combatBGMLevel;
private int entityBudgetLevel; private int entityBudgetLevel;
private float hpBase;
private float attackBase; @SerializedName("hpBase")
private float defenseBase; private float baseHp;
@SerializedName("attackBase")
private float baseAttack;
@SerializedName("defenseBase")
private float baseDefense;
private float fireSubHurt; private float fireSubHurt;
private float elecSubHurt; private float elecSubHurt;
private float grassSubHurt; private float grassSubHurt;
@ -47,125 +61,21 @@ public class MonsterData extends GameResource {
private int weaponId; private int weaponId;
private MonsterDescribeData describeData; private MonsterDescribeData describeData;
@Override public float getFightProperty(FightProperty prop) {
public int getId() { return switch (prop) {
return this.id; case FIGHT_PROP_BASE_HP -> this.baseHp;
} case FIGHT_PROP_BASE_ATTACK -> this.baseAttack;
case FIGHT_PROP_BASE_DEFENSE -> this.baseDefense;
public String getMonsterName() { case FIGHT_PROP_PHYSICAL_SUB_HURT -> this.physicalSubHurt;
return monsterName; case FIGHT_PROP_FIRE_SUB_HURT -> this.fireSubHurt;
} case FIGHT_PROP_ELEC_SUB_HURT -> this.elecSubHurt;
case FIGHT_PROP_WATER_SUB_HURT -> this.waterSubHurt;
public MonsterType getType() { case FIGHT_PROP_GRASS_SUB_HURT -> this.grassSubHurt;
return type; case FIGHT_PROP_WIND_SUB_HURT -> this.windSubHurt;
} case FIGHT_PROP_ROCK_SUB_HURT -> this.rockSubHurt;
case FIGHT_PROP_ICE_SUB_HURT -> this.iceSubHurt;
public String getServerScript() { default -> 0f;
return serverScript; };
}
public List<Integer> getAffix() {
return affix;
}
public String getAi() {
return ai;
}
public int[] getEquips() {
return equips;
}
public List<HpDrops> getHpDrops() {
return hpDrops;
}
public int getKillDropId() {
return killDropId;
}
public String getExcludeWeathers() {
return excludeWeathers;
}
public int getFeatureTagGroupID() {
return featureTagGroupID;
}
public int getMpPropID() {
return mpPropID;
}
public String getSkin() {
return skin;
}
public int getDescribeId() {
return describeId;
}
public int getCombatBGMLevel() {
return combatBGMLevel;
}
public int getEntityBudgetLevel() {
return entityBudgetLevel;
}
public float getBaseHp() {
return hpBase;
}
public float getBaseAttack() {
return attackBase;
}
public float getBaseDefense() {
return defenseBase;
}
public float getElecSubHurt() {
return elecSubHurt;
}
public float getGrassSubHurt() {
return grassSubHurt;
}
public float getWaterSubHurt() {
return waterSubHurt;
}
public float getWindSubHurt() {
return windSubHurt;
}
public float getIceSubHurt() {
return iceSubHurt;
}
public float getPhysicalSubHurt() {
return physicalSubHurt;
}
public List<PropGrowCurve> getPropGrowCurves() {
return propGrowCurves;
}
public long getNameTextMapHash() {
return nameTextMapHash;
}
public int getCampID() {
return campID;
}
public MonsterDescribeData getDescribeData() {
return describeData;
}
public int getWeaponId() {
return weaponId;
} }
@Override @Override
@ -186,15 +96,9 @@ public class MonsterData extends GameResource {
} }
} }
@Getter
public class HpDrops { public class HpDrops {
private int DropId; private int DropId;
private int HpPercent; private int HpPercent;
public int getDropId(){
return this.DropId;
}
public int getHpPercent(){
return this.HpPercent;
}
} }
} }

View File

@ -3,33 +3,14 @@ package emu.grasscutter.data.excels;
import emu.grasscutter.data.GameResource; import emu.grasscutter.data.GameResource;
import emu.grasscutter.data.ResourceType; import emu.grasscutter.data.ResourceType;
import emu.grasscutter.data.ResourceType.LoadPriority; import emu.grasscutter.data.ResourceType.LoadPriority;
import lombok.Getter;
@ResourceType(name = "MonsterDescribeExcelConfigData.json", loadPriority = LoadPriority.HIGH) @ResourceType(name = "MonsterDescribeExcelConfigData.json", loadPriority = LoadPriority.HIGH)
@Getter
public class MonsterDescribeData extends GameResource { public class MonsterDescribeData extends GameResource {
@Getter(onMethod = @__(@Override))
private int id; private int id;
private long nameTextMapHash; private long nameTextMapHash;
private int titleID; private int titleID;
private int specialNameLabID; private int specialNameLabID;
@Override
public int getId() {
return id;
}
public long getNameTextMapHash() {
return nameTextMapHash;
}
public int getTitleID() {
return titleID;
}
public int getSpecialNameLabID() {
return specialNameLabID;
}
@Override
public void onLoad() {
}
} }

View File

@ -10,12 +10,8 @@ import lombok.experimental.FieldDefaults;
@Getter @Getter
@FieldDefaults(level = AccessLevel.PRIVATE) @FieldDefaults(level = AccessLevel.PRIVATE)
public class MusicGameBasicData extends GameResource { public class MusicGameBasicData extends GameResource {
@Getter(onMethod = @__(@Override))
int id; int id;
int musicID; int musicID;
int musicLevel; int musicLevel;
@Override
public int getId() {
return this.id;
}
} }

View File

@ -2,9 +2,12 @@ package emu.grasscutter.data.excels;
import emu.grasscutter.data.GameResource; import emu.grasscutter.data.GameResource;
import emu.grasscutter.data.ResourceType; import emu.grasscutter.data.ResourceType;
import lombok.Getter;
@ResourceType(name = "NpcExcelConfigData.json") @ResourceType(name = "NpcExcelConfigData.json")
@Getter
public class NpcData extends GameResource { public class NpcData extends GameResource {
@Getter(onMethod = @__(@Override))
private int id; private int id;
private String jsonName; private String jsonName;
@ -19,54 +22,4 @@ public class NpcData extends GameResource {
private long nameTextMapHash; private long nameTextMapHash;
private int campID; private int campID;
@Override
public int getId() {
return this.id;
}
public String getJsonName() {
return jsonName;
}
public String getAlias() {
return alias;
}
public String getScriptDataPath() {
return scriptDataPath;
}
public String getLuaDataPath() {
return luaDataPath;
}
public boolean isIsInteractive() {
return isInteractive;
}
public boolean isHasMove() {
return hasMove;
}
public String getDyePart() {
return dyePart;
}
public String getBillboardIcon() {
return billboardIcon;
}
public long getNameTextMapHash() {
return nameTextMapHash;
}
public int getCampID() {
return campID;
}
@Override
public void onLoad() {
}
} }

View File

@ -3,15 +3,14 @@ package emu.grasscutter.data.excels;
import emu.grasscutter.data.GameData; import emu.grasscutter.data.GameData;
import emu.grasscutter.data.GameResource; import emu.grasscutter.data.GameResource;
import emu.grasscutter.data.ResourceType; import emu.grasscutter.data.ResourceType;
import lombok.AccessLevel;
import lombok.Getter; import lombok.Getter;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.Objects;
@ResourceType(name = "OpenStateConfigData.json", loadPriority = ResourceType.LoadPriority.HIGHEST) @ResourceType(name = "OpenStateConfigData.json", loadPriority = ResourceType.LoadPriority.HIGHEST)
public class OpenStateData extends GameResource { public class OpenStateData extends GameResource {
@Getter(onMethod = @__(@Override))
private int id; private int id;
@Getter private boolean defaultState; @Getter private boolean defaultState;
@Getter private boolean allowClientOpen; @Getter private boolean allowClientOpen;
@ -32,11 +31,6 @@ public class OpenStateData extends GameResource {
OPEN_STATE_COND_PARENT_QUEST; OPEN_STATE_COND_PARENT_QUEST;
} }
@Override
public int getId() {
return this.id;
}
@Override @Override
public void onLoad() { public void onLoad() {
// Add this open state to the global list. // Add this open state to the global list.

View File

@ -11,18 +11,13 @@ import java.util.List;
@ResourceType(name = "PersonalLineExcelConfigData.json") @ResourceType(name = "PersonalLineExcelConfigData.json")
@Getter @Getter
@Setter @Setter // TODO: remove setters next API break
@FieldDefaults(level = AccessLevel.PRIVATE) @FieldDefaults(level = AccessLevel.PRIVATE)
public class PersonalLineData extends GameResource { public class PersonalLineData extends GameResource {
@Getter(onMethod = @__(@Override))
int id; int id;
int avatarID; int avatarID;
List<Integer> preQuestId; List<Integer> preQuestId;
int startQuestId; int startQuestId;
int chapterId; int chapterId;
@Override
public int getId() {
return this.id;
}
} }

View File

@ -1,46 +1,23 @@
package emu.grasscutter.data.excels; package emu.grasscutter.data.excels;
import com.google.gson.annotations.SerializedName;
import emu.grasscutter.data.GameResource; import emu.grasscutter.data.GameResource;
import emu.grasscutter.data.ResourceType; import emu.grasscutter.data.ResourceType;
import emu.grasscutter.game.props.FightProperty; import emu.grasscutter.game.props.FightProperty;
import lombok.Getter;
@ResourceType(name = "ReliquaryAffixExcelConfigData.json") @ResourceType(name = "ReliquaryAffixExcelConfigData.json")
@Getter
public class ReliquaryAffixData extends GameResource { public class ReliquaryAffixData extends GameResource {
@Getter(onMethod = @__(@Override))
private int id; private int id;
private int depotId; private int depotId;
private int groupId; private int groupId;
private FightProperty propType; @SerializedName("propType")
private FightProperty fightProp;
private float propValue; private float propValue;
private int weight; private int weight;
private int upgradeWeight; private int upgradeWeight;
@Override
public int getId() {
return id;
}
public int getDepotId() {
return depotId;
}
public int getGroupId() {
return groupId;
}
public float getPropValue() {
return propValue;
}
public int getWeight() {
return weight;
}
public int getUpgradeWeight() {
return upgradeWeight;
}
public FightProperty getFightProp() {
return propType;
}
} }

View File

@ -7,36 +7,19 @@ import emu.grasscutter.data.ResourceType;
import emu.grasscutter.game.props.FightProperty; import emu.grasscutter.game.props.FightProperty;
import it.unimi.dsi.fastutil.ints.Int2FloatMap; import it.unimi.dsi.fastutil.ints.Int2FloatMap;
import it.unimi.dsi.fastutil.ints.Int2FloatOpenHashMap; import it.unimi.dsi.fastutil.ints.Int2FloatOpenHashMap;
import it.unimi.dsi.fastutil.ints.Int2ObjectMap; import lombok.Getter;
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
@ResourceType(name = "ReliquaryLevelExcelConfigData.json") @ResourceType(name = "ReliquaryLevelExcelConfigData.json")
public class ReliquaryLevelData extends GameResource { public class ReliquaryLevelData extends GameResource {
@Getter(onMethod = @__(@Override))
private int id; private int id;
private Int2FloatMap propMap; private Int2FloatMap propMap;
private int rank; @Getter private int rank;
private int level; @Getter private int level;
private int exp; @Getter private int exp;
private List<RelicLevelProperty> addProps; private List<RelicLevelProperty> addProps;
@Override
public int getId() {
return this.id;
}
public int getRank() {
return rank;
}
public int getLevel() {
return level;
}
public int getExp() {
return exp;
}
public float getPropValue(FightProperty prop) { public float getPropValue(FightProperty prop) {
return getPropValue(prop.getId()); return getPropValue(prop.getId());
} }
@ -54,16 +37,9 @@ public class ReliquaryLevelData extends GameResource {
} }
} }
@Getter
public class RelicLevelProperty { public class RelicLevelProperty {
private String propType; private String propType;
private float value; private float value;
public String getPropType() {
return propType;
}
public float getValue() {
return value;
}
} }
} }

View File

@ -1,31 +1,20 @@
package emu.grasscutter.data.excels; package emu.grasscutter.data.excels;
import com.google.gson.annotations.SerializedName;
import emu.grasscutter.data.GameResource; import emu.grasscutter.data.GameResource;
import emu.grasscutter.data.ResourceType; import emu.grasscutter.data.ResourceType;
import emu.grasscutter.game.props.FightProperty; import emu.grasscutter.game.props.FightProperty;
import lombok.Getter;
@ResourceType(name = "ReliquaryMainPropExcelConfigData.json") @ResourceType(name = "ReliquaryMainPropExcelConfigData.json")
@Getter
public class ReliquaryMainPropData extends GameResource { public class ReliquaryMainPropData extends GameResource {
@Getter(onMethod = @__(@Override))
private int id; private int id;
private int propDepotId; private int propDepotId;
private FightProperty propType; @SerializedName("propType")
private FightProperty fightProp;
private int weight; private int weight;
@Override
public int getId() {
return id;
}
public int getPropDepotId() {
return propDepotId;
}
public int getWeight() {
return weight;
}
public FightProperty getFightProp() {
return propType;
}
} }

View File

@ -1,30 +1,22 @@
package emu.grasscutter.data.excels; package emu.grasscutter.data.excels;
import java.util.Arrays; import java.util.Arrays;
import java.util.List;
import emu.grasscutter.Grasscutter;
import emu.grasscutter.data.GameData;
import emu.grasscutter.data.GameResource; import emu.grasscutter.data.GameResource;
import emu.grasscutter.data.ResourceType; import emu.grasscutter.data.ResourceType;
import emu.grasscutter.game.props.SceneType; import lombok.Getter;
import emu.grasscutter.data.ResourceType.LoadPriority; import emu.grasscutter.data.ResourceType.LoadPriority;
import emu.grasscutter.data.common.ItemParamData; import emu.grasscutter.data.common.ItemParamData;
import emu.grasscutter.data.common.ItemParamStringData; import emu.grasscutter.data.common.ItemParamStringData;
@ResourceType(name = "RewardPreviewExcelConfigData.json", loadPriority = LoadPriority.HIGH) @ResourceType(name = "RewardPreviewExcelConfigData.json", loadPriority = LoadPriority.HIGH)
public class RewardPreviewData extends GameResource { public class RewardPreviewData extends GameResource {
@Getter(onMethod = @__(@Override))
private int id; private int id;
private ItemParamStringData[] previewItems; private ItemParamStringData[] previewItems;
private ItemParamData[] previewItemsArray; private ItemParamData[] previewItemsArray;
@Override
public int getId() {
return this.id;
}
public ItemParamData[] getPreviewItems() { public ItemParamData[] getPreviewItems() {
return previewItemsArray; return previewItemsArray;
} }

View File

@ -1,32 +1,19 @@
package emu.grasscutter.data.excels; package emu.grasscutter.data.excels;
import emu.grasscutter.data.GameData; import com.google.gson.annotations.SerializedName;
import emu.grasscutter.data.GameResource; import emu.grasscutter.data.GameResource;
import emu.grasscutter.data.ResourceType; import emu.grasscutter.data.ResourceType;
import emu.grasscutter.game.props.SceneType; import emu.grasscutter.game.props.SceneType;
import lombok.Getter;
@ResourceType(name = "SceneExcelConfigData.json") @ResourceType(name = "SceneExcelConfigData.json")
@Getter
public class SceneData extends GameResource { public class SceneData extends GameResource {
@Getter(onMethod = @__(@Override))
private int id; private int id;
private SceneType type; @SerializedName("type")
private SceneType sceneType;
private String scriptData; private String scriptData;
@Override
public int getId() {
return this.id;
}
public SceneType getSceneType() {
return type;
}
public String getScriptData() {
return scriptData;
}
@Override
public void onLoad() {
}
} }

View File

@ -2,10 +2,11 @@ package emu.grasscutter.data.excels;
import emu.grasscutter.data.GameResource; import emu.grasscutter.data.GameResource;
import emu.grasscutter.data.ResourceType; import emu.grasscutter.data.ResourceType;
import lombok.Getter;
@ResourceType(name = "TowerFloorExcelConfigData.json") @ResourceType(name = "TowerFloorExcelConfigData.json")
@Getter
public class TowerFloorData extends GameResource { public class TowerFloorData extends GameResource {
private int floorId; private int floorId;
private int floorIndex; private int floorIndex;
private int levelGroupId; private int levelGroupId;
@ -17,33 +18,4 @@ public class TowerFloorData extends GameResource {
public int getId() { public int getId() {
return this.floorId; return this.floorId;
} }
@Override
public void onLoad() {
super.onLoad();
}
public int getFloorId() {
return floorId;
}
public int getFloorIndex() {
return floorIndex;
}
public int getLevelGroupId() {
return levelGroupId;
}
public int getOverrideMonsterLevel() {
return overrideMonsterLevel;
}
public int getTeamNum() {
return teamNum;
}
public int getFloorLevelConfigId() {
return floorLevelConfigId;
}
} }

View File

@ -1,6 +1,7 @@
package emu.grasscutter.database; package emu.grasscutter.database;
import java.util.List; import java.util.List;
import java.util.stream.Stream;
import com.mongodb.client.result.DeleteResult; import com.mongodb.client.result.DeleteResult;
@ -154,6 +155,11 @@ public final class DatabaseHelper {
DatabaseManager.getAccountDatastore().find(Account.class).filter(Filters.eq("id", target.getId())).delete(); DatabaseManager.getAccountDatastore().find(Account.class).filter(Filters.eq("id", target.getId())).delete();
} }
public static <T> Stream<T> getByGameClass(Class<T> classType) {
return DatabaseManager.getGameDatastore().find(classType).stream();
}
@Deprecated(forRemoval = true)
public static List<Player> getAllPlayers() { public static List<Player> getAllPlayers() {
return DatabaseManager.getGameDatastore().find(Player.class).stream().toList(); return DatabaseManager.getGameDatastore().find(Player.class).stream().toList();
} }

View File

@ -58,6 +58,7 @@ import emu.grasscutter.net.proto.ShowAvatarInfoOuterClass.ShowAvatarInfo;
import emu.grasscutter.net.proto.ShowEquipOuterClass.ShowEquip; import emu.grasscutter.net.proto.ShowEquipOuterClass.ShowEquip;
import emu.grasscutter.server.packet.send.*; import emu.grasscutter.server.packet.send.*;
import emu.grasscutter.utils.ProtoHelper; import emu.grasscutter.utils.ProtoHelper;
import it.unimi.dsi.fastutil.ints.Int2FloatMap;
import it.unimi.dsi.fastutil.ints.Int2FloatOpenHashMap; import it.unimi.dsi.fastutil.ints.Int2FloatOpenHashMap;
import it.unimi.dsi.fastutil.ints.Int2IntArrayMap; import it.unimi.dsi.fastutil.ints.Int2IntArrayMap;
import it.unimi.dsi.fastutil.ints.Int2IntOpenHashMap; import it.unimi.dsi.fastutil.ints.Int2IntOpenHashMap;
@ -89,7 +90,7 @@ public class Avatar {
private float currentEnergy; private float currentEnergy;
@Transient @Getter private final Int2ObjectMap<GameItem> equips; @Transient @Getter private final Int2ObjectMap<GameItem> equips;
@Transient private final Int2FloatOpenHashMap fightProp; @Transient @Getter private final Int2FloatOpenHashMap fightProperties;
@Transient @Getter private final Int2FloatOpenHashMap fightPropOverrides; @Transient @Getter private final Int2FloatOpenHashMap fightPropOverrides;
@Transient @Getter private Set<String> extraAbilityEmbryos; @Transient @Getter private Set<String> extraAbilityEmbryos;
@ -115,7 +116,7 @@ public class Avatar {
@Deprecated // Do not use. Morhpia only! @Deprecated // Do not use. Morhpia only!
public Avatar() { public Avatar() {
this.equips = new Int2ObjectOpenHashMap<>(); this.equips = new Int2ObjectOpenHashMap<>();
this.fightProp = new Int2FloatOpenHashMap(); this.fightProperties = new Int2FloatOpenHashMap();
this.fightPropOverrides = new Int2FloatOpenHashMap(); this.fightPropOverrides = new Int2FloatOpenHashMap();
this.extraAbilityEmbryos = new HashSet<>(); this.extraAbilityEmbryos = new HashSet<>();
this.fetters = new ArrayList<>(); // TODO Move to avatar this.fetters = new ArrayList<>(); // TODO Move to avatar
@ -276,10 +277,6 @@ public class Avatar {
} }
} }
public Int2FloatOpenHashMap getFightProperties() {
return fightProp;
}
public void setFightProperty(FightProperty prop, float value) { public void setFightProperty(FightProperty prop, float value) {
this.getFightProperties().put(prop.getId(), value); this.getFightProperties().put(prop.getId(), value);
} }
@ -399,9 +396,9 @@ public class Avatar {
public void recalcStats(boolean forceSendAbilityChange) { public void recalcStats(boolean forceSendAbilityChange) {
// Setup // Setup
AvatarData data = this.getAvatarData(); var data = this.getAvatarData();
AvatarPromoteData promoteData = GameData.getAvatarPromoteData(data.getAvatarPromoteId(), this.getPromoteLevel()); var promoteData = GameData.getAvatarPromoteData(data.getAvatarPromoteId(), this.getPromoteLevel());
Int2IntOpenHashMap setMap = new Int2IntOpenHashMap(); var setMap = new Int2IntOpenHashMap();
// Extra ability embryos // Extra ability embryos
Set<String> prevExtraAbilityEmbryos = this.getExtraAbilityEmbryos(); Set<String> prevExtraAbilityEmbryos = this.getExtraAbilityEmbryos();
@ -579,21 +576,11 @@ public class Avatar {
// Add any skill strings from this constellation // Add any skill strings from this constellation
// Set % stats // Set % stats
this.setFightProperty( FightProperty.forEachCompoundProperty(c -> this.setFightProperty(c.getResult(),
FightProperty.FIGHT_PROP_MAX_HP, this.getFightProperty(c.getFlat()) + (this.getFightProperty(c.getBase()) * (1f + this.getFightProperty(c.getPercent())))));
(getFightProperty(FightProperty.FIGHT_PROP_BASE_HP) * (1f + getFightProperty(FightProperty.FIGHT_PROP_HP_PERCENT))) + getFightProperty(FightProperty.FIGHT_PROP_HP)
);
this.setFightProperty(
FightProperty.FIGHT_PROP_CUR_ATTACK,
(getFightProperty(FightProperty.FIGHT_PROP_BASE_ATTACK) * (1f + getFightProperty(FightProperty.FIGHT_PROP_ATTACK_PERCENT))) + getFightProperty(FightProperty.FIGHT_PROP_ATTACK)
);
this.setFightProperty(
FightProperty.FIGHT_PROP_CUR_DEFENSE,
(getFightProperty(FightProperty.FIGHT_PROP_BASE_DEFENSE) * (1f + getFightProperty(FightProperty.FIGHT_PROP_DEFENSE_PERCENT))) + getFightProperty(FightProperty.FIGHT_PROP_DEFENSE)
);
// Reapply all overrides // Reapply all overrides
this.fightProp.putAll(this.fightPropOverrides); this.fightProperties.putAll(this.fightPropOverrides);
// Set current hp // Set current hp
this.setFightProperty(FightProperty.FIGHT_PROP_CUR_HP, this.getFightProperty(FightProperty.FIGHT_PROP_MAX_HP) * hpPercent); this.setFightProperty(FightProperty.FIGHT_PROP_CUR_HP, this.getFightProperty(FightProperty.FIGHT_PROP_MAX_HP) * hpPercent);

View File

@ -1,124 +0,0 @@
package emu.grasscutter.game.avatar;
import java.util.stream.Stream;
import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
public enum AvatarStat {
FIGHT_PROP_NONE(0),
FIGHT_PROP_BASE_HP(1),
FIGHT_PROP_HP(2),
FIGHT_PROP_HP_PERCENT(3),
FIGHT_PROP_BASE_ATTACK(4),
FIGHT_PROP_ATTACK(5),
FIGHT_PROP_ATTACK_PERCENT(6),
FIGHT_PROP_BASE_DEFENSE(7),
FIGHT_PROP_DEFENSE(8),
FIGHT_PROP_DEFENSE_PERCENT(9),
FIGHT_PROP_BASE_SPEED(10),
FIGHT_PROP_SPEED_PERCENT(11),
FIGHT_PROP_HP_MP_PERCENT(12),
FIGHT_PROP_ATTACK_MP_PERCENT(13),
FIGHT_PROP_CRITICAL(20),
FIGHT_PROP_ANTI_CRITICAL(21),
FIGHT_PROP_CRITICAL_HURT(22),
FIGHT_PROP_CHARGE_EFFICIENCY(23),
FIGHT_PROP_ADD_HURT(24),
FIGHT_PROP_SUB_HURT(25),
FIGHT_PROP_HEAL_ADD(26),
FIGHT_PROP_HEALED_ADD(27),
FIGHT_PROP_ELEMENT_MASTERY(28),
FIGHT_PROP_PHYSICAL_SUB_HURT(29),
FIGHT_PROP_PHYSICAL_ADD_HURT(30),
FIGHT_PROP_DEFENCE_IGNORE_RATIO(31),
FIGHT_PROP_DEFENCE_IGNORE_DELTA(32),
FIGHT_PROP_FIRE_ADD_HURT(40),
FIGHT_PROP_ELEC_ADD_HURT(41),
FIGHT_PROP_WATER_ADD_HURT(42),
FIGHT_PROP_GRASS_ADD_HURT(43),
FIGHT_PROP_WIND_ADD_HURT(44),
FIGHT_PROP_ROCK_ADD_HURT(45),
FIGHT_PROP_ICE_ADD_HURT(46),
FIGHT_PROP_HIT_HEAD_ADD_HURT(47),
FIGHT_PROP_FIRE_SUB_HURT(50),
FIGHT_PROP_ELEC_SUB_HURT(51),
FIGHT_PROP_WATER_SUB_HURT(52),
FIGHT_PROP_GRASS_SUB_HURT(53),
FIGHT_PROP_WIND_SUB_HURT(54),
FIGHT_PROP_ROCK_SUB_HURT(55),
FIGHT_PROP_ICE_SUB_HURT(56),
FIGHT_PROP_EFFECT_HIT(60),
FIGHT_PROP_EFFECT_RESIST(61),
FIGHT_PROP_FREEZE_RESIST(62),
FIGHT_PROP_TORPOR_RESIST(63),
FIGHT_PROP_DIZZY_RESIST(64),
FIGHT_PROP_FREEZE_SHORTEN(65),
FIGHT_PROP_TORPOR_SHORTEN(66),
FIGHT_PROP_DIZZY_SHORTEN(67),
FIGHT_PROP_MAX_FIRE_ENERGY(70),
FIGHT_PROP_MAX_ELEC_ENERGY(71),
FIGHT_PROP_MAX_WATER_ENERGY(72),
FIGHT_PROP_MAX_GRASS_ENERGY(73),
FIGHT_PROP_MAX_WIND_ENERGY(74),
FIGHT_PROP_MAX_ICE_ENERGY(75),
FIGHT_PROP_MAX_ROCK_ENERGY(76),
FIGHT_PROP_SKILL_CD_MINUS_RATIO(80),
FIGHT_PROP_SHIELD_COST_MINUS_RATIO(81),
FIGHT_PROP_CUR_FIRE_ENERGY(1000),
FIGHT_PROP_CUR_ELEC_ENERGY(1001),
FIGHT_PROP_CUR_WATER_ENERGY(1002),
FIGHT_PROP_CUR_GRASS_ENERGY(1003),
FIGHT_PROP_CUR_WIND_ENERGY(1004),
FIGHT_PROP_CUR_ICE_ENERGY(1005),
FIGHT_PROP_CUR_ROCK_ENERGY(1006),
FIGHT_PROP_CUR_HP(1010),
FIGHT_PROP_MAX_HP(2000),
FIGHT_PROP_CUR_ATTACK(2001),
FIGHT_PROP_CUR_DEFENSE(2002),
FIGHT_PROP_CUR_SPEED(2003),
FIGHT_PROP_NONEXTRA_ATTACK(3000),
FIGHT_PROP_NONEXTRA_DEFENSE(3001),
FIGHT_PROP_NONEXTRA_CRITICAL(3002),
FIGHT_PROP_NONEXTRA_ANTI_CRITICAL(3003),
FIGHT_PROP_NONEXTRA_CRITICAL_HURT(3004),
FIGHT_PROP_NONEXTRA_CHARGE_EFFICIENCY(3005),
FIGHT_PROP_NONEXTRA_ELEMENT_MASTERY(3006),
FIGHT_PROP_NONEXTRA_PHYSICAL_SUB_HURT(3007),
FIGHT_PROP_NONEXTRA_FIRE_ADD_HURT(3008),
FIGHT_PROP_NONEXTRA_ELEC_ADD_HURT(3009),
FIGHT_PROP_NONEXTRA_WATER_ADD_HURT(3010),
FIGHT_PROP_NONEXTRA_GRASS_ADD_HURT(3011),
FIGHT_PROP_NONEXTRA_WIND_ADD_HURT(3012),
FIGHT_PROP_NONEXTRA_ROCK_ADD_HURT(3013),
FIGHT_PROP_NONEXTRA_ICE_ADD_HURT(3014),
FIGHT_PROP_NONEXTRA_FIRE_SUB_HURT(3015),
FIGHT_PROP_NONEXTRA_ELEC_SUB_HURT(3016),
FIGHT_PROP_NONEXTRA_WATER_SUB_HURT(3017),
FIGHT_PROP_NONEXTRA_GRASS_SUB_HURT(3018),
FIGHT_PROP_NONEXTRA_WIND_SUB_HURT(3019),
FIGHT_PROP_NONEXTRA_ROCK_SUB_HURT(3020),
FIGHT_PROP_NONEXTRA_ICE_SUB_HURT(3021),
FIGHT_PROP_NONEXTRA_SKILL_CD_MINUS_RATIO(3022),
FIGHT_PROP_NONEXTRA_SHIELD_COST_MINUS_RATIO(3023),
FIGHT_PROP_NONEXTRA_PHYSICAL_ADD_HURT(3024);
private final int id;
private static final Int2ObjectMap<AvatarStat> map = new Int2ObjectOpenHashMap<>();
static {
Stream.of(values()).forEach(e -> map.put(e.getId(), e));
}
private AvatarStat(int id) {
this.id = id;
}
public int getId() {
return id;
}
public static AvatarStat getStatById(int value) {
return map.getOrDefault(value, FIGHT_PROP_NONE);
}
}

View File

@ -21,7 +21,6 @@ import emu.grasscutter.net.proto.ChangeHpReasonOuterClass.ChangeHpReason;
import emu.grasscutter.net.proto.EntityAuthorityInfoOuterClass.EntityAuthorityInfo; import emu.grasscutter.net.proto.EntityAuthorityInfoOuterClass.EntityAuthorityInfo;
import emu.grasscutter.net.proto.EntityClientDataOuterClass.EntityClientData; import emu.grasscutter.net.proto.EntityClientDataOuterClass.EntityClientData;
import emu.grasscutter.net.proto.EntityRendererChangedInfoOuterClass.EntityRendererChangedInfo; import emu.grasscutter.net.proto.EntityRendererChangedInfoOuterClass.EntityRendererChangedInfo;
import emu.grasscutter.net.proto.FightPropPairOuterClass.FightPropPair;
import emu.grasscutter.net.proto.PlayerDieTypeOuterClass.PlayerDieType; import emu.grasscutter.net.proto.PlayerDieTypeOuterClass.PlayerDieType;
import emu.grasscutter.net.proto.PropChangeReasonOuterClass.PropChangeReason; import emu.grasscutter.net.proto.PropChangeReasonOuterClass.PropChangeReason;
import emu.grasscutter.net.proto.PropPairOuterClass.PropPair; import emu.grasscutter.net.proto.PropPairOuterClass.PropPair;
@ -38,19 +37,24 @@ import emu.grasscutter.utils.Position;
import emu.grasscutter.utils.ProtoHelper; import emu.grasscutter.utils.ProtoHelper;
import emu.grasscutter.utils.Utils; import emu.grasscutter.utils.Utils;
import it.unimi.dsi.fastutil.ints.Int2FloatMap; import it.unimi.dsi.fastutil.ints.Int2FloatMap;
import it.unimi.dsi.fastutil.ints.Int2FloatOpenHashMap; import lombok.Getter;
import lombok.val; import lombok.val;
public class EntityAvatar extends GameEntity { public class EntityAvatar extends GameEntity {
private final Avatar avatar; @Getter private final Avatar avatar;
private PlayerDieType killedType; @Getter private PlayerDieType killedType;
private int killedBy; @Getter private int killedBy;
public EntityAvatar(Avatar avatar) {
this(null, avatar);
}
public EntityAvatar(Scene scene, Avatar avatar) { public EntityAvatar(Scene scene, Avatar avatar) {
super(scene); super(scene);
this.avatar = avatar; this.avatar = avatar;
this.avatar.setCurrentEnergy(); this.avatar.setCurrentEnergy();
if (scene != null)
this.id = getScene().getWorld().getNextEntityId(EntityIdType.AVATAR); this.id = getScene().getWorld().getNextEntityId(EntityIdType.AVATAR);
GameItem weapon = this.getAvatar().getWeapon(); GameItem weapon = this.getAvatar().getWeapon();
@ -59,47 +63,20 @@ public class EntityAvatar extends GameEntity {
} }
} }
public EntityAvatar(Avatar avatar) { public Player getPlayer() {return this.avatar.getPlayer();}
super(null);
this.avatar = avatar;
this.avatar.setCurrentEnergy();
}
public Player getPlayer() {
return avatar.getPlayer();
}
@Override @Override
public Position getPosition() { public Position getPosition() {return getPlayer().getPosition();}
return getPlayer().getPosition();
}
@Override @Override
public Position getRotation() { public Position getRotation() {return getPlayer().getRotation();}
return getPlayer().getRotation();
}
public Avatar getAvatar() {
return avatar;
}
public int getKilledBy() {
return killedBy;
}
public PlayerDieType getKilledType() {
return killedType;
}
@Override @Override
public boolean isAlive() { public boolean isAlive() {
return this.getFightProperty(FightProperty.FIGHT_PROP_CUR_HP) > 0f; return this.getFightProperty(FightProperty.FIGHT_PROP_CUR_HP) > 0f;
} }
@Override @Override public Int2FloatMap getFightProperties() {return getAvatar().getFightProperties();}
public Int2FloatOpenHashMap getFightProperties() {
return getAvatar().getFightProperties();
}
public int getWeaponEntityId() { public int getWeaponEntityId() {
if (getAvatar().getWeapon() != null) { if (getAvatar().getWeapon() != null) {
@ -145,11 +122,8 @@ public class EntityAvatar extends GameEntity {
public void clearEnergy(ChangeEnergyReason reason) { public void clearEnergy(ChangeEnergyReason reason) {
// Fight props. // Fight props.
FightProperty curEnergyProp = this.getAvatar().getSkillDepot().getElementType().getCurEnergyProp(); val curEnergyProp = this.getAvatar().getSkillDepot().getElementType().getCurEnergyProp();
FightProperty maxEnergyProp = this.getAvatar().getSkillDepot().getElementType().getMaxEnergyProp(); float curEnergy = this.getFightProperty(curEnergyProp);
// Get max energy.
float maxEnergy = this.avatar.getFightProperty(maxEnergyProp);
// Set energy to zero. // Set energy to zero.
this.avatar.setCurrentEnergy(curEnergyProp, 0); this.avatar.setCurrentEnergy(curEnergyProp, 0);
@ -158,7 +132,7 @@ public class EntityAvatar extends GameEntity {
this.getScene().broadcastPacket(new PacketEntityFightPropUpdateNotify(this, curEnergyProp)); this.getScene().broadcastPacket(new PacketEntityFightPropUpdateNotify(this, curEnergyProp));
if (reason == ChangeEnergyReason.CHANGE_ENERGY_REASON_SKILL_START) { if (reason == ChangeEnergyReason.CHANGE_ENERGY_REASON_SKILL_START) {
this.getScene().broadcastPacket(new PacketEntityFightPropChangeReasonNotify(this, curEnergyProp, -maxEnergy, reason)); this.getScene().broadcastPacket(new PacketEntityFightPropChangeReasonNotify(this, curEnergyProp, -curEnergy, reason));
} }
} }
@ -167,18 +141,16 @@ public class EntityAvatar extends GameEntity {
} }
public void addEnergy(float amount, PropChangeReason reason, boolean isFlat) { public void addEnergy(float amount, PropChangeReason reason, boolean isFlat) {
// Get current and maximum energy for this avatar. // Get current and maximum energy for this avatar.
FightProperty curEnergyProp = this.getAvatar().getSkillDepot().getElementType().getCurEnergyProp(); val elementType = this.getAvatar().getSkillDepot().getElementType();
FightProperty maxEnergyProp = this.getAvatar().getSkillDepot().getElementType().getMaxEnergyProp(); val curEnergyProp = elementType.getCurEnergyProp();
val maxEnergyProp = elementType.getMaxEnergyProp();
float curEnergy = this.getFightProperty(curEnergyProp); float curEnergy = this.getFightProperty(curEnergyProp);
float maxEnergy = this.getFightProperty(maxEnergyProp); float maxEnergy = this.getFightProperty(maxEnergyProp);
// Get energy recharge.
float energyRecharge = this.getFightProperty(FightProperty.FIGHT_PROP_CHARGE_EFFICIENCY);
// Scale amount by energy recharge, if the amount is not flat. // Scale amount by energy recharge, if the amount is not flat.
if (!isFlat) { if (!isFlat) {
amount *= energyRecharge; amount *= this.getFightProperty(FightProperty.FIGHT_PROP_CHARGE_EFFICIENCY);
} }
// Determine the new energy value. // Determine the new energy value.

View File

@ -3,11 +3,23 @@ package emu.grasscutter.game.entity;
import emu.grasscutter.data.binout.ConfigGadget; import emu.grasscutter.data.binout.ConfigGadget;
import emu.grasscutter.game.props.FightProperty; import emu.grasscutter.game.props.FightProperty;
import emu.grasscutter.game.world.Scene; import emu.grasscutter.game.world.Scene;
import emu.grasscutter.utils.Position;
import lombok.Getter;
public abstract class EntityBaseGadget extends GameEntity { public abstract class EntityBaseGadget extends GameEntity {
@Getter(onMethod = @__(@Override))
protected final Position position;
@Getter(onMethod = @__(@Override))
protected final Position rotation;
public EntityBaseGadget(Scene scene) { public EntityBaseGadget(Scene scene) {
this(scene, null, null);
}
public EntityBaseGadget(Scene scene, Position position, Position rotation) {
super(scene); super(scene);
this.position = position != null ? position.clone() : new Position();
this.rotation = rotation != null ? rotation.clone() : new Position();
} }
public abstract int getGadgetId(); public abstract int getGadgetId();

View File

@ -19,30 +19,28 @@ import emu.grasscutter.net.proto.SceneGadgetInfoOuterClass.SceneGadgetInfo;
import emu.grasscutter.net.proto.VectorOuterClass.Vector; import emu.grasscutter.net.proto.VectorOuterClass.Vector;
import emu.grasscutter.utils.Position; import emu.grasscutter.utils.Position;
import emu.grasscutter.utils.ProtoHelper; import emu.grasscutter.utils.ProtoHelper;
import it.unimi.dsi.fastutil.ints.Int2FloatOpenHashMap; import it.unimi.dsi.fastutil.ints.Int2FloatMap;
import lombok.Getter;
public class EntityClientGadget extends EntityBaseGadget { public class EntityClientGadget extends EntityBaseGadget {
private final Player owner; @Getter private final Player owner;
private final Position pos; @Getter(onMethod = @__(@Override))
private final Position rot; private int gadgetId;
private int configId; @Getter private int campId;
private int campId; @Getter private int campType;
private int campType; @Getter private int ownerEntityId;
private int ownerEntityId; @Getter private int targetEntityId;
private int targetEntityId; @Getter private boolean asyncLoad;
private boolean asyncLoad;
private int originalOwnerEntityId; @Getter private int originalOwnerEntityId;
public EntityClientGadget(Scene scene, Player player, EvtCreateGadgetNotify notify) { public EntityClientGadget(Scene scene, Player player, EvtCreateGadgetNotify notify) {
super(scene); super(scene, new Position(notify.getInitPos()), new Position(notify.getInitEulerAngles()));
this.owner = player; this.owner = player;
this.id = notify.getEntityId(); this.id = notify.getEntityId();
this.pos = new Position(notify.getInitPos()); this.gadgetId = notify.getConfigId();
this.rot = new Position(notify.getInitEulerAngles());
this.configId = notify.getConfigId();
this.campId = notify.getCampId(); this.campId = notify.getCampId();
this.campType = notify.getCampType(); this.campType = notify.getCampType();
this.ownerEntityId = notify.getPropOwnerEntityId(); this.ownerEntityId = notify.getPropOwnerEntityId();
@ -58,61 +56,12 @@ public class EntityClientGadget extends EntityBaseGadget {
} }
} }
@Override
public int getGadgetId() {
return configId;
}
public Player getOwner() {
return owner;
}
public int getCampId() {
return campId;
}
public int getCampType() {
return campType;
}
public int getOwnerEntityId() {
return ownerEntityId;
}
public int getTargetEntityId() {
return targetEntityId;
}
public boolean isAsyncLoad() {
return this.asyncLoad;
}
public int getOriginalOwnerEntityId() {
return this.originalOwnerEntityId;
}
@Override @Override
public void onDeath(int killerId) { public void onDeath(int killerId) {
super.onDeath(killerId); // Invoke super class's onDeath() method. super.onDeath(killerId); // Invoke super class's onDeath() method.
} }
@Override @Override public Int2FloatMap getFightProperties() {return null;}
public Int2FloatOpenHashMap getFightProperties() {
// TODO Auto-generated method stub
return null;
}
@Override
public Position getPosition() {
// TODO Auto-generated method stub
return this.pos;
}
@Override
public Position getRotation() {
// TODO Auto-generated method stub
return this.rot;
}
@Override @Override
public SceneEntityInfo toProto() { public SceneEntityInfo toProto() {

View File

@ -6,8 +6,6 @@ import emu.grasscutter.data.excels.GadgetData;
import emu.grasscutter.game.entity.gadget.*; import emu.grasscutter.game.entity.gadget.*;
import emu.grasscutter.game.player.Player; import emu.grasscutter.game.player.Player;
import emu.grasscutter.game.props.EntityIdType; import emu.grasscutter.game.props.EntityIdType;
import emu.grasscutter.game.props.EntityType;
import emu.grasscutter.game.props.FightProperty;
import emu.grasscutter.game.props.PlayerProperty; import emu.grasscutter.game.props.PlayerProperty;
import emu.grasscutter.game.world.Scene; import emu.grasscutter.game.world.Scene;
import emu.grasscutter.net.proto.AbilitySyncStateInfoOuterClass.AbilitySyncStateInfo; import emu.grasscutter.net.proto.AbilitySyncStateInfoOuterClass.AbilitySyncStateInfo;
@ -29,77 +27,47 @@ import emu.grasscutter.scripts.data.ScriptArgs;
import emu.grasscutter.server.packet.send.PacketGadgetStateNotify; import emu.grasscutter.server.packet.send.PacketGadgetStateNotify;
import emu.grasscutter.utils.Position; import emu.grasscutter.utils.Position;
import emu.grasscutter.utils.ProtoHelper; import emu.grasscutter.utils.ProtoHelper;
import it.unimi.dsi.fastutil.ints.Int2FloatMap;
import it.unimi.dsi.fastutil.ints.Int2FloatOpenHashMap; import it.unimi.dsi.fastutil.ints.Int2FloatOpenHashMap;
import lombok.Getter; import lombok.Getter;
import lombok.Setter;
import lombok.ToString; import lombok.ToString;
import java.util.Optional;
import javax.annotation.Nullable; import javax.annotation.Nullable;
@ToString(callSuper = true) @ToString(callSuper = true)
public class EntityGadget extends EntityBaseGadget { public class EntityGadget extends EntityBaseGadget {
private final GadgetData data; @Getter private final GadgetData gadgetData;
private final Position pos; @Getter(onMethod = @__(@Override)) @Setter
private final Position rot;
private int gadgetId; private int gadgetId;
private int state; @Getter @Setter private int state;
private int pointType; @Getter @Setter private int pointType;
private GadgetContent content; @Getter private GadgetContent content;
private Int2FloatOpenHashMap fightProp; @Getter(onMethod = @__(@Override), lazy = true)
private SceneGadget metaGadget; private final Int2FloatMap fightProperties = new Int2FloatOpenHashMap();
@Getter @Setter private SceneGadget metaGadget;
@Nullable @Getter @Nullable @Getter
private ConfigGadget configGadget; private ConfigGadget configGadget;
public EntityGadget(Scene scene, int gadgetId, Position pos, Position rot) { public EntityGadget(Scene scene, int gadgetId, Position pos) {
super(scene); this(scene, gadgetId, pos, null, null);
this.data = GameData.getGadgetDataMap().get(gadgetId);
if (data!=null && data.getJsonName()!=null) {
this.configGadget = GameData.getGadgetConfigData().get(data.getJsonName());
}
this.id = getScene().getWorld().getNextEntityId(EntityIdType.GADGET);
this.gadgetId = gadgetId;
this.pos = pos.clone();
this.rot = rot != null ? rot.clone() : new Position();
fillFightProps(configGadget);
} }
public EntityGadget(Scene scene, int gadgetId, Position pos) { public EntityGadget(Scene scene, int gadgetId, Position pos, Position rot) {
this(scene, gadgetId, pos, new Position()); this(scene, gadgetId, pos, rot, null);
} }
public EntityGadget(Scene scene, int gadgetId, Position pos, Position rot, GadgetContent content) { public EntityGadget(Scene scene, int gadgetId, Position pos, Position rot, GadgetContent content) {
this(scene, gadgetId, pos, rot); super(scene, pos, rot);
this.content = content; this.gadgetData = GameData.getGadgetDataMap().get(gadgetId);
} this.configGadget = Optional.ofNullable(this.gadgetData).map(GadgetData::getJsonName).map(GameData.getGadgetConfigData()::get).orElse(null);
this.id = this.getScene().getWorld().getNextEntityId(EntityIdType.GADGET);
public GadgetData getGadgetData() {
return data;
}
@Override
public Position getPosition() {
return this.pos;
}
@Override
public Position getRotation() {
return this.rot;
}
public int getGadgetId() {
return gadgetId;
}
public void setGadgetId(int gadgetId) {
this.gadgetId = gadgetId; this.gadgetId = gadgetId;
} this.content = content;
fillFightProps(configGadget);
public int getState() {
return state;
}
public void setState(int state) {
this.state = state;
} }
public void updateState(int state) { public void updateState(int state) {
@ -108,39 +76,18 @@ public class EntityGadget extends EntityBaseGadget {
getScene().getScriptManager().callEvent(EventType.EVENT_GADGET_STATE_CHANGE, new ScriptArgs(state, this.getConfigId())); getScene().getScriptManager().callEvent(EventType.EVENT_GADGET_STATE_CHANGE, new ScriptArgs(state, this.getConfigId()));
} }
public int getPointType() { @Deprecated(forRemoval = true) // Dont use!
return pointType;
}
public void setPointType(int pointType) {
this.pointType = pointType;
}
public GadgetContent getContent() {
return content;
}
@Deprecated // Dont use!
public void setContent(GadgetContent content) { public void setContent(GadgetContent content) {
this.content = this.content == null ? content : this.content; this.content = this.content == null ? content : this.content;
} }
public SceneGadget getMetaGadget() {
return metaGadget;
}
public void setMetaGadget(SceneGadget metaGadget) {
this.metaGadget = metaGadget;
}
// TODO refactor // TODO refactor
public void buildContent() { public void buildContent() {
if (getContent() != null || getGadgetData() == null || getGadgetData().getType() == null) { if (this.getContent() != null || this.getGadgetData() == null || this.getGadgetData().getType() == null) {
return; return;
} }
EntityType type = getGadgetData().getType(); this.content = switch (this.getGadgetData().getType()) {
GadgetContent content = switch (type) {
case GatherPoint -> new GadgetGatherPoint(this); case GatherPoint -> new GadgetGatherPoint(this);
case GatherObject -> new GadgetGatherObject(this); case GatherObject -> new GadgetGatherObject(this);
case Worktop -> new GadgetWorktop(this); case Worktop -> new GadgetWorktop(this);
@ -149,14 +96,6 @@ public class EntityGadget extends EntityBaseGadget {
case Gadget -> new GadgetObject(this); case Gadget -> new GadgetObject(this);
default -> null; default -> null;
}; };
this.content = content;
}
@Override
public Int2FloatOpenHashMap getFightProperties() {
if (this.fightProp == null) this.fightProp = new Int2FloatOpenHashMap();
return this.fightProp;
} }
@Override @Override
@ -216,7 +155,7 @@ public class EntityGadget extends EntityBaseGadget {
entityInfo.addPropList(pair); entityInfo.addPropList(pair);
// We do not use the getter to null check because the getter will create a fight prop map if it is null // We do not use the getter to null check because the getter will create a fight prop map if it is null
if (this.fightProp != null) { if (this.fightProperties != null) {
addAllFightPropsToEntityInfo(entityInfo); addAllFightPropsToEntityInfo(entityInfo);
} }

View File

@ -25,57 +25,33 @@ import emu.grasscutter.net.proto.VectorOuterClass.Vector;
import emu.grasscutter.server.packet.send.PacketGadgetInteractRsp; import emu.grasscutter.server.packet.send.PacketGadgetInteractRsp;
import emu.grasscutter.utils.Position; import emu.grasscutter.utils.Position;
import emu.grasscutter.utils.ProtoHelper; import emu.grasscutter.utils.ProtoHelper;
import it.unimi.dsi.fastutil.ints.Int2FloatOpenHashMap; import it.unimi.dsi.fastutil.ints.Int2FloatMap;
import lombok.Getter;
public class EntityItem extends EntityBaseGadget { public class EntityItem extends EntityBaseGadget {
private final Position pos; @Getter private final GameItem item;
private final Position rot; @Getter private final long guid;
@Getter private final boolean share;
private final GameItem item;
private final long guid;
private final boolean share;
public EntityItem(Scene scene, Player player, ItemData itemData, Position pos, int count) { public EntityItem(Scene scene, Player player, ItemData itemData, Position pos, int count) {
super(scene); this(scene, player, itemData, pos, count, true);
this.id = getScene().getWorld().getNextEntityId(EntityIdType.GADGET);
this.pos = new Position(pos);
this.rot = new Position();
this.guid = player == null ? scene.getWorld().getHost().getNextGameGuid() : player.getNextGameGuid();
this.item = new GameItem(itemData, count);
this.share = true;
} }
// In official game, some drop items are shared to all players, and some other items are independent to all players // In official game, some drop items are shared to all players, and some other items are independent to all players
// For example, if you killed a monster in MP mode, all players could get drops but rarity and number of them are different // For example, if you killed a monster in MP mode, all players could get drops but rarity and number of them are different
// but if you broke regional mine, when someone picked up the drop then it disappeared // but if you broke regional mine, when someone picked up the drop then it disappeared
public EntityItem(Scene scene, Player player, ItemData itemData, Position pos, int count, boolean share) { public EntityItem(Scene scene, Player player, ItemData itemData, Position pos, int count, boolean share) {
super(scene); super(scene, pos, null);
this.id = getScene().getWorld().getNextEntityId(EntityIdType.GADGET); this.id = getScene().getWorld().getNextEntityId(EntityIdType.GADGET);
this.pos = new Position(pos);
this.rot = new Position();
this.guid = player == null ? scene.getWorld().getHost().getNextGameGuid() : player.getNextGameGuid(); this.guid = player == null ? scene.getWorld().getHost().getNextGameGuid() : player.getNextGameGuid();
this.item = new GameItem(itemData, count); this.item = new GameItem(itemData, count);
this.share = share; this.share = share;
} }
@Override
public int getId() {
return this.id;
}
private GameItem getItem() {
return this.item;
}
public ItemData getItemData() { public ItemData getItemData() {
return this.getItem().getItemData(); return this.getItem().getItemData();
} }
public long getGuid() {
return guid;
}
public int getCount() { public int getCount() {
return this.getItem().getCount(); return this.getItem().getCount();
} }
@ -85,24 +61,7 @@ public class EntityItem extends EntityBaseGadget {
return this.getItemData().getGadgetId(); return this.getItemData().getGadgetId();
} }
@Override @Override public Int2FloatMap getFightProperties() {return null;}
public Position getPosition() {
return this.pos;
}
@Override
public Position getRotation() {
return this.rot;
}
@Override
public Int2FloatOpenHashMap getFightProperties() {
return null;
}
public boolean isShare() {
return share;
}
@Override @Override
public void onInteract(Player player, GadgetInteractReq interactReq) { public void onInteract(Player player, GadgetInteractReq interactReq) {

View File

@ -1,14 +1,12 @@
package emu.grasscutter.game.entity; package emu.grasscutter.game.entity;
import emu.grasscutter.Grasscutter; import java.util.Optional;
import emu.grasscutter.data.GameData; import emu.grasscutter.data.GameData;
import emu.grasscutter.data.common.ItemParamData;
import emu.grasscutter.data.common.PropGrowCurve; import emu.grasscutter.data.common.PropGrowCurve;
import emu.grasscutter.data.excels.EnvAnimalGatherConfigData; import emu.grasscutter.data.excels.EnvAnimalGatherConfigData;
import emu.grasscutter.data.excels.ItemData;
import emu.grasscutter.data.excels.MonsterCurveData; import emu.grasscutter.data.excels.MonsterCurveData;
import emu.grasscutter.data.excels.MonsterData; import emu.grasscutter.data.excels.MonsterData;
import emu.grasscutter.game.inventory.GameItem;
import emu.grasscutter.game.player.Player; import emu.grasscutter.game.player.Player;
import emu.grasscutter.game.props.ActionReason; import emu.grasscutter.game.props.ActionReason;
import emu.grasscutter.game.props.EntityIdType; import emu.grasscutter.game.props.EntityIdType;
@ -16,13 +14,11 @@ import emu.grasscutter.game.props.FightProperty;
import emu.grasscutter.game.props.PlayerProperty; import emu.grasscutter.game.props.PlayerProperty;
import emu.grasscutter.game.props.WatcherTriggerType; import emu.grasscutter.game.props.WatcherTriggerType;
import emu.grasscutter.game.world.Scene; import emu.grasscutter.game.world.Scene;
import emu.grasscutter.net.proto.VisionTypeOuterClass;
import emu.grasscutter.net.proto.AbilitySyncStateInfoOuterClass.AbilitySyncStateInfo; import emu.grasscutter.net.proto.AbilitySyncStateInfoOuterClass.AbilitySyncStateInfo;
import emu.grasscutter.net.proto.AnimatorParameterValueInfoPairOuterClass.AnimatorParameterValueInfoPair; import emu.grasscutter.net.proto.AnimatorParameterValueInfoPairOuterClass.AnimatorParameterValueInfoPair;
import emu.grasscutter.net.proto.EntityAuthorityInfoOuterClass.EntityAuthorityInfo; import emu.grasscutter.net.proto.EntityAuthorityInfoOuterClass.EntityAuthorityInfo;
import emu.grasscutter.net.proto.EntityClientDataOuterClass.EntityClientData; import emu.grasscutter.net.proto.EntityClientDataOuterClass.EntityClientData;
import emu.grasscutter.net.proto.EntityRendererChangedInfoOuterClass.EntityRendererChangedInfo; import emu.grasscutter.net.proto.EntityRendererChangedInfoOuterClass.EntityRendererChangedInfo;
import emu.grasscutter.net.proto.FightPropPairOuterClass.FightPropPair;
import emu.grasscutter.net.proto.GadgetInteractReqOuterClass.GadgetInteractReq; import emu.grasscutter.net.proto.GadgetInteractReqOuterClass.GadgetInteractReq;
import emu.grasscutter.net.proto.MonsterBornTypeOuterClass.MonsterBornType; import emu.grasscutter.net.proto.MonsterBornTypeOuterClass.MonsterBornType;
import emu.grasscutter.net.proto.PropPairOuterClass.PropPair; import emu.grasscutter.net.proto.PropPairOuterClass.PropPair;
@ -35,31 +31,32 @@ import emu.grasscutter.scripts.constants.EventType;
import emu.grasscutter.scripts.data.ScriptArgs; import emu.grasscutter.scripts.data.ScriptArgs;
import emu.grasscutter.utils.Position; import emu.grasscutter.utils.Position;
import emu.grasscutter.utils.ProtoHelper; import emu.grasscutter.utils.ProtoHelper;
import it.unimi.dsi.fastutil.ints.Int2FloatMap;
import it.unimi.dsi.fastutil.ints.Int2FloatOpenHashMap; import it.unimi.dsi.fastutil.ints.Int2FloatOpenHashMap;
import lombok.Getter; import lombok.Getter;
import lombok.Setter; import lombok.Setter;
public class EntityMonster extends GameEntity { public class EntityMonster extends GameEntity {
private final MonsterData monsterData; @Getter private final MonsterData monsterData;
private final Int2FloatOpenHashMap fightProp; @Getter(onMethod = @__(@Override))
private final Int2FloatOpenHashMap fightProperties;
private final Position pos; @Getter(onMethod = @__(@Override))
private final Position rot; private final Position position;
private final Position bornPos; @Getter(onMethod = @__(@Override))
private final int level; private final Position rotation;
@Getter private final Position bornPos;
@Getter private final int level;
private int weaponEntityId; private int weaponEntityId;
private int poseId; @Getter @Setter private int poseId;
@Getter @Setter @Getter @Setter private int aiId = -1;
private int aiId=-1;
public EntityMonster(Scene scene, MonsterData monsterData, Position pos, int level) { public EntityMonster(Scene scene, MonsterData monsterData, Position pos, int level) {
super(scene); super(scene);
this.id = getWorld().getNextEntityId(EntityIdType.MONSTER); this.id = getWorld().getNextEntityId(EntityIdType.MONSTER);
this.monsterData = monsterData; this.monsterData = monsterData;
this.fightProp = new Int2FloatOpenHashMap(); this.fightProperties = new Int2FloatOpenHashMap();
this.pos = new Position(pos); this.position = new Position(pos);
this.rot = new Position(); this.rotation = new Position();
this.bornPos = getPosition().clone(); this.bornPos = getPosition().clone();
this.level = level; this.level = level;
@ -71,59 +68,19 @@ public class EntityMonster extends GameEntity {
this.recalcStats(); this.recalcStats();
} }
@Override
public int getId() {
return this.id;
}
public MonsterData getMonsterData() {
return monsterData;
}
public int getMonsterWeaponId() { public int getMonsterWeaponId() {
return getMonsterData().getWeaponId(); return this.getMonsterData().getWeaponId();
} }
private int getMonsterId() { private int getMonsterId() {
return this.getMonsterData().getId(); return this.getMonsterData().getId();
} }
public int getLevel() {
return level;
}
@Override
public Position getPosition() {
return this.pos;
}
@Override
public Position getRotation() {
return this.rot;
}
public Position getBornPos() {
return bornPos;
}
@Override
public Int2FloatOpenHashMap getFightProperties() {
return fightProp;
}
@Override @Override
public boolean isAlive() { public boolean isAlive() {
return this.getFightProperty(FightProperty.FIGHT_PROP_CUR_HP) > 0f; return this.getFightProperty(FightProperty.FIGHT_PROP_CUR_HP) > 0f;
} }
public int getPoseId() {
return poseId;
}
public void setPoseId(int poseId) {
this.poseId = poseId;
}
@Override @Override
public void onInteract(Player player, GadgetInteractReq interactReq) { public void onInteract(Player player, GadgetInteractReq interactReq) {
EnvAnimalGatherConfigData gatherData = GameData.getEnvAnimalGatherConfigDataMap().get(this.getMonsterData().getId()); EnvAnimalGatherConfigData gatherData = GameData.getEnvAnimalGatherConfigDataMap().get(this.getMonsterData().getId());
@ -163,27 +120,24 @@ public class EntityMonster extends GameEntity {
@Override @Override
public void onDeath(int killerId) { public void onDeath(int killerId) {
super.onDeath(killerId); // Invoke super class's onDeath() method. super.onDeath(killerId); // Invoke super class's onDeath() method.
var scene = this.getScene();
var challenge = Optional.ofNullable(scene.getChallenge());
var scriptManager = scene.getScriptManager();
Optional.ofNullable(this.getSpawnEntry()).ifPresent(scene.getDeadSpawnedEntities()::add);
if (this.getSpawnEntry() != null) {
this.getScene().getDeadSpawnedEntities().add(getSpawnEntry());
}
// first set the challenge data // first set the challenge data
if (getScene().getChallenge() != null) { challenge.ifPresent(c -> c.onMonsterDeath(this));
getScene().getChallenge().onMonsterDeath(this);
} if (scriptManager.isInit() && this.getGroupId() > 0) {
if (getScene().getScriptManager().isInit() && this.getGroupId() > 0) { Optional.ofNullable(scriptManager.getScriptMonsterSpawnService()).ifPresent(s -> s.onMonsterDead(this));
if (getScene().getScriptManager().getScriptMonsterSpawnService() != null) {
getScene().getScriptManager().getScriptMonsterSpawnService().onMonsterDead(this);
}
// prevent spawn monster after success // prevent spawn monster after success
if (getScene().getChallenge() != null && getScene().getChallenge().inProgress()) { if (challenge.map(c -> c.inProgress()).orElse(true))
getScene().getScriptManager().callEvent(EventType.EVENT_ANY_MONSTER_DIE, new ScriptArgs().setParam1(this.getConfigId())); scriptManager.callEvent(EventType.EVENT_ANY_MONSTER_DIE, new ScriptArgs().setParam1(this.getConfigId()));
}else if (getScene().getChallenge() == null) {
getScene().getScriptManager().callEvent(EventType.EVENT_ANY_MONSTER_DIE, new ScriptArgs().setParam1(this.getConfigId()));
}
} }
// Battle Pass trigger // Battle Pass trigger
getScene().getPlayers().forEach(p -> p.getBattlePassManager().triggerMission(WatcherTriggerType.TRIGGER_MONSTER_DIE, this.getMonsterId(), 1)); scene.getPlayers().forEach(p -> p.getBattlePassManager().triggerMission(WatcherTriggerType.TRIGGER_MONSTER_DIE, this.getMonsterId(), 1));
} }
public void recalcStats() { public void recalcStats() {
@ -197,18 +151,7 @@ public class EntityMonster extends GameEntity {
this.getFightProperties().clear(); this.getFightProperties().clear();
// Base stats // Base stats
this.setFightProperty(FightProperty.FIGHT_PROP_BASE_HP, data.getBaseHp()); MonsterData.definedFightProperties.forEach(prop -> this.setFightProperty(prop, data.getFightProperty(prop)));
this.setFightProperty(FightProperty.FIGHT_PROP_BASE_ATTACK, data.getBaseAttack());
this.setFightProperty(FightProperty.FIGHT_PROP_BASE_DEFENSE, data.getBaseDefense());
this.setFightProperty(FightProperty.FIGHT_PROP_PHYSICAL_SUB_HURT, data.getPhysicalSubHurt());
this.setFightProperty(FightProperty.FIGHT_PROP_FIRE_SUB_HURT, .1f);
this.setFightProperty(FightProperty.FIGHT_PROP_ELEC_SUB_HURT, data.getElecSubHurt());
this.setFightProperty(FightProperty.FIGHT_PROP_WATER_SUB_HURT, data.getWaterSubHurt());
this.setFightProperty(FightProperty.FIGHT_PROP_GRASS_SUB_HURT, data.getGrassSubHurt());
this.setFightProperty(FightProperty.FIGHT_PROP_WIND_SUB_HURT, data.getWindSubHurt());
this.setFightProperty(FightProperty.FIGHT_PROP_ROCK_SUB_HURT, .1f);
this.setFightProperty(FightProperty.FIGHT_PROP_ICE_SUB_HURT, data.getIceSubHurt());
// Level curve // Level curve
MonsterCurveData curve = GameData.getMonsterCurveDataMap().get(this.getLevel()); MonsterCurveData curve = GameData.getMonsterCurveDataMap().get(this.getLevel());
@ -220,18 +163,8 @@ public class EntityMonster extends GameEntity {
} }
// Set % stats // Set % stats
this.setFightProperty( FightProperty.forEachCompoundProperty(c -> this.setFightProperty(c.getResult(),
FightProperty.FIGHT_PROP_MAX_HP, this.getFightProperty(c.getFlat()) + (this.getFightProperty(c.getBase()) * (1f + this.getFightProperty(c.getPercent())))));
(getFightProperty(FightProperty.FIGHT_PROP_BASE_HP) * (1f + getFightProperty(FightProperty.FIGHT_PROP_HP_PERCENT))) + getFightProperty(FightProperty.FIGHT_PROP_HP)
);
this.setFightProperty(
FightProperty.FIGHT_PROP_CUR_ATTACK,
(getFightProperty(FightProperty.FIGHT_PROP_BASE_ATTACK) * (1f + getFightProperty(FightProperty.FIGHT_PROP_ATTACK_PERCENT))) + getFightProperty(FightProperty.FIGHT_PROP_ATTACK)
);
this.setFightProperty(
FightProperty.FIGHT_PROP_CUR_DEFENSE,
(getFightProperty(FightProperty.FIGHT_PROP_BASE_DEFENSE) * (1f + getFightProperty(FightProperty.FIGHT_PROP_DEFENSE_PERCENT))) + getFightProperty(FightProperty.FIGHT_PROP_DEFENSE)
);
// Set current hp // Set current hp
this.setFightProperty(FightProperty.FIGHT_PROP_CUR_HP, this.getFightProperty(FightProperty.FIGHT_PROP_MAX_HP) * hpPercent); this.setFightProperty(FightProperty.FIGHT_PROP_CUR_HP, this.getFightProperty(FightProperty.FIGHT_PROP_MAX_HP) * hpPercent);
@ -239,14 +172,14 @@ public class EntityMonster extends GameEntity {
@Override @Override
public SceneEntityInfo toProto() { public SceneEntityInfo toProto() {
EntityAuthorityInfo authority = EntityAuthorityInfo.newBuilder() var authority = EntityAuthorityInfo.newBuilder()
.setAbilityInfo(AbilitySyncStateInfo.newBuilder()) .setAbilityInfo(AbilitySyncStateInfo.newBuilder())
.setRendererChangedInfo(EntityRendererChangedInfo.newBuilder()) .setRendererChangedInfo(EntityRendererChangedInfo.newBuilder())
.setAiInfo(SceneEntityAiInfo.newBuilder().setIsAiOpen(true).setBornPos(this.getBornPos().toProto())) .setAiInfo(SceneEntityAiInfo.newBuilder().setIsAiOpen(true).setBornPos(this.getBornPos().toProto()))
.setBornPos(this.getBornPos().toProto()) .setBornPos(this.getBornPos().toProto())
.build(); .build();
SceneEntityInfo.Builder entityInfo = SceneEntityInfo.newBuilder() var entityInfo = SceneEntityInfo.newBuilder()
.setEntityId(getId()) .setEntityId(getId())
.setEntityType(ProtEntityType.PROT_ENTITY_TYPE_MONSTER) .setEntityType(ProtEntityType.PROT_ENTITY_TYPE_MONSTER)
.setMotionInfo(this.getMotionInfo()) .setMotionInfo(this.getMotionInfo())
@ -257,13 +190,12 @@ public class EntityMonster extends GameEntity {
this.addAllFightPropsToEntityInfo(entityInfo); this.addAllFightPropsToEntityInfo(entityInfo);
PropPair pair = PropPair.newBuilder() entityInfo.addPropList(PropPair.newBuilder()
.setType(PlayerProperty.PROP_LEVEL.getId()) .setType(PlayerProperty.PROP_LEVEL.getId())
.setPropValue(ProtoHelper.newPropValue(PlayerProperty.PROP_LEVEL, getLevel())) .setPropValue(ProtoHelper.newPropValue(PlayerProperty.PROP_LEVEL, getLevel()))
.build(); .build());
entityInfo.addPropList(pair);
SceneMonsterInfo.Builder monsterInfo = SceneMonsterInfo.newBuilder() var monsterInfo = SceneMonsterInfo.newBuilder()
.setMonsterId(getMonsterId()) .setMonsterId(getMonsterId())
.setGroupId(this.getGroupId()) .setGroupId(this.getGroupId())
.setConfigId(this.getConfigId()) .setConfigId(this.getConfigId())

View File

@ -5,14 +5,16 @@ import emu.grasscutter.game.world.Scene;
import emu.grasscutter.net.proto.*; import emu.grasscutter.net.proto.*;
import emu.grasscutter.scripts.data.SceneNPC; import emu.grasscutter.scripts.data.SceneNPC;
import emu.grasscutter.utils.Position; import emu.grasscutter.utils.Position;
import it.unimi.dsi.fastutil.ints.Int2FloatOpenHashMap; import it.unimi.dsi.fastutil.ints.Int2FloatMap;
import lombok.Getter;
public class EntityNPC extends GameEntity{ public class EntityNPC extends GameEntity{
@Getter(onMethod = @__(@Override))
private final Position position; private final Position position;
@Getter(onMethod = @__(@Override))
private final Position rotation; private final Position rotation;
private final SceneNPC metaNpc; private final SceneNPC metaNpc;
private final int suiteId; @Getter private final int suiteId;
public EntityNPC(Scene scene, SceneNPC metaNPC, int blockId, int suiteId) { public EntityNPC(Scene scene, SceneNPC metaNPC, int blockId, int suiteId) {
super(scene); super(scene);
@ -27,24 +29,7 @@ public class EntityNPC extends GameEntity{
} }
@Override @Override public Int2FloatMap getFightProperties() {return null;}
public Int2FloatOpenHashMap getFightProperties() {
return null;
}
@Override
public Position getPosition() {
return position;
}
@Override
public Position getRotation() {
return rotation;
}
public int getSuiteId() {
return suiteId;
}
@Override @Override
public SceneEntityInfoOuterClass.SceneEntityInfo toProto() { public SceneEntityInfoOuterClass.SceneEntityInfo toProto() {

View File

@ -5,7 +5,7 @@ import emu.grasscutter.game.world.Scene;
import emu.grasscutter.net.proto.SceneEntityInfoOuterClass; import emu.grasscutter.net.proto.SceneEntityInfoOuterClass;
import emu.grasscutter.scripts.data.SceneRegion; import emu.grasscutter.scripts.data.SceneRegion;
import emu.grasscutter.utils.Position; import emu.grasscutter.utils.Position;
import it.unimi.dsi.fastutil.ints.Int2FloatOpenHashMap; import it.unimi.dsi.fastutil.ints.Int2FloatMap;
import lombok.Getter; import lombok.Getter;
import java.util.Set; import java.util.Set;
@ -57,20 +57,11 @@ public class EntityRegion extends GameEntity{
} }
public boolean entityLeave() {return this.entityLeave;} public boolean entityLeave() {return this.entityLeave;}
public void resetEntityLeave() {this.entityLeave = false;} public void resetEntityLeave() {this.entityLeave = false;}
@Override @Override public Int2FloatMap getFightProperties() {return null;}
public Int2FloatOpenHashMap getFightProperties() {
return null;
}
@Override @Override public Position getPosition() {return position;}
public Position getPosition() {
return position;
}
@Override @Override public Position getRotation() {return null;}
public Position getRotation() {
return null;
}
@Override @Override
public SceneEntityInfoOuterClass.SceneEntityInfo toProto() { public SceneEntityInfoOuterClass.SceneEntityInfo toProto() {

View File

@ -12,7 +12,6 @@ import emu.grasscutter.net.proto.AbilitySyncStateInfoOuterClass.AbilitySyncState
import emu.grasscutter.net.proto.AnimatorParameterValueInfoPairOuterClass.AnimatorParameterValueInfoPair; import emu.grasscutter.net.proto.AnimatorParameterValueInfoPairOuterClass.AnimatorParameterValueInfoPair;
import emu.grasscutter.net.proto.EntityAuthorityInfoOuterClass.EntityAuthorityInfo; import emu.grasscutter.net.proto.EntityAuthorityInfoOuterClass.EntityAuthorityInfo;
import emu.grasscutter.net.proto.EntityRendererChangedInfoOuterClass.EntityRendererChangedInfo; import emu.grasscutter.net.proto.EntityRendererChangedInfoOuterClass.EntityRendererChangedInfo;
import emu.grasscutter.net.proto.FightPropPairOuterClass.FightPropPair;
import emu.grasscutter.net.proto.MotionInfoOuterClass.MotionInfo; import emu.grasscutter.net.proto.MotionInfoOuterClass.MotionInfo;
import emu.grasscutter.net.proto.PropPairOuterClass.PropPair; import emu.grasscutter.net.proto.PropPairOuterClass.PropPair;
import emu.grasscutter.net.proto.ProtEntityTypeOuterClass.ProtEntityType; import emu.grasscutter.net.proto.ProtEntityTypeOuterClass.ProtEntityType;
@ -36,10 +35,8 @@ import java.util.List;
public class EntityVehicle extends EntityBaseGadget { public class EntityVehicle extends EntityBaseGadget {
@Getter private final Player owner; @Getter private final Player owner;
private final Int2FloatMap fightProp; @Getter(onMethod = @__(@Override))
private final Int2FloatMap fightProperties;
private final Position pos;
private final Position rot;
@Getter private final int pointId; @Getter private final int pointId;
@Getter private final int gadgetId; @Getter private final int gadgetId;
@ -49,12 +46,10 @@ public class EntityVehicle extends EntityBaseGadget {
@Nullable @Getter private ConfigGadget configGadget; @Nullable @Getter private ConfigGadget configGadget;
public EntityVehicle(Scene scene, Player player, int gadgetId, int pointId, Position pos, Position rot) { public EntityVehicle(Scene scene, Player player, int gadgetId, int pointId, Position pos, Position rot) {
super(scene); super(scene, pos, rot);
this.owner = player; this.owner = player;
this.id = getScene().getWorld().getNextEntityId(EntityIdType.GADGET); this.id = getScene().getWorld().getNextEntityId(EntityIdType.GADGET);
this.fightProp = new Int2FloatOpenHashMap(); this.fightProperties = new Int2FloatOpenHashMap();
this.pos = new Position(pos);
this.rot = new Position(rot);
this.gadgetId = gadgetId; this.gadgetId = gadgetId;
this.pointId = pointId; this.pointId = pointId;
this.curStamina = 240; // might be in configGadget.GCALKECLLLP.JBAKBEFIMBN.ANBMPHPOALP this.curStamina = 240; // might be in configGadget.GCALKECLLLP.JBAKBEFIMBN.ANBMPHPOALP
@ -74,19 +69,6 @@ public class EntityVehicle extends EntityBaseGadget {
this.addFightProperty(FightProperty.FIGHT_PROP_CHARGE_EFFICIENCY, 0); this.addFightProperty(FightProperty.FIGHT_PROP_CHARGE_EFFICIENCY, 0);
} }
@Override
public Int2FloatMap getFightProperties() {
return fightProp;
}
@Override
public Position getPosition() { return this.pos; }
@Override
public Position getRotation() {
return this.rot;
}
@Override @Override
public SceneEntityInfo toProto() { public SceneEntityInfo toProto() {

View File

@ -33,7 +33,7 @@ public abstract class GameEntity {
@Getter @Setter private int configId; @Getter @Setter private int configId;
@Getter @Setter private int groupId; @Getter @Setter private int groupId;
private MotionState moveState; @Getter @Setter private MotionState motionState;
@Getter @Setter private int lastMoveSceneTimeMs; @Getter @Setter private int lastMoveSceneTimeMs;
@Getter @Setter private int lastMoveReliableSeq; @Getter @Setter private int lastMoveReliableSeq;
@ -45,7 +45,7 @@ public abstract class GameEntity {
public GameEntity(Scene scene) { public GameEntity(Scene scene) {
this.scene = scene; this.scene = scene;
this.moveState = MotionState.MOTION_STATE_NONE; this.motionState = MotionState.MOTION_STATE_NONE;
} }
public int getEntityType() { public int getEntityType() {
@ -84,14 +84,6 @@ public abstract class GameEntity {
public abstract Position getRotation(); public abstract Position getRotation();
public MotionState getMotionState() {
return moveState;
}
public void setMotionState(MotionState moveState) {
this.moveState = moveState;
}
public void setFightProperty(FightProperty prop, float value) { public void setFightProperty(FightProperty prop, float value) {
this.getFightProperties().put(prop.getId(), value); this.getFightProperties().put(prop.getId(), value);
} }

View File

@ -20,12 +20,12 @@ import javax.annotation.Nullable;
public class EntityPlatform extends EntityBaseGadget { public class EntityPlatform extends EntityBaseGadget {
@Getter @Getter
private final Player owner; private final Player owner;
@Getter(onMethod = @__(@Override))
private final int gadgetId; private final int gadgetId;
@Getter @Getter
private final EntityClientGadget gadget; private final EntityClientGadget gadget;
private final Int2FloatMap fightProp; @Getter(onMethod = @__(@Override))
private final Position pos; private final Int2FloatMap fightProperties;
private final Position rot;
@Nullable @Nullable
@Getter @Getter
private ConfigGadget configGadget; private ConfigGadget configGadget;
@ -39,13 +39,11 @@ public class EntityPlatform extends EntityBaseGadget {
private boolean isActive; private boolean isActive;
public EntityPlatform(EntityClientGadget gadget, Scene scene, Player player, int gadgetId, Position pos, Position rot, MovingPlatformTypeOuterClass.MovingPlatformType movingPlatformType) { public EntityPlatform(EntityClientGadget gadget, Scene scene, Player player, int gadgetId, Position pos, Position rot, MovingPlatformTypeOuterClass.MovingPlatformType movingPlatformType) {
super(scene); super(scene, pos, rot);
this.gadget = gadget; this.gadget = gadget;
this.owner = player; this.owner = player;
this.id = getScene().getWorld().getNextEntityId(EntityIdType.GADGET); this.id = getScene().getWorld().getNextEntityId(EntityIdType.GADGET);
this.fightProp = new Int2FloatOpenHashMap(); this.fightProperties = new Int2FloatOpenHashMap();
this.pos = new Position(pos);
this.rot = new Position(rot);
this.movingPlatformType = movingPlatformType; this.movingPlatformType = movingPlatformType;
this.gadgetId = gadgetId; this.gadgetId = gadgetId;
GadgetData data = GameData.getGadgetDataMap().get(gadgetId); GadgetData data = GameData.getGadgetDataMap().get(gadgetId);
@ -56,26 +54,6 @@ public class EntityPlatform extends EntityBaseGadget {
fillFightProps(configGadget); fillFightProps(configGadget);
} }
@Override
public int getGadgetId() {
return gadgetId;
}
@Override
public Int2FloatMap getFightProperties() {
return fightProp;
}
@Override
public Position getPosition() {
return pos;
}
@Override
public Position getRotation() {
return rot;
}
@Override @Override
public SceneEntityInfoOuterClass.SceneEntityInfo toProto() { public SceneEntityInfoOuterClass.SceneEntityInfo toProto() {
var platform = PlatformInfoOuterClass.PlatformInfo.newBuilder() var platform = PlatformInfoOuterClass.PlatformInfo.newBuilder()

View File

@ -2,6 +2,7 @@ package emu.grasscutter.game.gacha;
import static emu.grasscutter.config.Configuration.*; import static emu.grasscutter.config.Configuration.*;
import emu.grasscutter.Grasscutter;
import emu.grasscutter.data.common.ItemParamData; import emu.grasscutter.data.common.ItemParamData;
import emu.grasscutter.game.player.Player; import emu.grasscutter.game.player.Player;
import emu.grasscutter.net.proto.GachaInfoOuterClass.GachaInfo; import emu.grasscutter.net.proto.GachaInfoOuterClass.GachaInfo;
@ -39,14 +40,41 @@ public class GachaBanner {
private int eventChance4 = 50; // Chance to win a featured event item private int eventChance4 = 50; // Chance to win a featured event item
private int eventChance5 = 50; // Chance to win a featured event item private int eventChance5 = 50; // Chance to win a featured event item
@Getter private BannerType bannerType = BannerType.STANDARD; @Getter private BannerType bannerType = BannerType.STANDARD;
// Kinda wanna deprecate these but they're in people's configs
private int[] rateUpItems1 = {};
private int[] rateUpItems2 = {};
private int eventChance = -1;
private int costItem = 0;
@Getter private int wishMaxProgress = 2; @Getter private int wishMaxProgress = 2;
// Deprecated fields that were tolerated in early May 2022 but have apparently still being circulating in new custom configs
// For now, throw up big scary errors on load telling people that they will be banned outright in a future version
@Deprecated private int[] rateUpItems1 = {};
@Deprecated private int[] rateUpItems2 = {};
@Deprecated private int eventChance = -1;
@Deprecated private int costItem = 0;
@Deprecated private int softPity = -1;
@Deprecated private int hardPity = -1;
@Deprecated private int minItemType = -1;
@Deprecated private int maxItemType = -1;
private static void warnDeprecated(String name, String replacement) {
Grasscutter.getLogger().error("Deprecated field found in Banners.json: "+name+" was replaced back in early May 2022, use "+replacement+" instead. If you do not remove this key from your config, it will refuse to load in a future Grasscutter version.");
}
public void onLoad() {
if (eventChance != -1)
warnDeprecated("eventChance", "eventChance4 & eventChance5");
if (costItem != 0)
warnDeprecated("costItem", "costItemId");
if (softPity != -1)
warnDeprecated("softPity", "weights5");
if (hardPity != -1)
warnDeprecated("hardPity", "weights5");
if (minItemType != -1)
warnDeprecated("minItemType", "fallbackItems[4,5]Pool[1,2]");
if (maxItemType != -1)
warnDeprecated("maxItemType", "fallbackItems[4,5]Pool[1,2]");
if (rateUpItems1.length > 0)
warnDeprecated("rateUpItems1", "rateUpItems5");
if (rateUpItems2.length > 0)
warnDeprecated("rateUpItems2", "rateUpItems4");
}
public String getPreviewPrefabPath() { public String getPreviewPrefabPath() {
if (this.previewPrefabPath != null && !this.previewPrefabPath.isEmpty()) if (this.previewPrefabPath != null && !this.previewPrefabPath.isEmpty())
return this.previewPrefabPath; return this.previewPrefabPath;

View File

@ -73,6 +73,7 @@ public class GachaSystem extends BaseGameSystem {
List<GachaBanner> banners = DataLoader.loadList("Banners.json", GachaBanner.class); List<GachaBanner> banners = DataLoader.loadList("Banners.json", GachaBanner.class);
if (banners.size() > 0) { if (banners.size() > 0) {
for (GachaBanner banner : banners) { for (GachaBanner banner : banners) {
banner.onLoad();
getGachaBanners().put(banner.getScheduleId(), banner); getGachaBanners().put(banner.getScheduleId(), banner);
} }
Grasscutter.getLogger().debug("Banners successfully loaded."); Grasscutter.getLogger().debug("Banners successfully loaded.");

View File

@ -181,6 +181,7 @@ public class Inventory extends BasePlayerManager implements Iterable<GameItem> {
if (data.isUseOnGain()) { if (data.isUseOnGain()) {
var params = new UseItemParams(this.player, data.getUseTarget()); var params = new UseItemParams(this.player, data.getUseTarget());
params.usedItemId = data.getId();
this.player.getServer().getInventorySystem().useItemDirect(data, params); this.player.getServer().getInventorySystem().useItemDirect(data, params);
return null; return null;
} }

View File

@ -1,6 +1,6 @@
package emu.grasscutter.game.player; package emu.grasscutter.game.player;
import java.util.LinkedList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.Optional; import java.util.Optional;
@ -15,11 +15,8 @@ import emu.grasscutter.server.packet.send.PacketServerBuffChangeNotify;
import it.unimi.dsi.fastutil.ints.Int2ObjectMap; import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap; import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
import lombok.AccessLevel;
import lombok.Getter; import lombok.Getter;
import lombok.val;
@Getter(AccessLevel.PRIVATE)
public class PlayerBuffManager extends BasePlayerManager { public class PlayerBuffManager extends BasePlayerManager {
private int nextBuffUid; private int nextBuffUid;
@ -29,7 +26,7 @@ public class PlayerBuffManager extends BasePlayerManager {
public PlayerBuffManager(Player player) { public PlayerBuffManager(Player player) {
super(player); super(player);
this.buffs = new Int2ObjectOpenHashMap<>(); this.buffs = new Int2ObjectOpenHashMap<>();
this.pendingBuffs = new LinkedList<>(); this.pendingBuffs = new ArrayList<>();
} }
/** /**
@ -46,7 +43,7 @@ public class PlayerBuffManager extends BasePlayerManager {
* @return True if a buff with this group id exists * @return True if a buff with this group id exists
*/ */
public synchronized boolean hasBuff(int groupId) { public synchronized boolean hasBuff(int groupId) {
return this.getBuffs().containsKey(groupId); return this.buffs.containsKey(groupId);
} }
/** /**
@ -55,11 +52,11 @@ public class PlayerBuffManager extends BasePlayerManager {
public synchronized void clearBuffs() { public synchronized void clearBuffs() {
// Remove from player // Remove from player
getPlayer().sendPacket( getPlayer().sendPacket(
new PacketServerBuffChangeNotify(getPlayer(), ServerBuffChangeType.SERVER_BUFF_CHANGE_TYPE_DEL_SERVER_BUFF, getBuffs().values()) new PacketServerBuffChangeNotify(getPlayer(), ServerBuffChangeType.SERVER_BUFF_CHANGE_TYPE_DEL_SERVER_BUFF, this.buffs.values())
); );
// Clear // Clear
getBuffs().clear(); this.buffs.clear();
} }
/** /**
@ -133,13 +130,11 @@ public class PlayerBuffManager extends BasePlayerManager {
} }
// Clear previous buff if it exists // Clear previous buff if it exists
if (this.hasBuff(buffData.getGroupId())) {
this.removeBuff(buffData.getGroupId()); this.removeBuff(buffData.getGroupId());
}
// Create and store buff // Create and store buff
PlayerBuff buff = new PlayerBuff(getNextBuffUid(), buffData, duration); PlayerBuff buff = new PlayerBuff(getNextBuffUid(), buffData, duration);
getBuffs().put(buff.getGroupId(), buff); this.buffs.put(buff.getGroupId(), buff);
// Packet // Packet
getPlayer().sendPacket(new PacketServerBuffChangeNotify(getPlayer(), ServerBuffChangeType.SERVER_BUFF_CHANGE_TYPE_ADD_SERVER_BUFF, buff)); getPlayer().sendPacket(new PacketServerBuffChangeNotify(getPlayer(), ServerBuffChangeType.SERVER_BUFF_CHANGE_TYPE_ADD_SERVER_BUFF, buff));
@ -153,7 +148,7 @@ public class PlayerBuffManager extends BasePlayerManager {
* @return True if a buff was remove * @return True if a buff was remove
*/ */
public synchronized boolean removeBuff(int buffGroupId) { public synchronized boolean removeBuff(int buffGroupId) {
PlayerBuff buff = this.getBuffs().get(buffGroupId); PlayerBuff buff = this.buffs.remove(buffGroupId);
if (buff != null) { if (buff != null) {
getPlayer().sendPacket( getPlayer().sendPacket(
@ -167,28 +162,24 @@ public class PlayerBuffManager extends BasePlayerManager {
public synchronized void onTick() { public synchronized void onTick() {
// Skip if no buffs // Skip if no buffs
if (getBuffs().size() == 0) return; if (this.buffs.isEmpty()) return;
long currentTime = System.currentTimeMillis(); long currentTime = System.currentTimeMillis();
// Add to pending buffs to remove if buff has expired // Add to pending buffs to remove if buff has expired
for (PlayerBuff buff : getBuffs().values()) { this.buffs.values().removeIf(buff -> {
if (currentTime > buff.getEndTime()) { if (currentTime <= buff.getEndTime())
this.getPendingBuffs().add(buff); return false;
} this.pendingBuffs.add(buff);
} return true;
});
if (this.getPendingBuffs().size() > 0) { if (this.pendingBuffs.size() > 0) {
// Send packet // Send packet
getPlayer().sendPacket( getPlayer().sendPacket(
new PacketServerBuffChangeNotify(getPlayer(), ServerBuffChangeType.SERVER_BUFF_CHANGE_TYPE_DEL_SERVER_BUFF, this.pendingBuffs) new PacketServerBuffChangeNotify(getPlayer(), ServerBuffChangeType.SERVER_BUFF_CHANGE_TYPE_DEL_SERVER_BUFF, this.pendingBuffs)
); );
this.pendingBuffs.clear();
// Remove buff from player buff map
for (PlayerBuff buff : this.getPendingBuffs()) {
getBuffs().remove(buff.getGroupId());
}
this.getPendingBuffs().clear();
} }
} }

View File

@ -4,6 +4,7 @@ import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Set; import java.util.Set;
import java.util.function.Consumer;
import static java.util.Map.entry; import static java.util.Map.entry;
@ -12,6 +13,7 @@ import java.util.stream.Stream;
import it.unimi.dsi.fastutil.ints.Int2ObjectMap; import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap; import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
import lombok.Getter;
public enum FightProperty { public enum FightProperty {
FIGHT_PROP_NONE(0), FIGHT_PROP_NONE(0),
@ -197,6 +199,35 @@ public enum FightProperty {
FIGHT_PROP_CUR_WATER_ENERGY, FIGHT_PROP_CUR_GRASS_ENERGY, FIGHT_PROP_CUR_WIND_ENERGY, FIGHT_PROP_CUR_ICE_ENERGY, FIGHT_PROP_CUR_WATER_ENERGY, FIGHT_PROP_CUR_GRASS_ENERGY, FIGHT_PROP_CUR_WIND_ENERGY, FIGHT_PROP_CUR_ICE_ENERGY,
FIGHT_PROP_CUR_ROCK_ENERGY, FIGHT_PROP_CUR_HP, FIGHT_PROP_MAX_HP, FIGHT_PROP_CUR_ATTACK, FIGHT_PROP_CUR_DEFENSE); FIGHT_PROP_CUR_ROCK_ENERGY, FIGHT_PROP_CUR_HP, FIGHT_PROP_MAX_HP, FIGHT_PROP_CUR_ATTACK, FIGHT_PROP_CUR_DEFENSE);
@Getter
public static class CompoundProperty {
private FightProperty result;
private FightProperty base;
private FightProperty percent;
private FightProperty flat;
public CompoundProperty(FightProperty result, FightProperty base, FightProperty percent, FightProperty flat) {
this.result = result;
this.base = base;
this.percent = percent;
this.flat = flat;
}
}
private static Map<FightProperty, CompoundProperty> compoundProperties = Map.ofEntries(
entry(FIGHT_PROP_MAX_HP, new CompoundProperty(FIGHT_PROP_MAX_HP, FIGHT_PROP_BASE_HP, FIGHT_PROP_HP_PERCENT, FIGHT_PROP_HP)),
entry(FIGHT_PROP_CUR_ATTACK, new CompoundProperty(FIGHT_PROP_CUR_ATTACK, FIGHT_PROP_BASE_ATTACK, FIGHT_PROP_ATTACK_PERCENT, FIGHT_PROP_ATTACK)),
entry(FIGHT_PROP_CUR_DEFENSE, new CompoundProperty(FIGHT_PROP_CUR_DEFENSE, FIGHT_PROP_BASE_DEFENSE, FIGHT_PROP_DEFENSE_PERCENT, FIGHT_PROP_DEFENSE))
);
public static CompoundProperty getCompoundProperty(FightProperty result) {
return compoundProperties.get(result);
}
public static void forEachCompoundProperty(Consumer<CompoundProperty> consumer) {
compoundProperties.values().forEach(consumer);
}
public static boolean isPercentage(FightProperty prop) { public static boolean isPercentage(FightProperty prop) {
return !flatProps.contains(prop); return !flatProps.contains(prop);
} }

View File

@ -24,7 +24,7 @@ public class ItemUseAction {
case ITEM_USE_GAIN_AVATAR -> new ItemUseGainAvatar(useParam); case ITEM_USE_GAIN_AVATAR -> new ItemUseGainAvatar(useParam);
case ITEM_USE_GAIN_COSTUME -> new ItemUseGainCostume(useParam); // TODO - real success/fail case ITEM_USE_GAIN_COSTUME -> new ItemUseGainCostume(useParam); // TODO - real success/fail
case ITEM_USE_GAIN_FLYCLOAK -> new ItemUseGainFlycloak(useParam); // TODO - real success/fail case ITEM_USE_GAIN_FLYCLOAK -> new ItemUseGainFlycloak(useParam); // TODO - real success/fail
case ITEM_USE_GAIN_NAME_CARD -> new ItemUseGainNameCard(useParam); // TODO case ITEM_USE_GAIN_NAME_CARD -> new ItemUseGainNameCard(useParam);
case ITEM_USE_CHEST_SELECT_ITEM -> new ItemUseChestSelectItem(useParam); case ITEM_USE_CHEST_SELECT_ITEM -> new ItemUseChestSelectItem(useParam);
case ITEM_USE_ADD_SELECT_ITEM -> new ItemUseAddSelectItem(useParam); case ITEM_USE_ADD_SELECT_ITEM -> new ItemUseAddSelectItem(useParam);
case ITEM_USE_GRANT_SELECT_REWARD -> new ItemUseGrantSelectReward(useParam); case ITEM_USE_GRANT_SELECT_REWARD -> new ItemUseGrantSelectReward(useParam);

View File

@ -13,6 +13,7 @@ public class ItemUseGainNameCard extends ItemUseAction {
@Override @Override
public boolean useItem(UseItemParams params) { public boolean useItem(UseItemParams params) {
return false; // TODO: work out if this is actually used and how to get the namecard id params.player.addNameCard(params.usedItemId);
return true;
} }
} }

View File

@ -12,6 +12,7 @@ public class UseItemParams {
public int count = 1; public int count = 1;
public int optionId = 0; public int optionId = 0;
public boolean isEnterMpDungeonTeam = false; public boolean isEnterMpDungeonTeam = false;
public int usedItemId = 0;
public UseItemParams(Player player, ItemUseTarget itemUseTarget, Avatar targetAvatar, int count, int optionId, boolean isEnterMpDungeonTeam) { public UseItemParams(Player player, ItemUseTarget itemUseTarget, Avatar targetAvatar, int count, int optionId, boolean isEnterMpDungeonTeam) {
this.player = player; this.player = player;

View File

@ -54,7 +54,7 @@ public class QuestSystem extends BaseGameSystem {
return; return;
} }
map.put(opcode.value().getValue(), handlerClass.newInstance()); map.put(opcode.value().getValue(), handlerClass.getDeclaredConstructor().newInstance());
} catch (Exception e) { } catch (Exception e) {
e.printStackTrace(); e.printStackTrace();
} }

View File

@ -743,6 +743,7 @@ public class InventorySystem extends BaseGameSystem {
if (itemData == null) return null; if (itemData == null) return null;
var params = new UseItemParams(player, itemData.getUseTarget(), target, count, optionId, isEnterMpDungeonTeam); var params = new UseItemParams(player, itemData.getUseTarget(), target, count, optionId, isEnterMpDungeonTeam);
params.usedItemId = item.getItemId();
if (useItemDirect(itemData, params)) { if (useItemDirect(itemData, params)) {
player.getInventory().removeItem(item, count); player.getInventory().removeItem(item, count);
var actions = itemData.getItemUseActions(); var actions = itemData.getItemUseActions();

View File

@ -274,15 +274,8 @@ public class Scene {
} }
public void showOtherEntities(Player player) { public void showOtherEntities(Player player) {
List<GameEntity> entities = new LinkedList<>();
GameEntity currentEntity = player.getTeamManager().getCurrentAvatarEntity(); GameEntity currentEntity = player.getTeamManager().getCurrentAvatarEntity();
List<GameEntity> entities = this.getEntities().values().stream().filter(entity -> entity != currentEntity).toList();
for (GameEntity entity : this.getEntities().values()) {
if (entity == currentEntity) {
continue;
}
entities.add(entity);
}
player.sendPacket(new PacketSceneEntityAppearNotify(entities, VisionType.VISION_TYPE_MEET)); player.sendPacket(new PacketSceneEntityAppearNotify(entities, VisionType.VISION_TYPE_MEET));
} }
@ -422,8 +415,8 @@ public class Scene {
} }
// Todo // Todo
List<GameEntity> toAdd = new LinkedList<>(); List<GameEntity> toAdd = new ArrayList<>();
List<GameEntity> toRemove = new LinkedList<>(); List<GameEntity> toRemove = new ArrayList<>();
var spawnedEntities = this.getSpawnedEntities(); var spawnedEntities = this.getSpawnedEntities();
for (SpawnDataEntry entry : visible) { for (SpawnDataEntry entry : visible) {
// If spawn entry is in our view and hasnt been spawned/killed yet, we should spawn it // If spawn entry is in our view and hasnt been spawned/killed yet, we should spawn it

View File

@ -27,7 +27,8 @@ public class PacketOpcodesUtils {
PacketOpcodes.PingRsp, PacketOpcodes.PingRsp,
PacketOpcodes.WorldPlayerRTTNotify, PacketOpcodes.WorldPlayerRTTNotify,
PacketOpcodes.UnionCmdNotify, PacketOpcodes.UnionCmdNotify,
PacketOpcodes.QueryPathReq PacketOpcodes.QueryPathReq,
PacketOpcodes.QueryPathRsp
); );
static { static {

View File

@ -181,7 +181,7 @@ public final class PluginManager {
// Add the plugin to the list of loaded plugins. // Add the plugin to the list of loaded plugins.
this.plugins.put(identifier.name, plugin); this.plugins.put(identifier.name, plugin);
// Create a collection for the plugin's listeners. // Create a collection for the plugin's listeners.
this.listeners.put(plugin, new LinkedList<>()); this.listeners.put(plugin, new ArrayList<>());
// Call the plugin's onLoad method. // Call the plugin's onLoad method.
try { try {
@ -242,18 +242,14 @@ public final class PluginManager {
* @param priority The priority to call for. * @param priority The priority to call for.
*/ */
private void checkAndFilter(Event event, HandlerPriority priority) { private void checkAndFilter(Event event, HandlerPriority priority) {
// Create a collection of listeners.
List<EventHandler<? extends Event>> listeners = new LinkedList<>();
// Add all listeners from every plugin. // Add all listeners from every plugin.
this.listeners.values().forEach(listeners::addAll); this.listeners.values().stream()
.flatMap(Collection::stream)
listeners.stream()
// Filter the listeners by priority. // Filter the listeners by priority.
.filter(handler -> handler.handles().isInstance(event)) .filter(handler -> handler.handles().isInstance(event))
.filter(handler -> handler.getPriority() == priority) .filter(handler -> handler.getPriority() == priority)
// Invoke the event. // Invoke the event.
.toList().forEach(handler -> this.invokeHandler(event, handler)); .forEach(handler -> this.invokeHandler(event, handler));
} }
/** /**

View File

@ -11,8 +11,9 @@ import emu.grasscutter.server.game.GameServer;
import emu.grasscutter.server.http.HttpServer; import emu.grasscutter.server.http.HttpServer;
import emu.grasscutter.server.http.Router; import emu.grasscutter.server.http.Router;
import java.util.LinkedList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.stream.Stream;
/** /**
* Hooks into the {@link GameServer} class, adding convenient ways to do certain things. * Hooks into the {@link GameServer} class, adding convenient ways to do certain things.
@ -60,8 +61,17 @@ public final class ServerHook {
* Gets all online players. * Gets all online players.
* @return Players connected to the server. * @return Players connected to the server.
*/ */
@Deprecated(forRemoval = true)
public List<Player> getOnlinePlayers() { public List<Player> getOnlinePlayers() {
return new LinkedList<>(this.gameServer.getPlayers().values()); return new ArrayList<>(this.gameServer.getPlayers().values());
}
/**
* Gets all online players.
* @return Players connected to the server.
*/
public Stream<Player> getOnlinePlayersStream() {
return this.gameServer.getPlayers().values().stream();
} }
/** /**

View File

@ -99,7 +99,7 @@ public class GameServerPacketHandler {
} }
// Log unhandled packets // Log unhandled packets
if (GAME_INFO.logPackets == ServerDebugMode.MISSING) { if (GAME_INFO.logPackets == ServerDebugMode.MISSING || GAME_INFO.logPackets == ServerDebugMode.ALL) {
Grasscutter.getLogger().info("Unhandled packet (" + opcode + "): " + emu.grasscutter.net.packet.PacketOpcodesUtils.getOpcodeName(opcode)); Grasscutter.getLogger().info("Unhandled packet (" + opcode + "): " + emu.grasscutter.net.packet.PacketOpcodesUtils.getOpcodeName(opcode));
} }
} }

View File

@ -98,6 +98,7 @@ public class GameSession implements GameSessionManager.KcpChannel {
public void logPacket(String sendOrRecv, int opcode, byte[] payload) { public void logPacket(String sendOrRecv, int opcode, byte[] payload) {
Grasscutter.getLogger().info(sendOrRecv + ": " + PacketOpcodesUtils.getOpcodeName(opcode) + " (" + opcode + ")"); Grasscutter.getLogger().info(sendOrRecv + ": " + PacketOpcodesUtils.getOpcodeName(opcode) + " (" + opcode + ")");
if (GAME_INFO.isShowPacketPayload)
System.out.println(Utils.bytesToHex(payload)); System.out.println(Utils.bytesToHex(payload));
} }
@ -122,7 +123,7 @@ public class GameSession implements GameSessionManager.KcpChannel {
// Log // Log
switch (GAME_INFO.logPackets) { switch (GAME_INFO.logPackets) {
case ALL -> { case ALL -> {
if (!PacketOpcodesUtils.LOOP_PACKETS.contains(packet.getOpcode())) { if (!PacketOpcodesUtils.LOOP_PACKETS.contains(packet.getOpcode()) || GAME_INFO.isShowLoopPackets) {
logPacket("SEND", packet.getOpcode(), packet.getData()); logPacket("SEND", packet.getOpcode(), packet.getData());
} }
} }
@ -154,7 +155,6 @@ public class GameSession implements GameSessionManager.KcpChannel {
Grasscutter.getLogger().info(translate("messages.game.connect", this.getAddress().toString())); Grasscutter.getLogger().info(translate("messages.game.connect", this.getAddress().toString()));
} }
@Override @Override
public void handleReceive(byte[] bytes) { public void handleReceive(byte[] bytes) {
// Decrypt and turn back into a packet // Decrypt and turn back into a packet
@ -200,7 +200,7 @@ public class GameSession implements GameSessionManager.KcpChannel {
// Log packet // Log packet
switch (GAME_INFO.logPackets) { switch (GAME_INFO.logPackets) {
case ALL -> { case ALL -> {
if (!PacketOpcodesUtils.LOOP_PACKETS.contains(opcode)) { if (!PacketOpcodesUtils.LOOP_PACKETS.contains(opcode) || GAME_INFO.isShowLoopPackets) {
logPacket("RECV", opcode, payload); logPacket("RECV", opcode, payload);
} }
} }

View File

@ -14,6 +14,7 @@ import static emu.grasscutter.config.Configuration.*;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import java.util.Objects; import java.util.Objects;
import java.util.StringJoiner;
/** /**
* Handles requests related to the announcements page. * Handles requests related to the announcements page.
@ -72,7 +73,17 @@ public final class AnnouncementsHandler implements Router {
} }
private static void getPageResources(Context ctx) { private static void getPageResources(Context ctx) {
try (InputStream filestream = DataLoader.load(ctx.path())) { // Re-process the path - remove the first slash and prevent directory traversal
// (the first slash will act as root path when resolving local path)
String[] path = ctx.path().split("/");
StringJoiner stringJoiner = new StringJoiner("/");
for (String pathName : path) {
// Filter the illegal payload to prevent directory traversal
if (!pathName.isEmpty() && !pathName.equals("..") && !pathName.contains("\\")) {
stringJoiner.add(pathName);
}
}
try (InputStream filestream = DataLoader.load(stringJoiner.toString())) {
String possibleFilename = ctx.path(); String possibleFilename = ctx.path();
ContentType fromExtension = ContentType.getContentTypeByExtension(possibleFilename.substring(possibleFilename.lastIndexOf(".") + 1)); ContentType fromExtension = ContentType.getContentTypeByExtension(possibleFilename.substring(possibleFilename.lastIndexOf(".") + 1));

View File

@ -15,7 +15,6 @@ import io.javalin.http.Context;
import io.javalin.http.staticfiles.Location; import io.javalin.http.staticfiles.Location;
import lombok.Getter; import lombok.Getter;
import java.io.File;
import java.io.IOException; import java.io.IOException;
import java.nio.charset.StandardCharsets; import java.nio.charset.StandardCharsets;
import java.nio.file.Files; import java.nio.file.Files;
@ -42,13 +41,6 @@ public final class GachaHandler implements Router {
} }
private static void gachaRecords(Context ctx) { private static void gachaRecords(Context ctx) {
File recordsTemplate = FileUtils.getDataPath("gacha/records.html").toFile();
if (!recordsTemplate.exists()) {
Grasscutter.getLogger().warn("File does not exist: " + recordsTemplate);
ctx.status(500);
return;
}
String sessionKey = ctx.queryParam("s"); String sessionKey = ctx.queryParam("s");
Account account = DatabaseHelper.getAccountBySessionKey(sessionKey); Account account = DatabaseHelper.getAccountBySessionKey(sessionKey);
if (account == null) { if (account == null) {
@ -70,7 +62,7 @@ public final class GachaHandler implements Router {
String records = DatabaseHelper.getGachaRecords(player.getUid(), page, gachaType).toString(); String records = DatabaseHelper.getGachaRecords(player.getUid(), page, gachaType).toString();
long maxPage = DatabaseHelper.getGachaRecordsMaxPage(player.getUid(), page, gachaType); long maxPage = DatabaseHelper.getGachaRecordsMaxPage(player.getUid(), page, gachaType);
String template = new String(FileUtils.read(recordsTemplate), StandardCharsets.UTF_8) String template = new String(FileUtils.read(FileUtils.getDataPath("gacha/records.html")), StandardCharsets.UTF_8)
.replace("{{REPLACE_RECORDS}}", records) .replace("{{REPLACE_RECORDS}}", records)
.replace("{{REPLACE_MAXPAGE}}", String.valueOf(maxPage)) .replace("{{REPLACE_MAXPAGE}}", String.valueOf(maxPage))
.replace("{{TITLE}}", translate(player, "gacha.records.title")) .replace("{{TITLE}}", translate(player, "gacha.records.title"))

View File

@ -1,8 +1,8 @@
package emu.grasscutter.server.packet.send; package emu.grasscutter.server.packet.send;
import java.util.Collection; import java.util.Collection;
import java.util.stream.Stream;
import emu.grasscutter.game.entity.EntityAvatar;
import emu.grasscutter.game.player.Player; import emu.grasscutter.game.player.Player;
import emu.grasscutter.game.player.PlayerBuffManager.PlayerBuff; import emu.grasscutter.game.player.PlayerBuffManager.PlayerBuff;
import emu.grasscutter.net.packet.BasePacket; import emu.grasscutter.net.packet.BasePacket;
@ -13,31 +13,25 @@ import emu.grasscutter.net.proto.ServerBuffChangeNotifyOuterClass.ServerBuffChan
public class PacketServerBuffChangeNotify extends BasePacket { public class PacketServerBuffChangeNotify extends BasePacket {
public PacketServerBuffChangeNotify(Player player, ServerBuffChangeType changeType, PlayerBuff buff) { public PacketServerBuffChangeNotify(Player player, ServerBuffChangeType changeType, PlayerBuff buff) {
super(PacketOpcodes.ServerBuffChangeNotify); this(player, changeType, Stream.of(buff));
var proto = ServerBuffChangeNotify.newBuilder();
for (EntityAvatar entity : player.getTeamManager().getActiveTeam()) {
proto.addAvatarGuidList(entity.getAvatar().getGuid());
}
proto.setServerBuffChangeType(changeType);
proto.addServerBuffList(buff.toProto());
this.setData(proto);
} }
public PacketServerBuffChangeNotify(Player player, ServerBuffChangeType changeType, Collection<PlayerBuff> buffs) { public PacketServerBuffChangeNotify(Player player, ServerBuffChangeType changeType, Collection<PlayerBuff> buffs) {
this(player, changeType, buffs.stream());
}
public PacketServerBuffChangeNotify(Player player, ServerBuffChangeType changeType, Stream<PlayerBuff> buffs) {
super(PacketOpcodes.ServerBuffChangeNotify); super(PacketOpcodes.ServerBuffChangeNotify);
var proto = ServerBuffChangeNotify.newBuilder(); var proto = ServerBuffChangeNotify.newBuilder();
for (EntityAvatar entity : player.getTeamManager().getActiveTeam()) { player.getTeamManager().getActiveTeam().stream()
proto.addAvatarGuidList(entity.getAvatar().getGuid()); .mapToLong(entity -> entity.getAvatar().getGuid())
} .forEach(proto::addAvatarGuidList);
proto.setServerBuffChangeType(changeType); proto.setServerBuffChangeType(changeType);
proto.addAllServerBuffList(buffs.stream().map(PlayerBuff::toProto).toList()); buffs.map(PlayerBuff::toProto)
.forEach(proto::addServerBuffList);
this.setData(proto); this.setData(proto);
} }

View File

@ -133,7 +133,7 @@ public final class TaskMap {
} }
public List<TaskHandler> getHandlersAsList() { public List<TaskHandler> getHandlersAsList() {
return new LinkedList<>(this.tasks.values()); return new ArrayList<>(this.tasks.values());
} }
public HashMap<String, TaskHandler> getHandlers() { public HashMap<String, TaskHandler> getHandlers() {

View File

@ -232,11 +232,15 @@ public final class Tools {
public static void createGachaMappings(Path location) throws IOException { public static void createGachaMappings(Path location) throws IOException {
ResourceLoader.loadResources(); ResourceLoader.loadResources();
List<String> jsons = createGachaMappingJsons(); List<String> jsons = createGachaMappingJsons();
var usedLocales = new HashSet<String>();
StringBuilder sb = new StringBuilder("mappings = {\n"); StringBuilder sb = new StringBuilder("mappings = {\n");
for (int i = 0; i < Language.TextStrings.NUM_LANGUAGES; i++) { for (int i = 0; i < Language.TextStrings.NUM_LANGUAGES; i++) {
sb.append("\t\"%s\": ".formatted(Language.TextStrings.ARR_GC_LANGUAGES[i].toLowerCase())); // TODO: change the templates to not use lowercased locale codes String locale = Language.TextStrings.ARR_GC_LANGUAGES[i].toLowerCase(); // TODO: change the templates to not use lowercased locale codes
if (usedLocales.add(locale)) { // Some locales fallback to en-us, we don't want to redefine en-us with vietnamese strings
sb.append("\t\"%s\": ".formatted(locale));
sb.append(jsons.get(i).replace("\n", "\n\t") + ",\n"); sb.append(jsons.get(i).replace("\n", "\n\t") + ",\n");
} }
}
sb.setLength(sb.length() - 2); // Delete trailing ",\n" sb.setLength(sb.length() - 2); // Delete trailing ",\n"
sb.append("\n}"); sb.append("\n}");

View File

@ -1,5 +1,7 @@
package emu.grasscutter.utils; package emu.grasscutter.utils;
import java.io.File;
import java.nio.file.Path;
import java.security.KeyFactory; import java.security.KeyFactory;
import java.security.PrivateKey; import java.security.PrivateKey;
import java.security.PublicKey; import java.security.PublicKey;
@ -8,6 +10,7 @@ import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec; import java.security.spec.X509EncodedKeySpec;
import java.util.Map; import java.util.Map;
import java.util.HashMap; import java.util.HashMap;
import java.util.regex.Pattern;
import emu.grasscutter.Grasscutter; import emu.grasscutter.Grasscutter;
@ -36,14 +39,21 @@ public final class Crypto {
CUR_SIGNING_KEY = KeyFactory.getInstance("RSA") CUR_SIGNING_KEY = KeyFactory.getInstance("RSA")
.generatePrivate(new PKCS8EncodedKeySpec(FileUtils.readResource("/keys/SigningKey.der"))); .generatePrivate(new PKCS8EncodedKeySpec(FileUtils.readResource("/keys/SigningKey.der")));
var CNRelSign = KeyFactory.getInstance("RSA") Pattern pattern = Pattern.compile("([0-9]*)_Pub\\.der");
.generatePublic(new X509EncodedKeySpec(FileUtils.readResource("/keys/CNRel_Pub.der"))); for (Path path : FileUtils.getPathsFromResource("/keys/game_keys")) {
if (path.toString().endsWith("_Pub.der")) {
var OSRelSign = KeyFactory.getInstance("RSA") var m = pattern.matcher(path.getFileName().toString());
.generatePublic(new X509EncodedKeySpec(FileUtils.readResource("/keys/OSRel_Pub.der")));
EncryptionKeys.put(2, CNRelSign); if (m.matches())
EncryptionKeys.put(3, OSRelSign); {
var key = KeyFactory.getInstance("RSA")
.generatePublic(new X509EncodedKeySpec(FileUtils.read(path)));
EncryptionKeys.put(Integer.valueOf(m.group(1)), key);
}
}
}
} }
catch (Exception e) { catch (Exception e) {
Grasscutter.getLogger().error("An error occurred while loading keys.", e); Grasscutter.getLogger().error("An error occurred while loading keys.", e);

View File

@ -356,6 +356,7 @@ public final class Language {
); );
} }
@SuppressWarnings("unchecked")
private static Int2ObjectMap<TextStrings> loadTextMapsCache() throws Exception { private static Int2ObjectMap<TextStrings> loadTextMapsCache() throws Exception {
try (ObjectInputStream file = new ObjectInputStream(new BufferedInputStream(Files.newInputStream(TEXTMAP_CACHE_PATH), 0x100000))) { try (ObjectInputStream file = new ObjectInputStream(new BufferedInputStream(Files.newInputStream(TEXTMAP_CACHE_PATH), 0x100000))) {
final int fileVersion = file.readInt(); final int fileVersion = file.readInt();

View File

@ -6,12 +6,13 @@ import emu.grasscutter.BuildConfig;
import emu.grasscutter.Grasscutter; import emu.grasscutter.Grasscutter;
import emu.grasscutter.Grasscutter.ServerRunMode; import emu.grasscutter.Grasscutter.ServerRunMode;
import emu.grasscutter.net.packet.PacketOpcodesUtils; import emu.grasscutter.net.packet.PacketOpcodesUtils;
import io.javalin.core.util.JavalinLogger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import java.util.Map; import java.util.Map;
import java.util.function.Function; import java.util.function.Function;
import static emu.grasscutter.config.Configuration.*;
/** /**
* A parser for start-up arguments. * A parser for start-up arguments.
*/ */
@ -80,14 +81,21 @@ public final class StartupArguments {
* @return False to continue execution. * @return False to continue execution.
*/ */
private static boolean enableDebug(String parameter) { private static boolean enableDebug(String parameter) {
// Get the level by parameter. if (parameter != null && parameter.equals("all")) {
var loggerLevel = parameter != null && parameter.equals("all") // Override default debug configs
? Level.DEBUG : Level.INFO; GAME_INFO.isShowLoopPackets = DEBUG_MODE_INFO.isShowLoopPackets;
GAME_INFO.isShowPacketPayload = DEBUG_MODE_INFO.isShowPacketPayload;
GAME_INFO.logPackets = DEBUG_MODE_INFO.logPackets;
DISPATCH_INFO.logRequests = DEBUG_MODE_INFO.logRequests;
}
// Set the logger to debug. // Set the main logger to debug.
Grasscutter.getLogger().setLevel(Level.DEBUG); Grasscutter.getLogger().setLevel(DEBUG_MODE_INFO.serverLoggerLevel);
Grasscutter.getLogger().debug("The logger is now running in debug mode."); Grasscutter.getLogger().debug("The logger is now running in debug mode.");
// Log level to other third-party services
Level loggerLevel = DEBUG_MODE_INFO.servicesLoggersLevel;
// Change loggers to debug. // Change loggers to debug.
((Logger) LoggerFactory.getLogger("io.javalin")) ((Logger) LoggerFactory.getLogger("io.javalin"))
.setLevel(loggerLevel); .setLevel(loggerLevel);

View File

@ -34,14 +34,14 @@
"gachaType": 301, "gachaType": 301,
"scheduleId": 903, "scheduleId": 903,
"bannerType": "EVENT", "bannerType": "EVENT",
"prefabPath": "GachaShowPanel_A097", "prefabPath": "GachaShowPanel_A103",
"titlePath": "UI_GACHA_SHOW_PANEL_A097_TITLE", "titlePath": "UI_GACHA_SHOW_PANEL_A081_TITLE",
"costItemId": 223, "costItemId": 223,
"beginTime": 0, "beginTime": 0,
"endTime": 1924992000, "endTime": 1924992000,
"sortId": 9998, "sortId": 9998,
"rateUpItems4": [1072, 1065, 1053], "rateUpItems4": [1032, 1020, 1034],
"rateUpItems5": [1071], "rateUpItems5": [1073],
"fallbackItems5Pool2": [], "fallbackItems5Pool2": [],
"weights5": [[1,80], [73,80], [90,10000]] "weights5": [[1,80], [73,80], [90,10000]]
}, },
@ -50,14 +50,14 @@
"gachaType": 400, "gachaType": 400,
"scheduleId": 923, "scheduleId": 923,
"bannerType": "EVENT", "bannerType": "EVENT",
"prefabPath": "GachaShowPanel_A098", "prefabPath": "GachaShowPanel_A104",
"titlePath": "UI_GACHA_SHOW_PANEL_A036_TITLE", "titlePath": "UI_GACHA_SHOW_PANEL_A049_TITLE",
"costItemId": 223, "costItemId": 223,
"beginTime": 0, "beginTime": 0,
"endTime": 1924992000, "endTime": 1924992000,
"sortId": 9998, "sortId": 9998,
"rateUpItems4": [1072, 1065, 1053], "rateUpItems4": [1032, 1020, 1034],
"rateUpItems5": [1022], "rateUpItems5": [1049],
"fallbackItems5Pool2": [], "fallbackItems5Pool2": [],
"weights5": [[1,80], [73,80], [90,10000]] "weights5": [[1,80], [73,80], [90,10000]]
}, },
@ -66,14 +66,14 @@
"gachaType": 302, "gachaType": 302,
"scheduleId": 913, "scheduleId": 913,
"bannerType": "WEAPON", "bannerType": "WEAPON",
"prefabPath": "GachaShowPanel_A099", "prefabPath": "GachaShowPanel_A105",
"titlePath": "UI_GACHA_SHOW_PANEL_A021_TITLE", "titlePath": "UI_GACHA_SHOW_PANEL_A021_TITLE",
"costItemId": 223, "costItemId": 223,
"beginTime": 0, "beginTime": 0,
"endTime": 1924992000, "endTime": 1924992000,
"sortId": 9997, "sortId": 9997,
"rateUpItems4":[12415, 11405, 13407, 14403, 15401], "rateUpItems4":[15405, 11402, 13407, 14402, 12403],
"rateUpItems5": [13511, 15503], "rateUpItems5": [14511, 15509],
"fallbackItems5Pool1": [], "fallbackItems5Pool1": [],
"weights4": [[1,600], [7,600], [8,6600], [10,12600]], "weights4": [[1,600], [7,600], [8,6600], [10,12600]],
"weights5": [[1,100], [62,100], [73,7800], [80,10000]], "weights5": [[1,100], [62,100], [73,7800], [80,10000]],

View File

@ -0,0 +1,27 @@
-----BEGIN RSA PRIVATE KEY-----
MIIEpAIBAAKCAQEAyaxqjPJP5+Innfv5IdfQqY/ftS++lnDRe3EczNkIjESWXhHS
OljEw9b9C+/BtF+fO9QZL7Z742y06eIdvsMPQKdGflB26+9OZ8AF4SpXDn3aVWGr
8+9qpB7BELRZI/Ph2FlFL4cobCzMHunncW8zTfMId48+fgHkAzCjRl5rC6XT0Yge
6+eKpXmF+hr0vGYWiTzqPzTABl44WZo3rw0yurZTzkrmRE4kR2VzkjY/rBnQAbFK
KFUKsUozjCXvSag4l461wDkhmmyivpNkK5cAxuDbsmC39iqagMt9438fajLVvYOv
pVs9ci5tiLcbBtfB4Rf/QVAkqtTm86Z0O3e7DwIDAQABAoIBAQCyma226vTW35LE
N5zXWuAg+hhcxk6bvofWMUMXKvGF/0vHPTMXlvuSkDeDNa4vBivneRthBNPMgb3q
DuTWxrogQMOOI8ZdhY3DFexfDvcQD2anDJuSqSmg9Nd36q+yxk3xIoXB5Ilo23dd
vTnJXHhsBNovv7zRLO134cAHFqDoKzt5EEHre0skUcn6HjHOek6c53jvpKr5LSrr
iwx5gMuY/7ZSIUDo9WGY70qbQFGY6bOlX9x8uNjcFF+7SztEVQ+vhJ/+7EvwqaJr
ysweo0l91TKM9WaMuwoucKeceVWuynEw6GGTw8UTLtltekLGe6bS8YxY8fVwnKkT
RwJYwAJRAoGBAP2rhcfOA+1Ja37hUHKebfp9rHsex4+pGyt3Kdu7WdqOn4sexmya
BuiHQcUchPDVla/ruQZ20+8LHgzBDo0m8sY7gpf715UV9NSVIRD0wu26SKRklOFz
J4HBOwU9hBGLSnRUJzyvVlt5O7E9hAv61SCrvWBEcow2YnKNQLwvjMVJAoGBAMuG
oSb3A/ulqtp2zpxVAclYe/bSItZZTOUWP6Vb4hOiHxIJ0n1H9ap6grOYkJ/Yn4gg
yYzKm/noF1wXP7Rj/xOahnvMkzhGdmOabvE9LH5HwQTWxBBWTkZzgBbYtbg+J5MT
cKqJaychSRjJj+xX+d90rtlSu/c27chlSRKAHXWXAoGAFTcIHDq9l1XBmL3tRXi8
h+uExlM/q2MgM5VmucrEbAPrke4D+Ec1drMBLCQDdkTWnPzg34qGlQJgA/8NYX61
ZSDK/j0AvaY1cKX8OvfNaaZftuf2j5ha4H4xmnGXnwQAORRkp62eUk4kUOFtLrdO
pcnXL7rpvZI6z4vCszpi0okCgYEAp3lZEl8g/+oK9UneKfYpSi1tlGTGFevVwozU
QpWhKta1CnraogycsnOtKWvZVi9C1xljwF7YioPY9QaMfTvroY3+K9DjM+OHd96U
fB4Chsc0pW60V10te/t+403f+oPqvLO6ehop+kEBjUwPCkQ6cQ3q8xmJYpvofoYZ
4wdZNnECgYBwG8Vrv7Z+kX9Zuh1FvcRoY57bYLU0cWW92SA3Nvi8pZOIEaLHrQyZ
pvvaLIicR1m9+KsOAmii7ru0zL7KsrGW+5migQsaDi4gzahKQpad/R7MLKi/L53r
Ymo0aZKARLHW82GbomQ0zxdRoo9vaqfGNpXkxyyt3k3GGDunmrskYw==
-----END RSA PRIVATE KEY-----

Binary file not shown.

View File

@ -0,0 +1,27 @@
-----BEGIN RSA PRIVATE KEY-----
MIIEpAIBAAKCAQEAsJbFp3WcsiojjdQtVnTuvtawL2m4XxK93F6lCnFwcZqUP39t
xFGGlrogHMqreyawIUN7E5shtwGzigzjW8Ly5CryBJpXP3ehNTqJS7emb+9LlC19
Oxa1eQuUQnatgcsd16DPH7kJ5JzN3vXnhvUyk4Qficdmm0uk7FRaNYFi7EJs4xyq
FTrp3rDZ0dzBHumlIeK1om7FNt6Nyivgp+UybO7kl0NLFEeSlV4S+7ofitWQsO5x
YqKAzSzz+KIRQcxJidGBlZ1JN/g5DPDpx/ztvOWYUlM7TYk6xN3focZpU0kBzAw/
rn94yW9z8jpXfzk+MvWzVL/HAcPy4ySwkay0NwIDAQABAoIBADzKWpawbVYEHaM4
lLb7oCjAPXzE9zx7djLDvisfLCdfoINPedkoe52ty1o+BtRpWB7LXTY9pFic1FLE
5wvyy6zyf8hH3ZsysqNhWFxhh4FnLmx/UGokAir+anaK5mYVJ1vQtxzjlV1HAbQs
kRyrklKoHDdRFqiFXOwiib97oDNWhD+RxfyGwwJnynZZSXdLbLSiz/QHQNr/+Ufk
KRBaxt0CfU7mOLZxoy6fNAxHdBcBJPHCyh+aDvEbix7nSncSU8Ju/48YJ8DrglbZ
sXCYoA5Uz8NMDuaEMgoNWCFQVoEcRkEUoaH7BlWd3UUFRPnDZ1B4BmkrVoRE8a58
3OqSwakCgYEA19wQUISXtpnmCrEZfbyZ6IwOy8ZCVaVUtbTjVa8UyfNglzzJG3yz
cXU3X35v5/HNCHaXbG2qcbQLThnHBA+obW3RDo+Q49V84Zh1fUNH0ONHHuC09kB/
/gHqzn/4nLf1aJ2O0NrMyrZNsZ0ZKUKQuVCqWjBOmTNUitcc8RpXZ8sCgYEA0W09
POM/It7RoVGI+cfbbgSRmzFo9kzSp5lP7iZ81bnvUMabu2nv3OeGc3Pmdh1ZJFRw
6iDM6VVbG0uz8g+f8+JT32XdqM7MJAmgfcYfTVBMiVnh330WNkeRrGWqQzB2f2Wr
+0vJjU8CAAcOWDh0oNguJ1l1TSyKxqdL8FsA38UCgYEAudt1AJ7psgOYmqQZ+rUl
H6FYLAQsoWmVIk75XpE9KRUwmYdw8QXRy2LNpp9K4z7C9wKFJorWMsh+42Q2gzyo
HHBtjEf4zPLIb8XBg3UmpKjMV73Kkiy/B4nHDr4I5YdO+iCPEy0RH4kQJFnLjEcQ
LT9TLgxh4G7d4B2PgdjYYTkCgYEArdgiV2LETCvulBzcuYufqOn9/He9i4cl7p4j
bathQQFBmSnkqGQ+Cn/eagQxsKaYEsJNoOxtbNu/7x6eVzeFLawYt38Vy0UuzFN5
eC54WXNotTN5fk2VnKU4VYVnGrMmCobZhpbYzoZhQKiazby/g60wUtW9u7xXzqOd
M/428YkCgYBwbEOx1RboH8H+fP1CAiF+cqtq4Jrz9IRWPOgcDpt2Usk1rDweWrZx
bTRlwIaVc5csIEE2X02fut/TTXr1MoXHa6s2cQrnZYq44488NsO4TAC26hqs/x/H
bVOcX13gT26SYngAHHeh7xjWJr/KgIIwvcvgvoVs6lu7a8aLUvrOag==
-----END RSA PRIVATE KEY-----

Binary file not shown.

View File

@ -1,7 +1,7 @@
{ {
"messages": { "messages": {
"game": { "game": {
"address_bind": "🇺🇸Game Server started at \u001b[1m\u001b[33m%s:%s\u001b[0m", "address_bind": "Servidor del juego iniciado en \u001b[1m\u001b[33m%s:%s\u001b[0m",
"port_bind": "Servidor del juego iniciado en el puerto %s", "port_bind": "Servidor del juego iniciado en el puerto %s",
"connect": "Cliente conectado desde %s", "connect": "Cliente conectado desde %s",
"disconnect": "Cliente desconectado desde %s", "disconnect": "Cliente desconectado desde %s",
@ -9,7 +9,7 @@
"command_error": "Comando de error:" "command_error": "Comando de error:"
}, },
"dispatch": { "dispatch": {
"address_bind": "🇺🇸[Dispatch] Dispatch server started at \u001b[1m\u001b[33m%s:%s\u001b[0m", "address_bind": "[Dispatch] Servidor de envio iniciado en \u001b[1m\u001b[33m%s:%s\u001b[0m",
"port_bind": "[Dispatch] El servidor de envío se inició en el puerto %s", "port_bind": "[Dispatch] El servidor de envío se inició en el puerto %s",
"request": "[Dispatch] Petición %s %s del cliente: %s", "request": "[Dispatch] Petición %s %s del cliente: %s",
"keystore": { "keystore": {
@ -293,9 +293,9 @@
"description": "Detiene el servidor" "description": "Detiene el servidor"
}, },
"talent": { "talent": {
"out_of_range": "🇺🇸Invalid talent level. Level should be in range of 1-15.", "out_of_range": "Nivel de talento inválido. El nivel debe estar en un rango de 1-15.",
"set_id": "🇺🇸Set talent %s - \"%s\" to %s.", "set_id": "Establecer talento %s - \"%s\" a %s.",
"id_desc": "🇺🇸Talent %s - \"%s\" - \"%s\"", "id_desc": "Talento %s - \"%s\" - \"%s\"",
"invalid_skill_id": "ID de habilidad inválido.", "invalid_skill_id": "ID de habilidad inválido.",
"invalid_level": "Nivel de talento inválido.", "invalid_level": "Nivel de talento inválido.",
"normal_attack_id": "ID del ataque normal %s.", "normal_attack_id": "ID del ataque normal %s.",
@ -385,19 +385,19 @@
} }
}, },
"plugin": { "plugin": {
"directory_failed": "🇺🇸Failed to create plugins directory: ", "directory_failed": "Error al crear directorio de plugins: ",
"unable_to_load": "🇺🇸Unable to load plugin.", "unable_to_load": "No se puede cargar el plugin.",
"invalid_config": "🇺🇸Plugin %s has an invalid config file.", "invalid_config": "El plugin %s tiene un archivo de configuración inválido.",
"invalid_main_class": "🇺🇸Plugin %s has an invalid main class.", "invalid_main_class": "El plugin %s tiene una clase principal inválida.",
"missing_config": "🇺🇸Plugin %s lacks a valid config file.", "missing_config": "El plugin %s no tiene un archivo de configuración válido.",
"failed_to_load_plugin": "🇺🇸Failed to load plugin: %s", "failed_to_load_plugin": "Error al cargar el plugin: %s",
"failed_to_load": "🇺🇸Failed to load a plugin.", "failed_to_load": "Error al cargar un plugin.",
"failed_to_load_dependencies": "🇺🇸Failed to load plugins with dependencies.", "failed_to_load_dependencies": "Error al cargar plugins con dependencias.",
"loading_plugin": "🇺🇸Loading plugin: %s", "loading_plugin": "Cargando plugin: %s",
"failed_add_id": "🇺🇸Failed to add plugin identifier: %s", "failed_add_id": "Error al añadir identificador de plugin: %s",
"enabling_plugin": "🇺🇸Enabling plugin: %s", "enabling_plugin": "Activando plugin: %s",
"enabling_failed": "🇺🇸Failed to enable plugin: %s", "enabling_failed": "Error al activar el plugin: %s",
"disabling_plugin": "🇺🇸Disabling plugin: %s", "disabling_plugin": "Desactivando el plugin: %s",
"disabling_failed": "🇺🇸Failed to disable plugin: %s" "disabling_failed": "Error al desactivar el plugin: %s"
} }
} }