diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index d14ebf3e0..89d029f19 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -25,6 +25,16 @@ jobs: with: distribution: temurin java-version: '17' + - name: Cache gradle files + uses: actions/cache@v2 + with: + path: | + ~/.gradle/caches + ~/.gradle/wrapper + ./.gradle/loom-cache + key: ${{ runner.os }}-gradle-${{ hashFiles('*.gradle', 'gradle.properties', '**/*.accesswidener') }} + restore-keys: | + ${{ runner.os }}-gradle- - name: Run Gradle run: ./gradlew && ./gradlew jar - name: Upload build diff --git a/.gitignore b/.gitignore index 35ee889c5..ede33151b 100644 --- a/.gitignore +++ b/.gitignore @@ -17,7 +17,7 @@ *.nar *.ear *.zip -*.tar.gz +*.gz *.rar # virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml @@ -52,20 +52,30 @@ tmp/ .vscode # Grasscutter -resources/ -logs/ -plugins/ -data/AbilityEmbryos.json -data/OpenConfig.json +/resources +/logs +/plugins +/data +/keys +/language +/languages +/src/generated + +/*.jar +/*.sh + GM Handbook.txt config.json mitmdump.exe -*.jar -!lib/*.jar mongod.exe -/src/generated/ -/*.sh -language/ -languages/ + gacha-mapping.js -data/gacha_mappings.js +mappings.js +BuildConfig.java + +# lombok +/.apt_generated/ + +# macOS +.DS_Store +data/hk4e/announcement/ diff --git a/README.md b/README.md index 99d80c65b..f9b4b53da 100644 --- a/README.md +++ b/README.md @@ -67,7 +67,7 @@ EN | [中文](README_zh-CN.md) 2. Set network proxy to `127.0.0.1:8080` or the proxy port you specified. -**you can also use `start.cmd` to start servers and proxy daemons automatically** +**you can also use `start.cmd` to start servers and proxy daemons automatically, but you have to set up JAVA_HOME enviroment** ### Building @@ -98,61 +98,11 @@ chmod +x gradlew You can find the output jar in the root of the project folder. -## Commands - -You might want to use this command (`java -jar grasscutter.jar -handbook`) in a cmd that is in the grasscutter folder. It will create a handbook file (GM Handbook.txt) where you can find the item IDs for stuff you want. - -You may want to use this command (`java -jar grasscutter.jar -gachamap`) to generate a mapping file for the gacha record subsystem. The file will be generated to `GRASSCUTTER_RESOURCE/gcstatic` folder. Otherwise you may only see number IDs in the gacha record page. - -There is a dummy user named "Server" in every player's friends list that you can message to use commands. Commands also work in other chat rooms, such as private/team chats. to run commands ingame, you need to add prefix `/` or `!` such as `/pos` - -| Commands | Usage | Permission node | Availability | description | Alias | -| -------------- | ------------------------------------------------- | ------------------------- | ------------ | ------------------------------------------------------------ | ----------------------------------------------- | -| account | account \ [UID] | | Server only | Creates an account with the specified username and the in-game UID for that account. The UID will be auto generated if not set. | | -| broadcast | broadcast \ | server.broadcast | Both side | Sends a message to all the players. | b | -| coop | coop \ \ | server.coop | Both side | Forces someone to join the world of others. | | -| changescene | changescene \ | player.changescene | Client only | Switch scenes by scene ID. | scene | -| clear | clear [UID] | player.clearinv | Client only | Deletes all unequipped and unlocked level 0 artifacts(art)/weapons(wp)/material(all) or all, including 5-star rarity ones from your inventory. | clear | -| drop | drop [amount] | server.drop | Client only | Drops an item around you. | `d` `dropitem` | -| enterdungeon | enterdungeon \ | player.enterdungeon | Client only | Enter a dungeon by dungeon ID | | -| give | give [player] [amount] [level] [finement] | player.give | Both side | Gives item(s) to you or the specified player. (finement option only weapon.) | `g` `item` `giveitem` | -| givechar | givechar \ \ | player.givechar | Both side | Gives the player a specified character. | givec | -| giveart | giveart [player] \ \ [\[,\]]... [level] | player.giveart | Both side | Gives the player a specified artifact. | gart | -| giveall | giveall [uid] [amount] | player.giveall | Both side | Gives all items. | givea | -| godmode | godmode [uid] | player.godmode | Client only | Prevents you from taking damage. | | -| heal | heal | player.heal | Client only | Heals all characters in your current team. | h | -| help | help [command] | | Both side | Sends the help message or shows information about a specified command. | | -| kick | kick \ | server.kick | Both side | Kicks the specified player from the server. (WIP) | k | -| killall | killall [playerUid] [sceneId] | server.killall | Both side | Kills all entities in the current scene or specified scene of the corresponding player. | | -| list | list | | Both side | Lists online players. | | -| permission | permission \ \ | * | Both side | Grants or removes a permission for a user. | | -| position | position | | Client only | Sends your current coordinates. | pos | -| reload | reload | server.reload | Both side | Reloads the server config | | -| resetconst | resetconst [all] | player.resetconstellation | Client only | Resets the constellation level on your currently selected character, will need to relog after using the command to see any changes. | resetconstellation | -| restart | | | Both side | Restarts the current session | | -| say | say \ \ | server.sendmessage | Both side | Sends a message to a player as the server | `sendservmsg` `sendservermessage` `sendmessage` | -| setfetterlevel | setfetterlevel \ | player.setfetterlevel | Client only | Sets the friendship level for your currently selected character | setfetterlvl | -| setstats | setstats \ \ | player.setstats | Client only | Sets a stat for your currently selected character | stats | -| setworldlevel | setworldlevel \ | player.setworldlevel | Client only | Sets your world level (Relog to see proper effects) | setworldlvl | -| spawn | spawn \ [amount] [level(monster only)] | server.spawn | Client only | Spawns some entities around you | | -| stop | stop | server.stop | Both side | Stops the server | | -| talent | talent \ \ | player.settalent | Client only | Sets talent level for your currently selected character | | -| teleport | teleport [@playerUid] \ \ \ [sceneId] | player.teleport | Both side | Change the player's position. | tp | -| tpall | | player.tpall | Client only | Teleports all players in your world to your position | | -| weather | weather \ \ | player.weather | Client only | Changes the weather | w | - -### Bonus - -- Teleporting - - When you want to teleport somewhere, use the in-game marking function on the map. - - Mark a point on the map using the fish hook marking (the last one.) - - (Optional) rename the map marker to a number to override the default Y coordinate (height, default 300.) - - Confirm and close the map. - - You will see your character falling from a very high destination, exact location that you marked. +### Commands have moved to the [wiki](https://github.com/Grasscutters/Grasscutter/wiki/Commands)! # Quick Troubleshooting * If compiling wasn't successful, please check your JDK installation (JDK 17 and validated JDK's bin PATH variable) -* My client doesn't connect, doesn't login, 4206, etc... - Your proxy daemon setup is most likely *the issue*, if you are using Fiddler, make sure it running on another port other than 8888 - -* Startup sequence: Mongodb > Grasscutter > Proxy daemon (mitmdump, fiddler, etc.) > Game +* My client doesn't connect, doesn't login, 4206, etc... - Mostly your proxy daemon setup is *the issue*, if using + Fiddler make sure it running on another port except 8888 +* Startup sequence: MongoDB > Grasscutter > Proxy daemon (mitmdump, fiddler, etc.) > Game diff --git a/README_zh-CN.md b/README_zh-CN.md index 66878b4f4..4c1705e93 100644 --- a/README_zh-CN.md +++ b/README_zh-CN.md @@ -123,11 +123,13 @@ chmod +x gradlew | godmode | godmode [uid] | player.godmode | 仅客户端 | 保护你不受到任何伤害(依然会被击退) | | | heal | heal | player.heal | 仅客户端 | 治疗队伍中所有角色 | h | | help | help [命令] | | 均可使用 | 显示帮助或展示指定命令的帮助 | | +| join | join [多个角色id] | player.join | 仅客户端 | 强制入队角色,跟config.json中的avatarLimits有关(跟队内角色数量上限有关)。用法:`join 10000021 10000022` | | | kick | kick \ | server.kick | 均可使用 | 从服务器中踢出指定玩家 (WIP) | k | | killall | killall [uid] [场景ID] | server.killall | 均可使用 | 杀死指定玩家世界中所在或指定场景的全部生物 | | | list | list | | 均可使用 | 列出在线玩家 | | | permission | permission <权限节点> | * | 均可使用 | 添加或移除玩家的权限 | | | position | position | | 仅客户端 | 获取当前坐标 | pos | +| remove | remove [多个角色在队伍中的序号] | player.remove | 仅客户端 | 强制将某个角色从当前队伍中移除。例如`remove 1 2`表示将1号和2号角色移除 | | | reload | reload | server.reload | 均可使用 | 重载服务器配置 | | | resetconst | resetconst [all] | player.resetconstellation | 仅客户端 | 重置当前角色的命座,重新登录即可生效 | resetconstellation | | restart | restart | | 均可使用 | 重启服务端 | | @@ -140,6 +142,7 @@ chmod +x gradlew | talent | talent <天赋ID> <等级> | player.settalent | 仅客户端 | 设置当前角色的天赋等级 | | | teleport | teleport [@playerUid] \ \ \ [sceneId] | player.teleport | 均可使用 | 传送玩家到指定坐标 | tp | | tpall | | player.tpall | 仅客户端 | 传送多人世界中所有的玩家到自身地点 | | +| unlocktower | | player.tower | 仅客户端 | 解锁深渊全部层 | ut | | weather | weather <天气ID> <气候ID> | player.weather | 仅客户端 | 改变天气 | w | ### 额外功能 diff --git a/build.gradle b/build.gradle index dd8fa9fa0..7beb3a816 100644 --- a/build.gradle +++ b/build.gradle @@ -43,7 +43,8 @@ sourceCompatibility = JavaVersion.VERSION_17 targetCompatibility = JavaVersion.VERSION_17 group = 'xyz.grasscutters' -version = '1.1.0' +version = '1.1.2-dev' + sourceCompatibility = 17 targetCompatibility = 17 @@ -86,6 +87,9 @@ dependencies { implementation group: 'org.luaj', name: 'luaj-jse', version: '3.0.1' protobuf files('proto/') + + compileOnly 'org.projectlombok:lombok:1.18.24' + annotationProcessor 'org.projectlombok:lombok:1.18.24' } configurations.all { @@ -97,12 +101,14 @@ application { mainClassName = 'emu.grasscutter.Grasscutter' } + jar { manifest { attributes 'Main-Class': 'emu.grasscutter.Grasscutter' } jar.baseName = 'grasscutter' + jar.archiveName = project.hasProperty('jarFilename') ? "${jarFilename}.${extension}" : archiveName from { configurations.runtimeClasspath.collect { it.isDirectory() ? it : zipTree(it) } @@ -226,6 +232,23 @@ javadoc { } } +task injectGitHash { + def gitCommitHash = { + try { + return 'git rev-parse --verify --short HEAD'.execute().text.trim() + } catch (e) { + return "GIT_NOT_FOUND" + } + } + new File(projectDir, "src/main/java/emu/grasscutter/BuildConfig.java").text = """ + package emu.grasscutter; + public class BuildConfig { + public static final String VERSION = \"${version}\"; + public static final String GIT_HASH = \"${gitCommitHash()}\"; + } + """ +} + processResources { dependsOn "generateProto" } diff --git a/data/Banners.json b/data/Banners.json deleted file mode 100644 index a4f724ac9..000000000 --- a/data/Banners.json +++ /dev/null @@ -1,49 +0,0 @@ -[ - { - "gachaType": 200, - "scheduleId": 893, - "bannerType": "STANDARD", - "prefabPath": "GachaShowPanel_A022", - "previewPrefabPath": "UI_Tab_GachaShowPanel_A022", - "titlePath": "UI_GACHA_SHOW_PANEL_A022_TITLE", - "costItem": 224, - "beginTime": 0, - "endTime": 1924992000, - "sortId": 1000, - "rateUpItems1": [], - "rateUpItems2": [] - }, - { - "gachaType": 301, - "scheduleId": 903, - "bannerType": "EVENT", - "prefabPath": "GachaShowPanel_A079", - "previewPrefabPath": "UI_Tab_GachaShowPanel_A079", - "titlePath": "UI_GACHA_SHOW_PANEL_A048_TITLE", - "costItem": 223, - "beginTime": 0, - "endTime": 1924992000, - "sortId": 9998, - "maxItemType": 1, - "rateUpItems1": [1002], - "rateUpItems2": [1053, 1020, 1045] - }, - { - "gachaType": 302, - "scheduleId": 913, - "bannerType": "WEAPON", - "prefabPath": "GachaShowPanel_A080", - "previewPrefabPath": "UI_Tab_GachaShowPanel_A080", - "titlePath": "UI_GACHA_SHOW_PANEL_A021_TITLE", - "costItem": 223, - "beginTime": 0, - "endTime": 1924992000, - "sortId": 9997, - "minItemType": 2, - "eventChance": 75, - "softPity": 80, - "hardPity": 80, - "rateUpItems1": [11509, 12504], - "rateUpItems2": [11401, 12402, 13407, 14401, 15401] - } -] diff --git a/data/GameAnnouncement.json b/data/GameAnnouncement.json deleted file mode 100644 index 2bce06fdb..000000000 --- a/data/GameAnnouncement.json +++ /dev/null @@ -1,29 +0,0 @@ -{ -"list": [ - { - "ann_id": 1, - "title": "Welcome to Grasscutter!", - "subtitle": "Welcome", - "banner": "https://uploadstatic-sea.mihoyo.com/announcement/2020/09/17/f4aa42d505822805eebf4a55d72a78d8_2755691727027973637.jpg", - "content": "Hi there!
First of all, welcome to Grasscutter. If you have any issues, please let us know so that Lawnmower can help you! Check out our:
", - "lang": "es-es" - }, - { - "ann_id": 2, - "title": "How to use announcements", - "subtitle": "How to use", - "banner": "https://uploadstatic-sea.mihoyo.com/announcement/2020/09/17/f4aa42d505822805eebf4a55d72a78d8_2755691727027973637.jpg", - "content": "Tips
>How to use announcements

>Announcement content can use HTML

>The specific content of the announcement is stored in the program directorydata/GameAnnouncement.json, whileGameAnnouncementList.json stores the announcement list data

How to use
>In GameAnnouncement
ParametersDescription
ann_IdAnnouncement unique id
titleShow at the top of the content
subtitletitle shown on the left
bannerDisplay between content and title
contentas u see
langdisplay language
totalAnnouncement quantity


>In GameAnnouncementList
If you want to add an annouement, please add the list data in the announcement type corresponding to GameAnnouncementList, and finally add the announcement content in GameAnnouncement", - "lang": "es-es" - }, - { - "ann_id": 3, - "title": "ǻ--This is the event announcement", - "subtitle": "Welcome", - "banner":"https://uploadstatic-sea.mihoyo.com/announcement/2020/09/22/7d85f19b152d218e73224d7c138a0fd0_5818585260283672899.jpg", - "content": "Welcome", - "lang": "es-es" - } -], -"total": 3 -} \ No newline at end of file diff --git a/data/GameAnnouncementList.json b/data/GameAnnouncementList.json deleted file mode 100644 index ea3091fc7..000000000 --- a/data/GameAnnouncementList.json +++ /dev/null @@ -1,119 +0,0 @@ -{ - "t": "System.currentTimeMillis()", - "list": [ - { - "list": [ - { - "ann_id": 1, - "title": "Welcome to Grasscutter!", - "subtitle": "Welcome", - "banner": "https://uploadstatic-sea.mihoyo.com/announcement/2020/09/22/7d85f19b152d218e73224d7c138a0fd0_5818585260283672899.jpg", - "content": "", - "type_label": "Juego", - "tag_label": "1", - "tag_icon": "https://uploadstatic-sea.mihoyo.com/announcement/2020/03/05/a2588f1a51faee9fa8dfe9aead649dd6_7237021399135895303.png", - "login_alert": 1, - "lang": "es-es", - "start_time": "2020-09-25 04:05:30", - "end_time": "2023-10-30 11:00:00", - "type": 2, - "remind": 0, - "alert": 0, - "tag_start_time": "2000-01-02 15:04:05", - "tag_end_time": "2030-01-02 15:04:05", - "remind_ver": 1, - "has_content": true, - "extra_remind": 0 - }, - { - "ann_id": 2, - "title": "Ϸ -- This is the game announcement", - "subtitle": "This is the game announcement", - "banner": "https://uploadstatic-sea.mihoyo.com/announcement/2020/09/17/85b7163c95745a76d49b3d163d893592_6487108933004985049.jpg", - "content": "", - "type_label": "Juego", - "tag_label": "1", - "tag_icon": "https://uploadstatic-sea.mihoyo.com/announcement/2020/03/05/a2588f1a51faee9fa8dfe9aead649dd6_7237021399135895303.png", - "login_alert": 1, - "lang": "es-es", - "start_time": "2020-09-25 15:12:09", - "end_time": "2030-10-30 11:00:00", - "type": 2, - "remind": 0, - "alert": 0, - "tag_start_time": "2000-01-02 08:04:05", - "tag_end_time": "2030-01-02 08:04:05", - "remind_ver": 1, - "has_content": true, - "extra_remind": 0 - } - ], - "type_id": 2, - "type_label": "Juego" - }, - { - "list": [ - { - "ann_id": 3, - "title": "ǻ--This is the event announcement", - "subtitle": "Welcome", - "banner": "https://uploadstatic-sea.mihoyo.com/announcement/2020/09/22/7d85f19b152d218e73224d7c138a0fd0_5818585260283672899.jpg", - "content": "", - "type_label": "Eventos", - "tag_label": "1", - "tag_icon": "https://uploadstatic-sea.mihoyo.com/announcement/2020/03/05/a2588f1a51faee9fa8dfe9aead649dd6_7237021399135895303.png", - "login_alert": 1, - "lang": "es-es", - "start_time": "2020-09-25 04:05:30", - "end_time": "2022-05-02 00:51:00", - "type": 2, - "remind": 0, - "alert": 0, - "tag_start_time": "2000-01-02 15:04:05", - "tag_end_time": "2022-05-02 00:51:00", - "remind_ver": 1, - "has_content": true, - "extra_remind": 0 - } - ], - "type_id": 1, - "type_label": "Eventos" - }, - { - "list": [ - {} - ], - "type_id": 3, - "type_label": "Others" - } - ], - "total": 3, - "type_list": [ - { - "id": 2, - "name": "Ϸϵͳ", - "mi18n_name": "Juego" - }, - { - "id": 1, - "name": "", - "mi18n_name": "Eventos" - }, - { - "id": 3, - "name": "", - "mi18n_name": "Others" - } - ], - "alert": true, - "alert_id": 2, - "timezone": -5, - "pic_list": [ - ], - "pic_total": 0, - "pic_type_list": [ - ], - "pic_alert": false, - "pic_alert_id": 0, - "static_sign": "" -} \ No newline at end of file diff --git a/data/query_cur_region.txt b/data/query_cur_region.txt deleted file mode 100644 index ddef1aff6..000000000 --- a/data/query_cur_region.txt +++ /dev/null @@ -1 +0,0 @@ -GpgdCgo4LjIwOS42Ni4xENWsARosaHR0cHM6Ly9vc2V1cm9vYXNlcnZlci55dWFuc2hlbi5jb20vcmVjaGFyZ2U6BGV1cm9COWh0dHBzOi8vYXV0b3BhdGNoaGsueXVhbnNoZW4uY29tL2NsaWVudF9nYW1lX3Jlcy8yLjZfbGl2ZUo8aHR0cHM6Ly9hdXRvcGF0Y2hoay55dWFuc2hlbi5jb20vY2xpZW50X2Rlc2lnbl9kYXRhLzIuNl9saXZlUpIBaHR0cHM6Ly93ZWJzdGF0aWMtc2VhLmhveW92ZXJzZS5jb20veXMvZXZlbnQvaW0tc2VydmljZS9pbmRleC5odG1sP2ltX291dD1mYWxzZSZzaWduX3R5cGU9MiZhdXRoX2FwcGlkPWltX2NjcyZhdXRoa2V5X3Zlcj0xJndpbl9kaXJlY3Rpb249cG9ydHJhaXRiCDIuNl9saXZlcNnsmgOQAdnsmgOaAVx7InJlbW90ZU5hbWUiOiAiZGF0YV92ZXJzaW9ucyIsICJtZDUiOiAiMWE0NDVhZTQ4ZmJkYmUwMzgzMTJiZTg2OGYyODEzZDIiLCAiZmlsZVNpemUiOiA0NDE1faIBW3sicmVtb3RlTmFtZSI6ICJkYXRhX3ZlcnNpb25zIiwgIm1kNSI6ICIxNzU0MmM3YmJhYzQ5YjkxMWRlMjlhOTYyNzU0YmQ2MSIsICJmaWxlU2l6ZSI6IDUxNH2yAYEGCL23mQMa4AV7InJlbW90ZU5hbWUiOiAicmVzX3ZlcnNpb25zX2V4dGVybmFsIiwgIm1kNSI6ICI5ZjA5MmNhMTMwNjdjYWU0MzEzNDkzZWVkM2QzZTlhZSIsICJmaWxlU2l6ZSI6IDUyNjk2Nn0NCnsicmVtb3RlTmFtZSI6ICJyZXNfdmVyc2lvbnNfbWVkaXVtIiwgIm1kNSI6ICI3Yzk0MmU3MDRhODA0YzIxMjJmYzYzZWU5MjhlNzE0OCIsICJmaWxlU2l6ZSI6IDI4NTU1MH0NCnsicmVtb3RlTmFtZSI6ICJyZXNfdmVyc2lvbnNfc3RyZWFtaW5nIiwgIm1kNSI6ICJlMzVmNmQ4NTNkMDRjZTAyMmFjN2MzNmQ0M2Y0MGU3YyIsICJmaWxlU2l6ZSI6IDEyMjAzNn0NCnsicmVtb3RlTmFtZSI6ICJyZWxlYXNlX3Jlc192ZXJzaW9uc19leHRlcm5hbCIsICJtZDUiOiAiODc2NGIwZjJlZDgxNWNkNDQzMGUwODVjNDYxYTZmNGQiLCAiZmlsZVNpemUiOiA1MjY5NjZ9DQp7InJlbW90ZU5hbWUiOiAicmVsZWFzZV9yZXNfdmVyc2lvbnNfbWVkaXVtIiwgIm1kNSI6ICIxYmJlMzc0YzE2YmZmNDU5MTU5OWMyMTk3MzQyMDM0OCIsICJmaWxlU2l6ZSI6IDI4NTU1MH0NCnsicmVtb3RlTmFtZSI6ICJyZWxlYXNlX3Jlc192ZXJzaW9uc19zdHJlYW1pbmciLCAibWQ1IjogIjcyZmE3ZmY2NmQ1MTRmY2JhMzRhZjAwN2YyYjljMmY4IiwgImZpbGVTaXplIjogMTIyMDM2fQ0KeyJyZW1vdGVOYW1lIjogImJhc2VfcmV2aXNpb24iLCAibWQ1IjogImZiZmE2ZDZlMDcwMmQxYzc5ZTA1NjRmYjI4NjdlNzM4IiwgImZpbGVTaXplIjogMTh9IgEwKgo3YTM0YTM2YTZlMggyLjZfbGl2ZboBnBBFYzJiEAAAAJNvNOvzlkCCDSqpQ6a141IACAAA6gq2poqqrhWr1LS/wULjaiSPJIGsouCxUfY40ezmGoMU5SZZLwQ97KrlkCLKvTVycxteFwEPDxFrKxiHE1oigrAAkjc0NU12JqcQBFL8ExWeR+3QfCPnh7MWo424stJoHPADl9E6R/n3YDXAtq1gUzZu5Y4aGtd9XDyVjozcbIrtVVTGVpvRIuwGYoOCRCwDeRKphu9MoJfbi9mawLh5XSq+KLsAksjM90JJ/DEUzP2XCB/QILsiSiwbET5LUrl65OXCN4sLxZg+86qmeU28cdz4tWDewXYFO+Y7AnJAt7JfpgR/8Os7A9CDPD8WA6GBdqyplmoKRtnjjZG9ZGZIk1YF7AdGUhE8672XlWW3clJaNMBpHFkON+t7Utgu6prY/uJJLFZlGm5KMSend8u8GTMYOE5/AJsVkX/5eS8V2F6Dt6mZJgPtGAlX7Rp1a1R57FMKQLS+9nIzKDMclWFs7ebbnv+lcqckuqln19/JMrYQg9E4IiDVn5akaHZbnYBw0+HDR5kfT2xWfWVo8CJu4mPSpVR1kI4HhZXTURnHa7ezObuwHQm3NS7wHK7VO0E+qgnUgb+vK9M0cHTQE6FsCi/bk0VaHZtDxpMCTfKluJiOsxhvRePOjEVyNyLwLJaoxwwSukMfSH9G+q62ygFmmErAQfKKWLreb72UegOgmhD8T8aytvWXHkWk07QCttqgPhax8BAW2OvRJluvorBUIeHDeO6QeBaZns4EXIYUcIQlfi3yGsLKhhGLb2OgN6a6B3ElxdgXRRYMGAdDoAxEnCcWmdZx874vEvf2KFUP6aU8l4lh0XV6RU/D7A+eqWN5bH4ffS4QBQq2MU6CNA5XumMsD4zUfC6od5T7Tt7uQsgbqtIMgXKpO8lRk4pRgnpPsqM1Ou8kTb1UdQSc4yREuJlrL2Tmtf35cAw7W290LIO/GaO9Rj1CPhATMn7jbUTA6+QN7rTxIzbVl66k95wQth81TYuvw1DlGOpVDTbcCB0uvfmcAAGhj57jVBlyVIu+KJzrrx8z9Uh6scsMCgrMPJn7nsCHSXD6yElTgLrF7FVwgqsxDjgcquqkrSnknP92jb+11IJbw05Ass4hcMRGJxdAefSWDIgdi7l4GnppPdUvLkG5uvBlO85AiT2NpqNmShebfst8rQLFc1B7hAcvh9EpM9Sii0/XXfe9tEf7AwKj2SWmS79PsjEiAiv18tJ6gDlaJ0WFSchXNBRPu8ESvKlE8q7myuc2t5nnxv/hctkez5+nh9SgQ3beL34chSL4RPTH32Jqzs5p7s2n87Bv4vfQccYFAF+kKMUZzKnWKl0LCrStSd3+s+4KF/tZXEKue5KoVobNhaQrD33HjWf9Rvw0ULd4ho4A6wj/uga9o7rZ2C2v1YEDNNqZ5/v/udN3hl7tXrWv5UXcUyTtooa66sJ8oITE8+4aDWimtX6dy0CdFQj2mHppbbvRF66flDd/gFA35xQOXfYdOud3NJogglHXofOoB0LEdbao9C6Nt2v+z7C4S0l3cAMXp/yiI9qxJT9b0mQ2zp2GN5i4gvrp+6iKjqxf+IA++oB/JzbbpSVOFJmFSysv4v3Al2AVbmFycYqv0GaoiZ22wiu8Ok3+LCyKJTITtaJLAtgbpwRfa9SUkdpRwMK1vMN1s6jZU9gdejY3oLVKjFpg66c9bagpmvIG1/gfgY9yT++Y8img4aB/JDJMAS2MVGxGlyrRFGaXBWLq3SkhqDqGD5klbvYv2IFMr4BAHP2uwLb92qEhtlksiVQYk8HGxLWlU2Fo+Pxee8L1xvQOgPdY4/cb33BuJXvtyW5ea5EmVtBPo4MU1ws9BvAzLs13Nisl+/FBvBn8ktkZmE5e6nsdpGEtq4/d7MfCLXGRI5L730mIieeAvtQcb0NMWGcubHPgjY58Pv4MSdRpOQakzM2rA5UD5O57rGH5q6p3HZfZk2iPUcJRsebMKJ4l1omr5JeD95DKDKy7Cts3RajufslekL3/wHwUZbAdEWyux2w1zbiukmhTfh0nbenm8Q8KOASCDo0SWO3e9FpOz5o+phHBVYgmfxRt11OonnLt1qKB7j/a7YdufvkFSsFm4UdgsJMPIHzeBjbSSDlLeCdmdKGQtFLC73npe6efYGUupMIV9EYup1D/OoCNlz2r/FhJ7aLRtj6Q6cb161MLp+rmjbSzN+RVFwf7wAVpLQMrHwTvUzB/8M7cTjN5VFE583uhy4KKf+W3iTxzdyX2SAD9QVu6EXv4KaEFBy8Vo0sga907Fi7imkwgjY2cdnEtPMMeO2Jqj3yWPdqrlVtfAn1jd14oix7YObsJ8mVBeueiplG9d7dxcA1GV+7xX9wOyPDcfH5FBAIlglBIEhjSyNQErH+JYRbOUotXMBQa1tlRRtBSLLDCRgEpG8F+Dv5Oao9ZBnKAi0GRjPKo+OjsehWzrNXGDcoTQsGD/bmKpCdaUOuEa6rLA7tbYwqT2SPdCJzBx2J+kI1bwDWhFF9ROrh9MvmwvMBE9dH5mbQj78p2P5gar2BUcNNbSVvSgxtCa1NHsf/GwrPmTQLPxCrEBcDucxIpsqfINp48iCJGZ4NvlRIZolmaVBWlxjVl/XYcb2YOl3K48e+LsfblTU6tyneZimrS+Y0qt7lncte6NZGPnf98wNLxY39IX8gATezGXoZ03TOhy1jX3epf4Bfw5ZyvU8/XJ2BvbPpw+b8LgS0y0DkeQG4mQGlHidnCAU9odHRwczovL3dlYnN0YXRpYy1zZWEuaG95b3ZlcnNlLmNvbS95cy9ldmVudC9lMjAyMDA0MTBnb19jb21tdW5pdHkvaW5kZXguaHRtbCMv0gEKYWRmZWI4YmU3MdoBCmFkZmViOGJlNzH6AR1odHRwczovL2FjY291bnQuaG95b3ZlcnNlLmNvbYICcWh0dHBzOi8vaGs0ZS1hcGktb3MuaG95b3ZlcnNlLmNvbS9jb21tb24vYXBpY2RrZXkvYXBpL2V4Y2hhbmdlQ2RrZXk/c2lnbl90eXBlPTImYXV0aF9hcHBpZD1hcGljZGtleSZhdXRoa2V5X3Zlcj0xigJMaHR0cHM6Ly9hY2NvdW50LmhveW92ZXJzZS5jb20vIy9hYm91dC9wcml2YWN5SW5HYW1lP2FwcF9pZD00JmJpej1oazRlX2dsb2JhbFqcEEVjMmIQAAAAk2806/OWQIINKqlDprXjUgAIAADqCramiqquFavUtL/BQuNqJI8kgayi4LFR9jjR7OYagxTlJlkvBD3squWQIsq9NXJzG14XAQ8PEWsrGIcTWiKCsACSNzQ1TXYmpxAEUvwTFZ5H7dB8I+eHsxajjbiy0mgc8AOX0TpH+fdgNcC2rWBTNm7ljhoa131cPJWOjNxsiu1VVMZWm9Ei7AZig4JELAN5EqmG70ygl9uL2ZrAuHldKr4ouwCSyMz3Qkn8MRTM/ZcIH9AguyJKLBsRPktSuXrk5cI3iwvFmD7zqqZ5Tbxx3Pi1YN7BdgU75jsCckC3sl+mBH/w6zsD0IM8PxYDoYF2rKmWagpG2eONkb1kZkiTVgXsB0ZSETzrvZeVZbdyUlo0wGkcWQ4363tS2C7qmtj+4kksVmUabkoxJ6d3y7wZMxg4Tn8AmxWRf/l5LxXYXoO3qZkmA+0YCVftGnVrVHnsUwpAtL72cjMoMxyVYWzt5tue/6VypyS6qWfX38kythCD0TgiINWflqRodludgHDT4cNHmR9PbFZ9ZWjwIm7iY9KlVHWQjgeFldNRGcdrt7M5u7AdCbc1LvAcrtU7QT6qCdSBv68r0zRwdNAToWwKL9uTRVodm0PGkwJN8qW4mI6zGG9F486MRXI3IvAslqjHDBK6Qx9If0b6rrbKAWaYSsBB8opYut5vvZR6A6CaEPxPxrK29ZceRaTTtAK22qA+FrHwEBbY69EmW6+isFQh4cN47pB4FpmezgRchhRwhCV+LfIawsqGEYtvY6A3proHcSXF2BdFFgwYB0OgDEScJxaZ1nHzvi8S9/YoVQ/ppTyXiWHRdXpFT8PsD56pY3lsfh99LhAFCrYxToI0Dle6YywPjNR8Lqh3lPtO3u5CyBuq0gyBcqk7yVGTilGCek+yozU67yRNvVR1BJzjJES4mWsvZOa1/flwDDtbb3Qsg78Zo71GPUI+EBMyfuNtRMDr5A3utPEjNtWXrqT3nBC2HzVNi6/DUOUY6lUNNtwIHS69+ZwAAaGPnuNUGXJUi74onOuvHzP1SHqxywwKCsw8mfuewIdJcPrISVOAusXsVXCCqzEOOByq6qStKeSc/3aNv7XUglvDTkCyziFwxEYnF0B59JYMiB2LuXgaemk91S8uQbm68GU7zkCJPY2mo2ZKF5t+y3ytAsVzUHuEBy+H0Skz1KKLT9dd9720R/sDAqPZJaZLv0+yMSICK/Xy0nqAOVonRYVJyFc0FE+7wRK8qUTyrubK5za3mefG/+Fy2R7Pn6eH1KBDdt4vfhyFIvhE9MffYmrOzmnuzafzsG/i99BxxgUAX6QoxRnMqdYqXQsKtK1J3f6z7goX+1lcQq57kqhWhs2FpCsPfceNZ/1G/DRQt3iGjgDrCP+6Br2jutnYLa/VgQM02pnn+/+503eGXu1eta/lRdxTJO2ihrrqwnyghMTz7hoNaKa1fp3LQJ0VCPaYemltu9EXrp+UN3+AUDfnFA5d9h0653c0miCCUdeh86gHQsR1tqj0Lo23a/7PsLhLSXdwAxen/KIj2rElP1vSZDbOnYY3mLiC+un7qIqOrF/4gD76gH8nNtulJU4UmYVLKy/i/cCXYBVuYXJxiq/QZqiJnbbCK7w6Tf4sLIolMhO1oksC2BunBF9r1JSR2lHAwrW8w3WzqNlT2B16NjegtUqMWmDrpz1tqCma8gbX+B+Bj3JP75jyKaDhoH8kMkwBLYxUbEaXKtEUZpcFYurdKSGoOoYPmSVu9i/YgUyvgEAc/a7Atv3aoSG2WSyJVBiTwcbEtaVTYWj4/F57wvXG9A6A91jj9xvfcG4le+3Jbl5rkSZW0E+jgxTXCz0G8DMuzXc2KyX78UG8GfyS2RmYTl7qex2kYS2rj93sx8ItcZEjkvvfSYiJ54C+1BxvQ0xYZy5sc+CNjnw+/gxJ1Gk5BqTMzasDlQPk7nusYfmrqncdl9mTaI9RwlGx5swoniXWiavkl4P3kMoMrLsK2zdFqO5+yV6Qvf/AfBRlsB0RbK7HbDXNuK6SaFN+HSdt6ebxDwo4BIIOjRJY7d70Wk7Pmj6mEcFViCZ/FG3XU6iecu3WooHuP9rth25++QVKwWbhR2Cwkw8gfN4GNtJIOUt4J2Z0oZC0UsLveel7p59gZS6kwhX0Ri6nUP86gI2XPav8WEntotG2PpDpxvXrUwun6uaNtLM35FUXB/vABWktAysfBO9TMH/wztxOM3lUUTnze6HLgop/5beJPHN3JfZIAP1BW7oRe/gpoQUHLxWjSyBr3TsWLuKaTCCNjZx2cS08wx47YmqPfJY92quVW18CfWN3XiiLHtg5uwnyZUF656KmUb13t3FwDUZX7vFf3A7I8Nx8fkUEAiWCUEgSGNLI1ASsf4lhFs5Si1cwFBrW2VFG0FIssMJGASkbwX4O/k5qj1kGcoCLQZGM8qj46Ox6FbOs1cYNyhNCwYP9uYqkJ1pQ64RrqssDu1tjCpPZI90InMHHYn6QjVvANaEUX1E6uH0y+bC8wET10fmZtCPvynY/mBqvYFRw01tJW9KDG0JrU0ex/8bCs+ZNAs/EKsQFwO5zEimyp8g2njyIIkZng2+VEhmiWZpUFaXGNWX9dhxvZg6Xcrjx74ux9uVNTq3Kd5mKatL5jSq3uWdy17o1kY+d/3zA0vFjf0hfyABN7MZehnTdM6HLWNfd6l/gF/DlnK9Tz9cnYG9s+nD5vwuBLTLQOR5AbiZAaUeJ2WLVAtaPymwUgxn8nMUMGk2pDMbkJNLgHPcao2F2HLBdC2W3r1Qs5PtDbMuMCIPSYscVx1x66qcJw19SiQyNLxCY9ErLGDY4mChY/X5NX7Pc2ricYE7EzCSZMYQmtUVZoqn1RPGz1P9Gj65Edm70zFOfRWfR+sTboONM7W3oNk1mkI2M5GwQrQpkAiyb5zwdxpsOwtQ0s0Sin4IOonpW9KcRv8yB8rMOMu6+C6m4h2MwRPj6QrVPWd8PV22qB4iAwfV3UYpjo91Mw/V5xT1DRSrb65vYtu8E4lR3HMv37at4aV6v8RluqeAIHHDJBANaUN3eLCSHewfEb+osQ5BV7XQT5Dwdy/LLFo+hdCBp0Retgtiwsq3+EgRs9i/d9tmghRqEdD/6re752aRiyTsf7CkWY6O7cCGfvmOLE0XhNQra+tLnlJCR1y4m1LOTB3ulQnZrA2E7Mmk7 \ No newline at end of file diff --git a/data/query_region_list.txt b/data/query_region_list.txt deleted file mode 100644 index b4681aaa7..000000000 --- a/data/query_region_list.txt +++ /dev/null @@ -1 +0,0 @@ -ElIKBm9zX3VzYRIHQW1lcmljYRoKREVWX1BVQkxJQyIzaHR0cHM6Ly9vc3VzYWRpc3BhdGNoLnl1YW5zaGVuLmNvbS9xdWVyeV9jdXJfcmVnaW9uElMKB29zX2V1cm8SBkV1cm9wZRoKREVWX1BVQkxJQyI0aHR0cHM6Ly9vc2V1cm9kaXNwYXRjaC55dWFuc2hlbi5jb20vcXVlcnlfY3VyX3JlZ2lvbhJRCgdvc19hc2lhEgRBc2lhGgpERVZfUFVCTElDIjRodHRwczovL29zYXNpYWRpc3BhdGNoLnl1YW5zaGVuLmNvbS9xdWVyeV9jdXJfcmVnaW9uElUKBm9zX2NodBIKVFcsIEhLLCBNTxoKREVWX1BVQkxJQyIzaHR0cHM6Ly9vc2NodGRpc3BhdGNoLnl1YW5zaGVuLmNvbS9xdWVyeV9jdXJfcmVnaW9uKpwQRWMyYhAAAABbrAvbhfIRHfaSCN24qQyVAAgAAMs68ZiMdPfEj41O2wBCYqGiC/WdovvJvaw4t3/m1zIYDrt3/ftK9GKFb7C+2E8FmaHqOnwjJYBg2wI1sXpGmuSxkeWw8Avr36wlNtQjhXNV9zoNKstuZYuheyLlpbPRbYZ3UA6/BzTVsjIhjR1lcqFrigQnpV6MgRR9KqxakCaffK6qIzMlodx4ZPKlqseQhCiyVAvLWQSRqCRcZipzotXsmgLQbpDFtRzhgukXPjfW5dAlzMwswPuu7ZQsf1AKipI34dVQLu6gtXthGgbjn89h/79VR5AokLCPGqIV7/2s+gHfykrjDtyp5rwCcmGQqwV3gHy5LGrHl8Zm12jNd7Qcng51ydqtX4xzet6J2iMF6Dw5nPd/hTyxn+i3Ttk6fop9rbCq3iNgEw3+0cSDal1I1ThYdVnMgPhZgQkZc5/SpTaR+8vfDzRIKbSSrrPSEgLnQvWZOOugXhNdyuiaBc8rJveno7vvktmnhDUF3xWi6osj75j2KghRrdHfDR3Zuh4COrGZDRBSKHft2AvfrxaMT9O8hPzzzYk0U2iicVCDlNP/8wqaT9Vqt1kHmruLxqh377iyp0mxKfNt0+SNRzLyRoyvOar/z3AT6TU9LRoCFrkcJpVsUN+2MVeT52PfMbv5O/Nw9sqsFDlofCJJ/EknY0wDc+tNarYOhDM67/ojn/p6W3ZPBJxb2wcF1TOh9dpAeZdCGJusqhMIj5lpoW8nENTFhkEgMUv2Lh5Z6WpeOAKAu9eDpBMhlRNCccDaNYUgo6TdVDtWxtPrS3NRYqtkvb2I2SEFP0apht954oKdG3ncxyOgHRUkwgtxbCMAngzWo9+VWV3H3OlqeEOv7DdO2o0y95EvlHYb/qtosXPI2jC+6FPa+yl4xmLqcENRTUrU23dsmX3SyBEmZvML4dNeyC53B+mh7DUFtPFJFndxj2tGO9mTSDgy8eCmKG90AiJOMoxaLB2HpnDXN1sTiIcd3WraiE6ZCt4E54hKXvXHPyN52CHkxq1y/TeXHEq4X4MyHyDSRLHmzVs9pnwHM0ZLthKFNyvGfTvjiYokAWtNEuh74syt+m6Wietb6JvgibnnDj6uFKI3BbH4GUT9blsnMgug323bJ6bFvV4iESvz1fNnnUSokWQy5+fWzxPDohULgFzhDCpwov78Bp0E3t6DXSWnrUdNqpLbYKmXO1Hdbn+QH4B90p85UB1V5eSZgxPpUvZbIO4GPScil8K+dkDLdsFa1zypWNmlUN0Ns5H/iuzMuJql2QFYz+SnV1R1T+qywwqCNP9oswcLiAR3XnSacs52vd3PI9+0PZuoF6tVMWlvutsQ34IFZaAwIkdKigZcHumLBt/0KyFASBfN674n8FnHrHOQHU6oCeXkQA9kC8MtkvMb7fOLdzbTsD6SVojzZ64i9mDXxF+iLR9o52OxjIFzwLGRy/ivT/aAnHLZ3AsbnvslDjlQl2ADBFvf7xjmvFu0xlfK58TUpfVEkScFFapWJyKVybB4CRz1wKKz6n/a9581LpCVOWRsJa5p+j0zYcS2PfhmRf3RzwsDHeBjEVlIARbhxNKvmjdZyIidSdMMcsJHDRLE3bvo9kKfag0vRVKmuPLPc9FrACsz3vlkApcVQvzieHWoiP+foEvfj9+7Ti2tLfKdzVkMUmugZiZ46+7PKvIciiiuBPlyld0CCPTtTFHUOMO5dUfrUblX8K3awWiaNQFBS0J3iK08t1bgWfLhsKzsS32fRWugaqecwO9Rji9oHn+UuN8Nz9SgNxodroq9q7y/KHFxbqjCl62g25HN9zUa/s5wnIRwVAiWgTuOe3qGqjwp5m/GR8YVSSK/8mV9EL4AaF8d1uifdVA6wWSH1e/1UB8vcdU83P8ne3u1ho+Y/57WB7KnQaGaiD/108+wiAxNqMb2ex8on01VxdLKV1makXV3gzsvWaRevW8t/K11ZwYfo9g+guWADsA0JO0jWooiaupq1kNWrEheBdSRXBO7Jnb+56cTjPGwLpp7ZOHe/bSCJ4MGzPF3lK66LXhVo+rxvNjhoKVRjhGYxN4T8+AiRo3r+1KwdIGSrtODp3ri3JWAy6Eajp1Ukp9GaCbHSJFnYml84nKew7zLLe//ExQpjd4QAjMTvnbm+Ff6a1jf69QEVo0I33gI7/buwqgjiuvjeL6EYaMolKrKlHZHf/HwWbFbdID8T9aoyZJuCUd6YHaMPRAS6n5nvTwkRLlJ/f6wgyypUGZ22Bb1qGIb9SoPgSgIJkifUoewQW2EexqfoAsHXJVABLy+jp/SC4xzHZOSh42zU1k80HIgrnSOmu6T56F6gqy4Y2cZuZU8LXbO/01u8ifEz8yaXfEFSFdxE0TWl92OLKFtJZr9nNOBQQQr5FDGf6zB1/0CziG/5+PrUDgG3irzho6+7wXkc2CpxlBKOLWdjs3V/Lab6cURz1QZY4HYgUkJtm4U5OKUeO2+murlhC7SrnwyUtGrsD8NbCmI4SRHKPoeLBJQO/m3dRze5Ltr8N9IS7/ukPeOYe1O2agrmhH/JjYfz/l8Gmq8PGY+oavYp8I+2yKvGLD9kCxEgKcTeRh9AW/xPTLGsacrGKQCY+M76DfyLKxCZDiDY9xkBIKchxsMsn7FqZvRMMyJBHbqa3AKQyAN73NCSuFF5f1qDjARU/xqJFhOaKoR64c78oqh1GqOqEFbfNQIRw6WeFCGyW6v6p10uLdR7KXnR7+wub9aG992MpIBk0+gru74yO/WcA0vLdDEQIBwc+M0lmLB53ylsPtde3nliaC5ROHR1IS4LO8Q+3o0BHMr0my0bqFwwCAvZVXOFBHxXyUgrrmUTnZYVSQXNV6+MALBmmRU5yOzhhyHoEdj9YHZeyPpZkYc6DkJWCRYbFfmczNIs133KB9rlfug40w/hHa8pXyRyLaKQUMIUYEvt3Y4AQ== \ No newline at end of file diff --git a/install.sh b/install.sh new file mode 100644 index 000000000..08a31b51d --- /dev/null +++ b/install.sh @@ -0,0 +1,295 @@ +#!/usr/bin/env bash + +# Grasscutter install script for linux +# Made by TurtleIdiot + +# Stops the installer if any command has a non-zero exit status +set -e + +# Checks for root +if [ $EUID != 0 ]; then + echo "Please run the installer as root!" + exit +fi + +is_command() { + # Checks if a given command is available + local check_command="$1" + command -v "${check_command}" > /dev/null 2>&1 +} + +# IP validation +valid_ip() { + local ip=$1 + local stat=1 + + if [[ $ip =~ ^[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}$ ]]; then + OIFS=$IFS + IFS="." + ip=($ip) + IFS=$OIFS + [[ ${ip[0]} -le 255 && ${ip[1]} -le 255 \ + && ${ip[2]} -le 255 && ${ip[3]} -le 255 ]] + stat=$? + fi + return $stat +} + +# Checks for supported installer(s) (only apt-get and pacman right now, might add more in the future) +if is_command apt-get ; then + echo -e "Supported package manager found (apt-get)\n" + + GC_DEPS="mongodb openjdk-17-jre" + INSTALLER_DEPS="wget openssl unzip git" + SYSTEM="deb" # Debian-based (debian, ubuntu) +elif is_command pacman ; then + echo -e "supported package manager found (pacman)\n" + + GC_DEPS="jre17-openjdk" + INSTALLER_DEPS="curl wget openssl unzip git base-devel" # curl is still a dependency here in order to successfully build mongodb + SYSTEM="arch" # Arch for the elitists :P +else + echo "No supported package manager found" + exit +fi + +BRANCH="stable" # Stable by default +# Allows choice between stable and dev branch +echo "Please select the branch you wish to install" +echo -e "!!NOTE!!: stable is the recommended branch.\nDo *NOT* use development unless you have a reason to and know what you're doing" +select branch in "stable" "development" ; do + case $branch in + stable ) + BRANCH="stable" + break;; + development ) + BRANCH="development" + break;; + esac +done + +echo "The following packages will have to be installed in order to INSTALL grasscutter:" +echo -e "$INSTALLER_DEPS \n" +echo "The following packages will have to be installed to RUN grasscutter:" +echo -e "$GC_DEPS \n" + +echo "Do you wish to proceed and install grasscutter?" +select yn in "Yes" "No" ; do + case $yn in + Yes ) break;; + No ) exit;; + esac +done + +echo "Updating package cache..." +case $SYSTEM in # More concise than if + deb ) apt-get update -qq;; + arch ) pacman -Syy;; +esac + +# Starts installing dependencies +echo "Installing setup dependencies..." +case $SYSTEM in # These are one-liners anyways + deb ) apt-get -qq install $INSTALLER_DEPS -y;; + arch ) pacman -Sq --noconfirm --needed $INSTALLER_DEPS > /dev/null;; +esac +echo "Done" + +echo "Installing grasscutter dependencies..." +case $SYSTEM in + deb) apt-get -qq install $GC_DEPS -y > /dev/null;; + arch ) pacman -Sq --noconfirm --needed $GC_DEPS > /dev/null;; +esac +# *sighs* here we go... +INST_ARCH_MONGO="no" +if [ $SYSTEM = "arch" ]; then + echo -e "-=-=-=-=-=--- !! IMPORTANT !! ---=-=-=-=-=-\n" + echo -e " Due to licensing issues with mongodb,\n it is no longer available on the official arch repositiries." + echo -e " In order to install mongodb,\n it needs to be fetched from the Arch User Repository.\n" + echo -e " As this script is running as root,\n a temporary user will need to be created to run makepkg." + echo -e " The temporary user will be deleted once\n makepkg has finished.\n" + echo -e " This will be handled automatically.\n" + echo -e "-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-\n" + echo -e "!!NOTE!!: Only select \"Skip\" if mongodb is already installed on this system" + echo "Do you want to continue?" + select yn in "Yes" "Skip" "No" ; do + case $yn in + Yes ) + INST_ARCH_MONGO="yes" + break;; + No ) exit;; + Skip ) + INST_ARCH_MONGO="no" + break;; + esac + done +fi + +if [ $INST_ARCH_MONGO = "yes" ]; then + DIR=$(pwd) + # Make temp user + echo "Creating temporary user..." + TEMPUSER="gctempuser" + TEMPHOME="/home/$TEMPUSER" + useradd -m $TEMPUSER + cd $TEMPHOME + + # Do the actual makepkg shenanigans + echo "Building mongodb... (this will take a moment)" + su $TEMPUSER< /dev/null + exit +EOF + mv "$(find -name "mongodb-bin*.pkg.tar.zst" -type f)" ./mongodb-bin.pkg.tar.zst + cd $DIR + + # Snatch the file to current working directory + mv "$TEMPHOME/mongodb-bin.pkg.tar.zst" ./mongodb-bin.pkg.tar.zst + chown root ./mongodb-bin.pkg.tar.zst + chgrp root ./mongodb-bin.pkg.tar.zst + chmod 775 ./mongodb-bin.pkg.tar.zst + + echo "Installing mongodb..." + pacman -U mongodb-bin.pkg.tar.zst --noconfirm > /dev/null + rm mongodb-bin.pkg.tar.zst + + echo "Starting mongodb..." + systemctl enable mongodb + systemctl start mongodb + + echo "Removing temporary account..." + userdel -r $TEMPUSER +fi +echo "Done" + +echo "Getting grasscutter..." + +# Download and rename jar +wget -q --show-progress "https://nightly.link/Grasscutters/Grasscutter/workflows/build/$BRANCH/Grasscutter.zip" +echo "unzipping" +unzip -qq Grasscutter.zip +mv $(find -name "grasscutter*.jar" -type f) grasscutter.jar + +# Download resources +echo "Downloading resources... (this will take a moment)" +wget -q --show-progress https://github.com/Koko-boya/Grasscutter_Resources/archive/refs/heads/main.zip -O resources.zip +echo "Extracting..." +unzip -qq resources.zip +mv ./Grasscutter_Resources-main/Resources ./resources + +# Here we do a sparse checkout to only pull /data and /keys +echo "Downloading keys and data..." +mkdir repo +cd repo +git init -q +git remote add origin https://github.com/Grasscutters/Grasscutter.git +git fetch -q +git config core.sparseCheckout true +echo "data/" >> .git/info/sparse-checkout +echo "keys/" >> .git/info/sparse-checkout +git pull origin stable -q +cd ../ +mv ./repo/data ./data +mv ./repo/keys ./keys + +# Generate handbook/config +echo "Please enter language when *NEXT* prompted (press enter/return to continue to language select)" +read +java -jar grasscutter.jar -handbook + +# Prompt IP address for config.json and for generating new keystore.p12 file +echo "Please enter the IP address that will be used to connect to the server" +echo "This can be a local or a public IP address" +echo "This IP address will be used to generate SSL certificates so it is important it is correct" + +while : ; do + read -p "Enter IP: " SERVER_IP + if valid_ip $SERVER_IP; then + break; + else + echo "Invalid IP address. Try again." + fi +done + +# Replaces "127.0.0.1" with given IP +sed -i "s/127.0.0.1/$SERVER_IP/g" config.json + +# Generates new keystore.p12 with the server's IP address +# This is done to prevent a "Connection Timed Out" error from appearing +# after clicking to enter the door in the main menu/title screen +# This issue only exists when connecting to a server *other* than localhost +# since the default keystore.p12 has only been made for localhost + +mkdir certs +cd certs +echo "Generating CA key and certificate pair..." +openssl req -x509 -nodes -days 25202 -newkey rsa:2048 -subj "/C=GB/ST=Essex/L=London/O=Grasscutters/OU=Grasscutters/CN=$SERVER_IP" -keyout CAkey.key -out CAcert.crt +echo "Generating SSL key and certificate pair..." + +openssl genpkey -out ssl.key -algorithm rsa + +# Creates a conf file in order to generate a csr +cat > csr.conf < cert.conf <"; - public Mail.MailItem[] WelcomeMailItems = { - new Mail.MailItem(13509, 1, 1), - new Mail.MailItem(201, 10000, 1), - }; - - public boolean EnableOfficialShop = true; - - public GameRates Game = new GameRates(); - - public GameRates getGameRates() { return Game; } - - public static class GameRates { - public float ADVENTURE_EXP_RATE = 1.0f; - public float MORA_RATE = 1.0f; - public float DOMAIN_DROP_RATE = 1.0f; - } - } -} diff --git a/src/main/java/emu/grasscutter/Configuration.java b/src/main/java/emu/grasscutter/Configuration.java new file mode 100644 index 000000000..486c5d33c --- /dev/null +++ b/src/main/java/emu/grasscutter/Configuration.java @@ -0,0 +1,111 @@ +package emu.grasscutter; + +import emu.grasscutter.utils.ConfigContainer; +import emu.grasscutter.utils.ConfigContainer.*; + +import java.util.Locale; +import java.nio.file.Paths; + +import static emu.grasscutter.Grasscutter.config; + + +/** + * A data container for the server's configuration. + * + * Use `import static emu.grasscutter.Configuration.*;` + * to import all configuration constants. + */ +public final class Configuration extends ConfigContainer { + + /* + * Constants + */ + + // 'c' is short for 'config' and makes code look 'cleaner'. + public static final ConfigContainer c = config; + + public static final Locale LANGUAGE = config.language.language; + public static final Locale FALLBACK_LANGUAGE = config.language.fallback; + public static final String DOCUMENT_LANGUAGE = config.language.document; + private static final String DATA_FOLDER = config.folderStructure.data; + private static final String RESOURCES_FOLDER = config.folderStructure.resources; + private static final String PLUGINS_FOLDER = config.folderStructure.plugins; + private static final String SCRIPTS_FOLDER = config.folderStructure.scripts; + private static final String PACKETS_FOLDER = config.folderStructure.packets; + + public static final Server SERVER = config.server; + public static final Database DATABASE = config.databaseInfo; + public static final Account ACCOUNT = config.account; + + public static final HTTP HTTP_INFO = config.server.http; + public static final Game GAME_INFO = config.server.game; + public static final Dispatch DISPATCH_INFO = config.server.dispatch; + + public static final Encryption HTTP_ENCRYPTION = config.server.http.encryption; + public static final Policies HTTP_POLICIES = config.server.http.policies; + public static final Files HTTP_STATIC_FILES = config.server.http.files; + + public static final GameOptions GAME_OPTIONS = config.server.game.gameOptions; + public static final GameOptions.InventoryLimits INVENTORY_LIMITS = config.server.game.gameOptions.inventoryLimits; + + /* + * Utilities + */ + public static String DATA() { + return DATA_FOLDER; + } + + public static String DATA(String path) { + return Paths.get(DATA_FOLDER, path).toString(); + } + + public static String RESOURCE(String path) { + return Paths.get(RESOURCES_FOLDER, path).toString(); + } + + public static String PLUGIN() { + return PLUGINS_FOLDER; + } + + public static String PLUGIN(String path) { + return Paths.get(PLUGINS_FOLDER, path).toString(); + } + + public static String SCRIPT(String path) { + return Paths.get(SCRIPTS_FOLDER, path).toString(); + } + + public static String PACKET(String path) { + return Paths.get(PACKETS_FOLDER, path).toString(); + } + + /** + * Fallback method. + * @param left Attempt to use. + * @param right Use if left is undefined. + * @return Left or right. + */ + public static T lr(T left, T right) { + return left == null ? right : left; + } + + /** + * {@link Configuration#lr(Object, Object)} for {@link String}s. + * @param left Attempt to use. + * @param right Use if left is empty. + * @return Left or right. + */ + public static String lr(String left, String right) { + return left.isEmpty() ? right : left; + } + + /** + * {@link Configuration#lr(Object, Object)} for {@link Integer}s. + * @param left Attempt to use. + * @param right Use if left is 0. + * @return Left or right. + */ + public static int lr(int left, int right) { + return left == 0 ? right : left; + } +} diff --git a/src/main/java/emu/grasscutter/GameConstants.java b/src/main/java/emu/grasscutter/GameConstants.java index dc07c32e1..5e50e3fd6 100644 --- a/src/main/java/emu/grasscutter/GameConstants.java +++ b/src/main/java/emu/grasscutter/GameConstants.java @@ -6,7 +6,7 @@ import emu.grasscutter.utils.Position; import emu.grasscutter.utils.Utils; public final class GameConstants { - public static String VERSION = "2.6.0"; + public static String VERSION = "2.7.0"; public static final int MAX_TEAMS = 4; public static final int MAIN_CHARACTER_MALE = 10000005; diff --git a/src/main/java/emu/grasscutter/Grasscutter.java b/src/main/java/emu/grasscutter/Grasscutter.java index c593f5f13..af88b74ac 100644 --- a/src/main/java/emu/grasscutter/Grasscutter.java +++ b/src/main/java/emu/grasscutter/Grasscutter.java @@ -1,15 +1,22 @@ package emu.grasscutter; -import java.io.File; -import java.io.FileReader; -import java.io.FileWriter; -import java.io.IOError; +import java.io.*; import java.util.Calendar; +import emu.grasscutter.auth.AuthenticationSystem; +import emu.grasscutter.auth.DefaultAuthentication; import emu.grasscutter.command.CommandMap; +import emu.grasscutter.game.managers.EnergyManager.EnergyManager; +import emu.grasscutter.game.managers.StaminaManager.StaminaManager; import emu.grasscutter.plugin.PluginManager; import emu.grasscutter.plugin.api.ServerHook; import emu.grasscutter.scripts.ScriptLoader; +import emu.grasscutter.server.http.HttpServer; +import emu.grasscutter.server.http.dispatch.DispatchHandler; +import emu.grasscutter.server.http.handlers.*; +import emu.grasscutter.server.http.dispatch.RegionHandler; +import emu.grasscutter.server.http.documentation.DocumentationServerHandler; +import emu.grasscutter.utils.ConfigContainer; import emu.grasscutter.utils.Utils; import org.jline.reader.EndOfFileException; import org.jline.reader.LineReader; @@ -27,30 +34,33 @@ import ch.qos.logback.classic.Logger; import emu.grasscutter.data.ResourceLoader; import emu.grasscutter.database.DatabaseManager; import emu.grasscutter.utils.Language; -import emu.grasscutter.server.dispatch.DispatchServer; import emu.grasscutter.server.game.GameServer; import emu.grasscutter.tools.Tools; import emu.grasscutter.utils.Crypto; +import javax.annotation.Nullable; + import static emu.grasscutter.utils.Language.translate; +import static emu.grasscutter.Configuration.*; public final class Grasscutter { private static final Logger log = (Logger) LoggerFactory.getLogger(Grasscutter.class); private static LineReader consoleLineReader = null; - - private static Config config; + private static Language language; private static final Gson gson = new GsonBuilder().setPrettyPrinting().create(); - private static final File configFile = new File("./config.json"); + public static final File configFile = new File("./config.json"); private static int day; // Current day of week. - private static DispatchServer dispatchServer; + private static HttpServer httpServer; private static GameServer gameServer; private static PluginManager pluginManager; + private static AuthenticationSystem authenticationSystem; public static final Reflections reflector = new Reflections("emu.grasscutter"); + public static ConfigContainer config; static { // Declare logback configuration. @@ -58,6 +68,8 @@ public final class Grasscutter { // Load server configuration. Grasscutter.loadConfig(); + // Attempt to update configuration. + ConfigContainer.updateConfig(); // Load translation files. Grasscutter.loadLanguage(); @@ -66,9 +78,9 @@ public final class Grasscutter { Utils.startupCheck(); } - public static void main(String[] args) throws Exception { - Crypto.loadKeys(); // Load keys from buffers. - + public static void main(String[] args) throws Exception { + Crypto.loadKeys(); // Load keys from buffers. + // Parse arguments. boolean exitEarly = false; for (String arg : args) { @@ -77,57 +89,81 @@ public final class Grasscutter { Tools.createGmHandbook(); exitEarly = true; } case "-gachamap" -> { - Tools.createGachaMapping(Grasscutter.getConfig().DATA_FOLDER + "/gacha_mappings.js"); exitEarly = true; + Tools.createGachaMapping(DATA("gacha_mappings.js")); exitEarly = true; + } + case "-version" -> { + System.out.println("Grasscutter version: " + BuildConfig.VERSION + "-" + BuildConfig.GIT_HASH); exitEarly = true; } } } // Exit early if argument sets it. if(exitEarly) System.exit(0); - + // Initialize server. Grasscutter.getLogger().info(translate("messages.status.starting")); - + Grasscutter.getLogger().info(translate("messages.status.game_version", GameConstants.VERSION)); + Grasscutter.getLogger().info(translate("messages.status.version", BuildConfig.VERSION, BuildConfig.GIT_HASH)); + // Load all resources. Grasscutter.updateDayOfWeek(); ResourceLoader.loadAll(); ScriptLoader.init(); - + EnergyManager.initialize(); + // Initialize database. DatabaseManager.initialize(); - + + // Initialize the default authentication system. + authenticationSystem = new DefaultAuthentication(); + // Create server instances. - dispatchServer = new DispatchServer(); + httpServer = new HttpServer(); gameServer = new GameServer(); // Create a server hook instance with both servers. - new ServerHook(gameServer, dispatchServer); + new ServerHook(gameServer, httpServer); + // Create plugin manager instance. pluginManager = new PluginManager(); - + // Add HTTP routes after loading plugins. + httpServer.addRouter(HttpServer.UnhandledRequestRouter.class); + httpServer.addRouter(HttpServer.DefaultRequestRouter.class); + httpServer.addRouter(RegionHandler.class); + httpServer.addRouter(LogHandler.class); + httpServer.addRouter(GenericHandler.class); + httpServer.addRouter(AnnouncementsHandler.class); + httpServer.addRouter(DispatchHandler.class); + httpServer.addRouter(GachaHandler.class); + httpServer.addRouter(DocumentationServerHandler.class); + + // TODO: find a better place? + StaminaManager.initialize(); + // Start servers. - if (getConfig().RunMode == ServerRunMode.HYBRID) { - dispatchServer.start(); + var runMode = SERVER.runMode; + if (runMode == ServerRunMode.HYBRID) { + httpServer.start(); gameServer.start(); - } else if (getConfig().RunMode == ServerRunMode.DISPATCH_ONLY) { - dispatchServer.start(); - } else if (getConfig().RunMode == ServerRunMode.GAME_ONLY) { + } else if (runMode == ServerRunMode.DISPATCH_ONLY) { + httpServer.start(); + } else if (runMode == ServerRunMode.GAME_ONLY) { gameServer.start(); } else { - getLogger().error(translate("messages.status.run_mode_error", getConfig().RunMode)); + getLogger().error(translate("messages.status.run_mode_error", runMode)); getLogger().error(translate("messages.status.run_mode_help")); getLogger().error(translate("messages.status.shutdown")); System.exit(1); } - + // Enable all plugins. pluginManager.enablePlugins(); - + // Hook into shutdown event. Runtime.getRuntime().addShutdownHook(new Thread(Grasscutter::onShutdown)); - + // Open console. startConsole(); - } + } /** * Server shutdown event. @@ -137,75 +173,61 @@ public final class Grasscutter { pluginManager.disablePlugins(); } - public static void loadConfig() { - try (FileReader file = new FileReader(configFile)) { - config = gson.fromJson(file, Config.class); - saveConfig(); - } catch (Exception e) { - Grasscutter.config = new Config(); - saveConfig(); - } - } - + /* + * Methods for the language system component. + */ + public static void loadLanguage() { - var locale = config.LocaleLanguage; - var languageTag = locale.toLanguageTag(); - - if (languageTag.equals("und")) { - Grasscutter.getLogger().error("Illegal locale language, using 'en-US' instead."); - language = Language.getLanguage("en-US"); - } else { - language = Language.getLanguage(languageTag); - } + var locale = config.language.language; + language = Language.getLanguage(Utils.getLanguageCode(locale)); + } + + /* + * Methods for the configuration system component. + */ + + /** + * Attempts to load the configuration from a file. + */ + public static void loadConfig() { + // Check if config.json exists. If not, we generate a new config. + if (!configFile.exists()) { + getLogger().info("config.json could not be found. Generating a default configuration ..."); + config = new ConfigContainer(); + Grasscutter.saveConfig(config); + return; + } + + // If the file already exists, we attempt to load it. + try (FileReader file = new FileReader(configFile)) { + config = gson.fromJson(file, ConfigContainer.class); + } catch (Exception exception) { + getLogger().error("There was an error while trying to load the configuration from config.json. Please make sure that there are no syntax errors. If you want to start with a default configuration, delete your existing config.json."); + System.exit(1); + } } - public static void saveConfig() { + /** + * Saves the provided server configuration. + * @param config The configuration to save, or null for a new one. + */ + public static void saveConfig(@Nullable ConfigContainer config) { + if(config == null) config = new ConfigContainer(); + try (FileWriter file = new FileWriter(configFile)) { file.write(gson.toJson(config)); + } catch (IOException ignored) { + Grasscutter.getLogger().error("Unable to write to config file."); } catch (Exception e) { - Grasscutter.getLogger().error("Unable to save config file."); + Grasscutter.getLogger().error("Unable to save config file.", e); } } - public static void startConsole() { - // Console should not start in dispatch only mode. - if (getConfig().RunMode == ServerRunMode.DISPATCH_ONLY) { - getLogger().info(translate("messages.dispatch.no_commands_error")); - return; - } - - getLogger().info(translate("messages.status.done")); - String input = null; - boolean isLastInterrupted = false; - while (true) { - try { - input = consoleLineReader.readLine("> "); - } catch (UserInterruptException e) { - if (!isLastInterrupted) { - isLastInterrupted = true; - Grasscutter.getLogger().info("Press Ctrl-C again to shutdown."); - continue; - } else { - Runtime.getRuntime().exit(0); - } - } catch (EndOfFileException e) { - Grasscutter.getLogger().info("EOF detected."); - continue; - } catch (IOError e) { - Grasscutter.getLogger().error("An IO error occurred.", e); - continue; - } - - isLastInterrupted = false; - try { - CommandMap.getInstance().invoke(null, null, input); - } catch (Exception e) { - Grasscutter.getLogger().error(translate("messages.game.command_error"), e); - } - } - } - - public static Config getConfig() { + /* + * Getters for the various server components. + */ + + public static ConfigContainer getConfig() { return config; } @@ -213,6 +235,14 @@ public final class Grasscutter { return language; } + public static void setLanguage(Language language) { + Grasscutter.language = language; + } + + public static Language getLanguage(String langCode) { + return Language.getLanguage(langCode); + } + public static Logger getLogger() { return log; } @@ -241,8 +271,8 @@ public final class Grasscutter { return gson; } - public static DispatchServer getDispatchServer() { - return dispatchServer; + public static HttpServer getHttpServer() { + return httpServer; } public static GameServer getGameServer() { @@ -252,16 +282,74 @@ public final class Grasscutter { public static PluginManager getPluginManager() { return pluginManager; } - - public static void updateDayOfWeek() { - Calendar calendar = Calendar.getInstance(); - day = calendar.get(Calendar.DAY_OF_WEEK); + + public static AuthenticationSystem getAuthenticationSystem() { + return authenticationSystem; } public static int getCurrentDayOfWeek() { return day; } + + /* + * Utility methods. + */ + + public static void updateDayOfWeek() { + Calendar calendar = Calendar.getInstance(); + day = calendar.get(Calendar.DAY_OF_WEEK); + } + public static void startConsole() { + // Console should not start in dispatch only mode. + if (SERVER.runMode == ServerRunMode.DISPATCH_ONLY) { + getLogger().info(translate("messages.dispatch.no_commands_error")); + return; + } + + getLogger().info(translate("messages.status.done")); + String input = null; + boolean isLastInterrupted = false; + while (config.server.game.enableConsole) { + try { + input = consoleLineReader.readLine("> "); + } catch (UserInterruptException e) { + if (!isLastInterrupted) { + isLastInterrupted = true; + Grasscutter.getLogger().info("Press Ctrl-C again to shutdown."); + continue; + } else { + Runtime.getRuntime().exit(0); + } + } catch (EndOfFileException e) { + Grasscutter.getLogger().info("EOF detected."); + continue; + } catch (IOError e) { + Grasscutter.getLogger().error("An IO error occurred.", e); + continue; + } + + isLastInterrupted = false; + try { + CommandMap.getInstance().invoke(null, null, input); + } catch (Exception e) { + Grasscutter.getLogger().error(translate("messages.game.command_error"), e); + } + } + } + + /** + * Sets the authentication system for the server. + * @param authenticationSystem The authentication system to use. + */ + public static void setAuthenticationSystem(AuthenticationSystem authenticationSystem) { + Grasscutter.authenticationSystem = authenticationSystem; + } + + /* + * Enums for the configuration. + */ + public enum ServerRunMode { HYBRID, DISPATCH_ONLY, GAME_ONLY } diff --git a/src/main/java/emu/grasscutter/auth/AuthenticationSystem.java b/src/main/java/emu/grasscutter/auth/AuthenticationSystem.java new file mode 100644 index 000000000..90f2648c5 --- /dev/null +++ b/src/main/java/emu/grasscutter/auth/AuthenticationSystem.java @@ -0,0 +1,134 @@ +package emu.grasscutter.auth; + +import emu.grasscutter.game.Account; +import emu.grasscutter.server.http.objects.*; +import express.http.Request; +import express.http.Response; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Getter; + +import javax.annotation.Nullable; + +/** + * Defines an authenticator for the server. + * Can be changed by plugins. + */ +public interface AuthenticationSystem { + + /** + * Called when a user requests to make an account. + * @param username The provided username. + * @param password The provided password. (SHA-256'ed) + */ + void createAccount(String username, String password); + + /** + * Called when a user requests to reset their password. + * @param username The username of the account to reset. + */ + void resetPassword(String username); + + /** + * Called by plugins to internally verify a user's identity. + * @param details A unique identifier to identify the user. (For example: a JWT token) + * @return The user's account if the verification was successful, null if the user was unable to be verified. + */ + Account verifyUser(String details); + + /** + * This is the authenticator used for password authentication. + * @return An authenticator. + */ + Authenticator getPasswordAuthenticator(); + + /** + * This is the authenticator used for token authentication. + * @return An authenticator. + */ + Authenticator getTokenAuthenticator(); + + /** + * This is the authenticator used for session authentication. + * @return An authenticator. + */ + Authenticator getSessionKeyAuthenticator(); + + /** + * This is the authenticator used for handling external authentication requests. + * @return An authenticator. + */ + ExternalAuthenticator getExternalAuthenticator(); + + /** + * This is the authenticator used for handling OAuth authentication requests. + * @return An authenticator. + */ + OAuthAuthenticator getOAuthAuthenticator(); + + /** + * A data container that holds relevant data for authenticating a client. + */ + @Builder @AllArgsConstructor @Getter + class AuthenticationRequest { + private final Request request; + @Nullable private final Response response; + + @Nullable private final LoginAccountRequestJson passwordRequest; + @Nullable private final LoginTokenRequestJson tokenRequest; + @Nullable private final ComboTokenReqJson sessionKeyRequest; + @Nullable private final ComboTokenReqJson.LoginTokenData sessionKeyData; + } + + /** + * Generates an authentication request from a {@link LoginAccountRequestJson} object. + * @param request The Express request. + * @param jsonData The JSON data. + * @return An authentication request. + */ + static AuthenticationRequest fromPasswordRequest(Request request, LoginAccountRequestJson jsonData) { + return AuthenticationRequest.builder() + .request(request) + .passwordRequest(jsonData) + .build(); + } + + /** + * Generates an authentication request from a {@link LoginTokenRequestJson} object. + * @param request The Express request. + * @param jsonData The JSON data. + * @return An authentication request. + */ + static AuthenticationRequest fromTokenRequest(Request request, LoginTokenRequestJson jsonData) { + return AuthenticationRequest.builder() + .request(request) + .tokenRequest(jsonData) + .build(); + } + + /** + * Generates an authentication request from a {@link ComboTokenReqJson} object. + * @param request The Express request. + * @param jsonData The JSON data. + * @return An authentication request. + */ + static AuthenticationRequest fromComboTokenRequest(Request request, ComboTokenReqJson jsonData, + ComboTokenReqJson.LoginTokenData tokenData) { + return AuthenticationRequest.builder() + .request(request) + .sessionKeyRequest(jsonData) + .sessionKeyData(tokenData) + .build(); + } + + /** + * Generates an authentication request from a {@link Response} object. + * @param request The Express request. + * @param response the Express response. + * @return An authentication request. + */ + static AuthenticationRequest fromExternalRequest(Request request, Response response) { + return AuthenticationRequest.builder().request(request) + .response(response).build(); + } +} diff --git a/src/main/java/emu/grasscutter/auth/Authenticator.java b/src/main/java/emu/grasscutter/auth/Authenticator.java new file mode 100644 index 000000000..a5d756d8c --- /dev/null +++ b/src/main/java/emu/grasscutter/auth/Authenticator.java @@ -0,0 +1,17 @@ +package emu.grasscutter.auth; + +import emu.grasscutter.server.http.objects.*; + +/** + * Handles username/password authentication from the client. + * @param The response object type. Should be {@link LoginResultJson} or {@link ComboTokenResJson} + */ +public interface Authenticator { + + /** + * Attempt to authenticate the client with the provided credentials. + * @param request The authentication request wrapped in a {@link AuthenticationSystem.AuthenticationRequest} object. + * @return The result of the login in an object. + */ + T authenticate(AuthenticationSystem.AuthenticationRequest request); +} \ No newline at end of file diff --git a/src/main/java/emu/grasscutter/auth/DefaultAuthentication.java b/src/main/java/emu/grasscutter/auth/DefaultAuthentication.java new file mode 100644 index 000000000..ba77e7d6e --- /dev/null +++ b/src/main/java/emu/grasscutter/auth/DefaultAuthentication.java @@ -0,0 +1,62 @@ +package emu.grasscutter.auth; + +import emu.grasscutter.Grasscutter; +import emu.grasscutter.auth.DefaultAuthenticators.*; +import emu.grasscutter.game.Account; +import emu.grasscutter.server.http.objects.ComboTokenResJson; +import emu.grasscutter.server.http.objects.LoginResultJson; + +import static emu.grasscutter.utils.Language.translate; + +/** + * The default Grasscutter authentication implementation. + * Allows all users to access any account. + */ +public final class DefaultAuthentication implements AuthenticationSystem { + private final Authenticator passwordAuthenticator = new PasswordAuthenticator(); + private final Authenticator tokenAuthenticator = new TokenAuthenticator(); + private final Authenticator sessionKeyAuthenticator = new SessionKeyAuthenticator(); + private final ExternalAuthenticator externalAuthenticator = new ExternalAuthentication(); + private final OAuthAuthenticator oAuthAuthenticator = new OAuthAuthentication(); + + @Override + public void createAccount(String username, String password) { + // Unhandled. The default authenticator doesn't store passwords. + } + + @Override + public void resetPassword(String username) { + // Unhandled. The default authenticator doesn't store passwords. + } + + @Override + public Account verifyUser(String details) { + Grasscutter.getLogger().info(translate("dispatch.authentication.default_unable_to_verify")); + return null; + } + + @Override + public Authenticator getPasswordAuthenticator() { + return this.passwordAuthenticator; + } + + @Override + public Authenticator getTokenAuthenticator() { + return this.tokenAuthenticator; + } + + @Override + public Authenticator getSessionKeyAuthenticator() { + return this.sessionKeyAuthenticator; + } + + @Override + public ExternalAuthenticator getExternalAuthenticator() { + return this.externalAuthenticator; + } + + @Override + public OAuthAuthenticator getOAuthAuthenticator() { + return this.oAuthAuthenticator; + } +} diff --git a/src/main/java/emu/grasscutter/auth/DefaultAuthenticators.java b/src/main/java/emu/grasscutter/auth/DefaultAuthenticators.java new file mode 100644 index 000000000..905535380 --- /dev/null +++ b/src/main/java/emu/grasscutter/auth/DefaultAuthenticators.java @@ -0,0 +1,228 @@ +package emu.grasscutter.auth; + +import emu.grasscutter.Grasscutter; +import emu.grasscutter.auth.AuthenticationSystem.AuthenticationRequest; +import emu.grasscutter.database.DatabaseHelper; +import emu.grasscutter.game.Account; +import emu.grasscutter.server.http.objects.*; + +import static emu.grasscutter.Configuration.*; +import static emu.grasscutter.utils.Language.translate; + +/** + * A class containing default authenticators. + */ +public final class DefaultAuthenticators { + + /** + * Handles the authentication request from the username and password form. + */ + public static class PasswordAuthenticator implements Authenticator { + @Override public LoginResultJson authenticate(AuthenticationRequest request) { + var response = new LoginResultJson(); + + var requestData = request.getPasswordRequest(); + assert requestData != null; // This should never be null. + int playerCount = Grasscutter.getGameServer().getPlayers().size(); + + boolean successfulLogin = false; + String address = request.getRequest().ip(); + String responseMessage = translate("messages.dispatch.account.username_error"); + String loggerMessage = ""; + + // Get account from database. + Account account = DatabaseHelper.getAccountByName(requestData.account); + if (ACCOUNT.maxPlayer <= -1 || playerCount < ACCOUNT.maxPlayer) { + // Check if account exists. + if(account == null && ACCOUNT.autoCreate) { + // This account has been created AUTOMATICALLY. There will be no permissions added. + account = DatabaseHelper.createAccountWithUid(requestData.account, 0); + + // Check if the account was created successfully. + if(account == null) { + responseMessage = translate("messages.dispatch.account.username_create_error"); + Grasscutter.getLogger().info(translate("messages.dispatch.account.account_login_create_error", address)); + } else { + // Continue with login. + successfulLogin = true; + + // Log the creation. + Grasscutter.getLogger().info(translate("messages.dispatch.account.account_login_create_success", address, response.data.account.uid)); + } + } else if(account != null) + successfulLogin = true; + else + loggerMessage = translate("messages.dispatch.account.account_login_exist_error", address); + + } else { + responseMessage = translate("messages.dispatch.account.server_max_player_limit"); + loggerMessage = translate("messages.dispatch.account.login_max_player_limit", address); + } + + + // Set response data. + if(successfulLogin) { + response.message = "OK"; + response.data.account.uid = account.getId(); + response.data.account.token = account.generateSessionKey(); + response.data.account.email = account.getEmail(); + + loggerMessage = translate("messages.dispatch.account.login_success", address, account.getId()); + } else { + response.retcode = -201; + response.message = responseMessage; + + } + Grasscutter.getLogger().info(loggerMessage); + + return response; + } + } + + /** + * Handles the authentication request from the game when using a registry token. + */ + public static class TokenAuthenticator implements Authenticator { + @Override public LoginResultJson authenticate(AuthenticationRequest request) { + var response = new LoginResultJson(); + + var requestData = request.getTokenRequest(); + assert requestData != null; + + boolean successfulLogin; + String address = request.getRequest().ip(); + String loggerMessage; + int playerCount = Grasscutter.getGameServer().getPlayers().size(); + + // Log the attempt. + Grasscutter.getLogger().info(translate("messages.dispatch.account.login_token_attempt", address)); + + if (ACCOUNT.maxPlayer <= -1 || playerCount < ACCOUNT.maxPlayer) { + + // Get account from database. + Account account = DatabaseHelper.getAccountById(requestData.uid); + + // Check if account exists/token is valid. + successfulLogin = account != null && account.getSessionKey().equals(requestData.token); + + // Set response data. + if(successfulLogin) { + response.message = "OK"; + response.data.account.uid = account.getId(); + response.data.account.token = account.getSessionKey(); + response.data.account.email = account.getEmail(); + + // Log the login. + loggerMessage = translate("messages.dispatch.account.login_token_success", address, requestData.uid); + } else { + response.retcode = -201; + response.message = translate("messages.dispatch.account.account_cache_error"); + + // Log the failure. + loggerMessage = translate("messages.dispatch.account.login_token_error", address); + } + + } else { + response.retcode = -201; + response.message = translate("messages.dispatch.account.server_max_player_limit"); + + loggerMessage = translate("messages.dispatch.account.login_max_player_limit", address); + } + + Grasscutter.getLogger().info(loggerMessage); + return response; + } + } + + /** + * Handles the authentication request from the game when using a combo token/session key. + */ + public static class SessionKeyAuthenticator implements Authenticator { + @Override public ComboTokenResJson authenticate(AuthenticationRequest request) { + var response = new ComboTokenResJson(); + + var requestData = request.getSessionKeyRequest(); + var loginData = request.getSessionKeyData(); + assert requestData != null; assert loginData != null; + + boolean successfulLogin; + String address = request.getRequest().ip(); + String loggerMessage; + int playerCount = Grasscutter.getGameServer().getPlayers().size(); + + if (ACCOUNT.maxPlayer <= -1 || playerCount < ACCOUNT.maxPlayer) { + // Get account from database. + Account account = DatabaseHelper.getAccountById(loginData.uid); + + // Check if account exists/token is valid. + successfulLogin = account != null && account.getSessionKey().equals(loginData.token); + + // Set response data. + if(successfulLogin) { + response.message = "OK"; + response.data.open_id = account.getId(); + response.data.combo_id = "157795300"; + response.data.combo_token = account.generateLoginToken(); + + // Log the login. + loggerMessage = translate("messages.dispatch.account.combo_token_success", address); + + } else { + response.retcode = -201; + response.message = translate("messages.dispatch.account.session_key_error"); + + // Log the failure. + loggerMessage = translate("messages.dispatch.account.combo_token_error", address); + } + } else { + response.retcode = -201; + response.message = translate("messages.dispatch.account.server_max_player_limit"); + + loggerMessage = translate("messages.dispatch.account.login_max_player_limit", address); + } + + Grasscutter.getLogger().info(loggerMessage); + return response; + } + } + + /** + * Handles authentication requests from external sources. + */ + public static class ExternalAuthentication implements ExternalAuthenticator { + @Override public void handleLogin(AuthenticationRequest request) { + assert request.getResponse() != null; + request.getResponse().send("Authentication is not available with the default authentication method."); + } + + @Override public void handleAccountCreation(AuthenticationRequest request) { + assert request.getResponse() != null; + request.getResponse().send("Authentication is not available with the default authentication method."); + } + + @Override public void handlePasswordReset(AuthenticationRequest request) { + assert request.getResponse() != null; + request.getResponse().send("Authentication is not available with the default authentication method."); + } + } + + /** + * Handles authentication requests from OAuth sources. + */ + public static class OAuthAuthentication implements OAuthAuthenticator { + @Override public void handleLogin(AuthenticationRequest request) { + assert request.getResponse() != null; + request.getResponse().send("Authentication is not available with the default authentication method."); + } + + @Override public void handleRedirection(AuthenticationRequest request, ClientType type) { + assert request.getResponse() != null; + request.getResponse().send("Authentication is not available with the default authentication method."); + } + + @Override public void handleTokenProcess(AuthenticationRequest request) { + assert request.getResponse() != null; + request.getResponse().send("Authentication is not available with the default authentication method."); + } + } +} diff --git a/src/main/java/emu/grasscutter/auth/ExternalAuthenticator.java b/src/main/java/emu/grasscutter/auth/ExternalAuthenticator.java new file mode 100644 index 000000000..6bf78af6e --- /dev/null +++ b/src/main/java/emu/grasscutter/auth/ExternalAuthenticator.java @@ -0,0 +1,33 @@ +package emu.grasscutter.auth; + +import emu.grasscutter.auth.AuthenticationSystem.AuthenticationRequest; + +/** + * Handles authentication via external routes. + */ +public interface ExternalAuthenticator { + + /** + * Called when an external login request is made. + * @param request The authentication request. + */ + void handleLogin(AuthenticationRequest request); + + /** + * Called when an external account creation request is made. + * @param request The authentication request. + * + * For developers: Use {@link AuthenticationRequest#getRequest()} to get the request body. + * Use {@link AuthenticationRequest#getResponse()} to get the response body. + */ + void handleAccountCreation(AuthenticationRequest request); + + /** + * Called when an external password reset request is made. + * @param request The authentication request. + * + * For developers: Use {@link AuthenticationRequest#getRequest()} to get the request body. + * Use {@link AuthenticationRequest#getResponse()} to get the response body. + */ + void handlePasswordReset(AuthenticationRequest request); +} diff --git a/src/main/java/emu/grasscutter/auth/OAuthAuthenticator.java b/src/main/java/emu/grasscutter/auth/OAuthAuthenticator.java new file mode 100644 index 000000000..2d7a73454 --- /dev/null +++ b/src/main/java/emu/grasscutter/auth/OAuthAuthenticator.java @@ -0,0 +1,35 @@ +package emu.grasscutter.auth; + +import emu.grasscutter.auth.AuthenticationSystem.AuthenticationRequest; + +/** + * Handles authentication via OAuth routes. + */ +public interface OAuthAuthenticator { + + /** + * Called when an OAuth login request is made. + * @param request The authentication request. + */ + void handleLogin(AuthenticationRequest request); + + /** + * Called when a client requests to redirect to login page. + * @param request The authentication request. + */ + void handleRedirection(AuthenticationRequest request, ClientType clientType); + + /** + * Called when an OAuth login requests callback. + * @param request The authentication request. + */ + void handleTokenProcess(AuthenticationRequest request); + + /** + * The type of the client. + * Used for handling redirection. + */ + enum ClientType { + DESKTOP, MOBILE + } +} diff --git a/src/main/java/emu/grasscutter/command/Command.java b/src/main/java/emu/grasscutter/command/Command.java index 734f454ea..37f813f16 100644 --- a/src/main/java/emu/grasscutter/command/Command.java +++ b/src/main/java/emu/grasscutter/command/Command.java @@ -9,7 +9,7 @@ public @interface Command { String usage() default "No usage specified"; - String description() default "No description specified"; + String description() default "commands.generic.no_description_specified"; String[] aliases() default {}; @@ -17,5 +17,13 @@ public @interface Command { String permissionTargeted() default ""; + public enum TargetRequirement { + NONE, // targetPlayer is not required + OFFLINE, // targetPlayer must be offline + PLAYER, // targetPlayer can be online or offline + ONLINE // targetPlayer must be online + } + TargetRequirement targetRequirement() default TargetRequirement.ONLINE; + boolean threading() default false; } diff --git a/src/main/java/emu/grasscutter/command/CommandHandler.java b/src/main/java/emu/grasscutter/command/CommandHandler.java index ffe21c9be..66553d60a 100644 --- a/src/main/java/emu/grasscutter/command/CommandHandler.java +++ b/src/main/java/emu/grasscutter/command/CommandHandler.java @@ -2,10 +2,14 @@ package emu.grasscutter.command; import emu.grasscutter.Grasscutter; import emu.grasscutter.game.player.Player; +import emu.grasscutter.server.event.game.CommandResponseEvent; +import emu.grasscutter.server.event.types.ServerEvent; +import static emu.grasscutter.utils.Language.translate; import java.util.List; public interface CommandHandler { + /** * Send a message to the target. * @@ -18,6 +22,12 @@ public interface CommandHandler { } else { player.dropMessage(message); } + CommandResponseEvent event = new CommandResponseEvent(ServerEvent.Type.GAME,player, message); + event.call(); + } + + static void sendTranslatedMessage(Player player, String messageKey, Object... args) { + sendMessage(player, translate(player, messageKey, args)); } /** diff --git a/src/main/java/emu/grasscutter/command/CommandMap.java b/src/main/java/emu/grasscutter/command/CommandMap.java index a183c6ac3..2495bf4ad 100644 --- a/src/main/java/emu/grasscutter/command/CommandMap.java +++ b/src/main/java/emu/grasscutter/command/CommandMap.java @@ -8,8 +8,6 @@ import org.reflections.Reflections; import java.util.*; -import static emu.grasscutter.utils.Language.translate; - @SuppressWarnings({"UnusedReturnValue", "unused"}) public final class CommandMap { private final Map commands = new HashMap<>(); @@ -117,7 +115,7 @@ public final class CommandMap { public void invoke(Player player, Player targetPlayer, String rawMessage) { rawMessage = rawMessage.trim(); if (rawMessage.length() == 0) { - CommandHandler.sendMessage(player, translate("commands.generic.not_specified")); + CommandHandler.sendTranslatedMessage(player, "commands.generic.not_specified"); return; } @@ -144,19 +142,20 @@ public final class CommandMap { if (targetUidStr != null) { if (targetUidStr.equals("")) { // Clears the default targetPlayer. targetPlayerIds.remove(playerId); - CommandHandler.sendMessage(player, translate("commands.execution.clear_target")); + CommandHandler.sendTranslatedMessage(player, "commands.execution.clear_target"); } else { // Sets default targetPlayer to the UID provided. try { int uid = Integer.parseInt(targetUidStr); - targetPlayer = Grasscutter.getGameServer().getPlayerByUid(uid); + targetPlayer = Grasscutter.getGameServer().getPlayerByUid(uid, true); if (targetPlayer == null) { - CommandHandler.sendMessage(player, translate("commands.generic.execution.player_exist_offline_error")); + CommandHandler.sendTranslatedMessage(player, "commands.execution.player_exist_error"); } else { targetPlayerIds.put(playerId, uid); - CommandHandler.sendMessage(player, translate("commands.execution.set_target", targetUidStr)); + CommandHandler.sendTranslatedMessage(player, "commands.execution.set_target", targetUidStr); + CommandHandler.sendTranslatedMessage(player, targetPlayer.isOnline()? "commands.execution.set_target_online" : "commands.execution.set_target_offline", targetUidStr); } } catch (NumberFormatException e) { - CommandHandler.sendMessage(player, translate("commands.execution.uid_error")); + CommandHandler.sendTranslatedMessage(player, "commands.execution.uid_error"); } } return; @@ -165,7 +164,7 @@ public final class CommandMap { // Get command handler. CommandHandler handler = this.commands.get(label); if (handler == null) { - CommandHandler.sendMessage(player, translate("commands.generic.unknown_command", label)); + CommandHandler.sendTranslatedMessage(player, "commands.generic.unknown_command", label); return; } @@ -176,14 +175,14 @@ public final class CommandMap { arg = args.remove(i).substring(1); try { int uid = Integer.parseInt(arg); - targetPlayer = Grasscutter.getGameServer().getPlayerByUid(uid); + targetPlayer = Grasscutter.getGameServer().getPlayerByUid(uid, true); if (targetPlayer == null) { - CommandHandler.sendMessage(player, translate("commands.generic.execution.player_exist_offline_error")); + CommandHandler.sendTranslatedMessage(player, "commands.execution.player_exist_error"); return; } break; } catch (NumberFormatException e) { - CommandHandler.sendMessage(player, translate("commands.execution.uid_error")); + CommandHandler.sendTranslatedMessage(player, "commands.execution.uid_error"); return; } } @@ -192,9 +191,9 @@ public final class CommandMap { // If there's still no targetPlayer at this point, use previously-set target if (targetPlayer == null) { if (targetPlayerIds.containsKey(playerId)) { - targetPlayer = Grasscutter.getGameServer().getPlayerByUid(targetPlayerIds.get(playerId)); // We check every time in case the target goes offline after being targeted + targetPlayer = Grasscutter.getGameServer().getPlayerByUid(targetPlayerIds.get(playerId), true); // We check every time in case the target is deleted after being targeted if (targetPlayer == null) { - CommandHandler.sendMessage(player, translate("commands.generic.execution.player_exist_offline_error")); + CommandHandler.sendTranslatedMessage(player, "commands.execution.player_exist_error"); return; } } else { @@ -210,12 +209,29 @@ public final class CommandMap { Account account = player.getAccount(); if (player != targetPlayer) { // Additional permission required for targeting another player if (!permissionNodeTargeted.isEmpty() && !account.hasPermission(permissionNodeTargeted)) { - CommandHandler.sendMessage(player, translate("commands.generic.permission_error")); + CommandHandler.sendTranslatedMessage(player, "commands.generic.permission_error"); return; } } if (!permissionNode.isEmpty() && !account.hasPermission(permissionNode)) { - CommandHandler.sendMessage(player, translate("commands.generic.permission_error")); + CommandHandler.sendTranslatedMessage(player, "commands.generic.permission_error"); + return; + } + } + + // Check if command has unfulfilled constraints on targetPlayer + Command.TargetRequirement targetRequirement = this.annotations.get(label).targetRequirement(); + if (targetRequirement != Command.TargetRequirement.NONE) { + if (targetPlayer == null) { + CommandHandler.sendTranslatedMessage(player, "commands.execution.need_target"); + return; + } + if ((targetRequirement == Command.TargetRequirement.ONLINE) && !targetPlayer.isOnline()) { + CommandHandler.sendTranslatedMessage(player, "commands.execution.need_target_online"); + return; + } + if ((targetRequirement == Command.TargetRequirement.OFFLINE) && targetPlayer.isOnline()) { + CommandHandler.sendTranslatedMessage(player, "commands.execution.need_target_offline"); return; } } diff --git a/src/main/java/emu/grasscutter/command/commands/AccountCommand.java b/src/main/java/emu/grasscutter/command/commands/AccountCommand.java index 627f4680f..0d50b3787 100644 --- a/src/main/java/emu/grasscutter/command/commands/AccountCommand.java +++ b/src/main/java/emu/grasscutter/command/commands/AccountCommand.java @@ -1,26 +1,28 @@ package emu.grasscutter.command.commands; +import emu.grasscutter.Grasscutter; import emu.grasscutter.command.Command; import emu.grasscutter.command.CommandHandler; import emu.grasscutter.database.DatabaseHelper; +import emu.grasscutter.game.Account; import emu.grasscutter.game.player.Player; import java.util.List; import static emu.grasscutter.utils.Language.translate; -@Command(label = "account", usage = "account [uid]", description = "Modify user accounts") +@Command(label = "account", usage = "account [uid]", description = "commands.account.description", targetRequirement = Command.TargetRequirement.NONE) public final class AccountCommand implements CommandHandler { @Override public void execute(Player sender, Player targetPlayer, List args) { if (sender != null) { - CommandHandler.sendMessage(sender, translate("commands.generic.console_execute_error")); + CommandHandler.sendMessage(sender, translate(sender, "commands.generic.console_execute_error")); return; } if (args.size() < 2) { - CommandHandler.sendMessage(null, translate("commands.account.command_usage")); + CommandHandler.sendMessage(null, translate(sender, "commands.account.command_usage")); return; } @@ -29,7 +31,7 @@ public final class AccountCommand implements CommandHandler { switch (action) { default: - CommandHandler.sendMessage(null, translate("commands.account.command_usage")); + CommandHandler.sendMessage(null, translate(sender, "commands.account.command_usage")); return; case "create": int uid = 0; @@ -37,28 +39,41 @@ public final class AccountCommand implements CommandHandler { try { uid = Integer.parseInt(args.get(2)); } catch (NumberFormatException ignored) { - CommandHandler.sendMessage(null, translate("commands.account.invalid")); + CommandHandler.sendMessage(null, translate(sender, "commands.account.invalid")); return; } } - emu.grasscutter.game.Account account = DatabaseHelper.createAccountWithId(username, uid); + emu.grasscutter.game.Account account = DatabaseHelper.createAccountWithUid(username, uid); if (account == null) { - CommandHandler.sendMessage(null, translate("commands.account.exists")); + CommandHandler.sendMessage(null, translate(sender, "commands.account.exists")); return; } else { account.addPermission("*"); account.save(); // Save account to database. - CommandHandler.sendMessage(null, translate("commands.account.create", Integer.toString(account.getPlayerUid()))); + CommandHandler.sendMessage(null, translate(sender, "commands.account.create", Integer.toString(account.getReservedPlayerUid()))); } return; case "delete": - if (DatabaseHelper.deleteAccount(username)) { - CommandHandler.sendMessage(null, translate("commands.account.delete")); - } else { - CommandHandler.sendMessage(null, translate("commands.account.no_account")); + // Get the account we want to delete. + Account toDelete = DatabaseHelper.getAccountByName(username); + + if (toDelete == null) { + CommandHandler.sendMessage(null, translate(sender, "commands.account.no_account")); + return; } + + // Get the player for the account. + // If that player is currently online, we kick them before proceeding with the deletion. + Player player = Grasscutter.getGameServer().getPlayerByAccountId(toDelete.getId()); + if (player != null) { + player.getSession().close(); + } + + // Finally, we do the actual deletion. + DatabaseHelper.deleteAccount(toDelete); + CommandHandler.sendMessage(null, translate(sender, "commands.account.delete")); } } } diff --git a/src/main/java/emu/grasscutter/command/commands/BroadcastCommand.java b/src/main/java/emu/grasscutter/command/commands/BroadcastCommand.java index 1aa234919..163db3891 100644 --- a/src/main/java/emu/grasscutter/command/commands/BroadcastCommand.java +++ b/src/main/java/emu/grasscutter/command/commands/BroadcastCommand.java @@ -9,14 +9,13 @@ import java.util.List; import static emu.grasscutter.utils.Language.translate; -@Command(label = "broadcast", usage = "broadcast ", - description = "Sends a message to all the players", aliases = {"b"}, permission = "server.broadcast") +@Command(label = "broadcast", usage = "broadcast ", aliases = {"b"}, permission = "server.broadcast", description = "commands.broadcast.description", targetRequirement = Command.TargetRequirement.NONE) public final class BroadcastCommand implements CommandHandler { @Override public void execute(Player sender, Player targetPlayer, List args) { if (args.size() < 1) { - CommandHandler.sendMessage(sender, translate("commands.broadcast.command_usage")); + CommandHandler.sendMessage(sender, translate(sender, "commands.broadcast.command_usage")); return; } @@ -26,6 +25,6 @@ public final class BroadcastCommand implements CommandHandler { CommandHandler.sendMessage(p, message); } - CommandHandler.sendMessage(sender, translate("commands.broadcast.message_sent")); + CommandHandler.sendMessage(sender, translate(sender, "commands.broadcast.message_sent")); } } diff --git a/src/main/java/emu/grasscutter/command/commands/ChangeSceneCommand.java b/src/main/java/emu/grasscutter/command/commands/ChangeSceneCommand.java index 1a4e97927..3d41205cc 100644 --- a/src/main/java/emu/grasscutter/command/commands/ChangeSceneCommand.java +++ b/src/main/java/emu/grasscutter/command/commands/ChangeSceneCommand.java @@ -8,36 +8,32 @@ import java.util.List; import static emu.grasscutter.utils.Language.translate; -@Command(label = "changescene", usage = "changescene ", - description = "Changes your scene", aliases = {"scene"}, permission = "player.changescene") +@Command(label = "changescene", usage = "changescene ", aliases = {"scene"}, permission = "player.changescene", permissionTargeted = "player.changescene.others", description = "commands.changescene.description") public final class ChangeSceneCommand implements CommandHandler { + @Override public void execute(Player sender, Player targetPlayer, List args) { - if (targetPlayer == null) { - CommandHandler.sendMessage(sender, translate("commands.execution.need_target")); - return; - } - if (args.size() != 1) { - CommandHandler.sendMessage(sender, translate("commands.changescene.usage")); + CommandHandler.sendMessage(sender, translate(sender, "commands.changescene.usage")); return; } try { int sceneId = Integer.parseInt(args.get(0)); if (sceneId == targetPlayer.getSceneId()) { - CommandHandler.sendMessage(sender, translate("commands.changescene.already_in_scene")); + CommandHandler.sendMessage(sender, translate(sender, "commands.changescene.already_in_scene")); return; } boolean result = targetPlayer.getWorld().transferPlayerToScene(targetPlayer, sceneId, targetPlayer.getPos()); - CommandHandler.sendMessage(sender, translate("commands.changescene.result", Integer.toString(sceneId))); - if (!result) { - CommandHandler.sendMessage(sender, translate("commands.changescene.exists_error")); + CommandHandler.sendMessage(sender, translate(sender, "commands.changescene.exists_error")); + return; } + + CommandHandler.sendMessage(sender, translate(sender, "commands.changescene.success", Integer.toString(sceneId))); } catch (Exception e) { - CommandHandler.sendMessage(sender, translate("commands.execution.argument_error")); + CommandHandler.sendMessage(sender, translate(sender, "commands.execution.argument_error")); } } } diff --git a/src/main/java/emu/grasscutter/command/commands/ClearCommand.java b/src/main/java/emu/grasscutter/command/commands/ClearCommand.java index 47d9f2c0d..d28957450 100644 --- a/src/main/java/emu/grasscutter/command/commands/ClearCommand.java +++ b/src/main/java/emu/grasscutter/command/commands/ClearCommand.java @@ -13,19 +13,15 @@ import java.util.List; import static emu.grasscutter.utils.Language.translate; @Command(label = "clear", usage = "clear ", //Merged /clearartifacts and /clearweapons to /clear [uid] - description = "Deletes unequipped unlocked items, including yellow rarity ones from your inventory", - aliases = {"clear"}, permission = "player.clearinv") + description = "commands.clear.description", + aliases = {"clear"}, permission = "player.clearinv", permissionTargeted = "player.clearinv.others") public final class ClearCommand implements CommandHandler { @Override public void execute(Player sender, Player targetPlayer, List args) { - if (targetPlayer == null) { - CommandHandler.sendMessage(sender, translate("commands.execution.need_target")); - return; - } if (args.size() < 1) { - CommandHandler.sendMessage(sender, translate("commands.clear.command_usage")); + CommandHandler.sendMessage(sender, translate(sender, "commands.clear.command_usage")); return; } Inventory playerInventory = targetPlayer.getInventory(); @@ -37,7 +33,7 @@ public final class ClearCommand implements CommandHandler { .filter(item -> item.getItemType() == ItemType.ITEM_WEAPON) .filter(item -> !item.isLocked() && !item.isEquipped()) .toList(); - CommandHandler.sendMessage(sender, translate("commands.clear.weapons", targetPlayer.getNickname())); + CommandHandler.sendMessage(sender, translate(sender, "commands.clear.weapons", targetPlayer.getNickname())); } case "art" -> { toDelete = playerInventory.getItems().values().stream() @@ -45,7 +41,7 @@ public final class ClearCommand implements CommandHandler { .filter(item -> item.getLevel() == 1 && item.getExp() == 0) .filter(item -> !item.isLocked() && !item.isEquipped()) .toList(); - CommandHandler.sendMessage(sender, translate("commands.clear.artifacts", targetPlayer.getNickname())); + CommandHandler.sendMessage(sender, translate(sender, "commands.clear.artifacts", targetPlayer.getNickname())); } case "mat" -> { toDelete = playerInventory.getItems().values().stream() @@ -53,7 +49,7 @@ public final class ClearCommand implements CommandHandler { .filter(item -> item.getLevel() == 1 && item.getExp() == 0) .filter(item -> !item.isLocked() && !item.isEquipped()) .toList(); - CommandHandler.sendMessage(sender, translate("commands.clear.materials", targetPlayer.getNickname())); + CommandHandler.sendMessage(sender, translate(sender, "commands.clear.materials", targetPlayer.getNickname())); } case "all" -> { toDelete = playerInventory.getItems().values().stream() @@ -61,7 +57,7 @@ public final class ClearCommand implements CommandHandler { .filter(item1 -> item1.getLevel() == 1 && item1.getExp() == 0) .filter(item1 -> !item1.isLocked() && !item1.isEquipped()) .toList(); - CommandHandler.sendMessage(sender, translate("commands.clear.artifacts", targetPlayer.getNickname())); + CommandHandler.sendMessage(sender, translate(sender, "commands.clear.artifacts", targetPlayer.getNickname())); playerInventory.removeItems(toDelete); toDelete = playerInventory.getItems().values().stream() @@ -69,7 +65,7 @@ public final class ClearCommand implements CommandHandler { .filter(item2 -> !item2.isLocked() && !item2.isEquipped()) .toList(); playerInventory.removeItems(toDelete); - CommandHandler.sendMessage(sender, translate("commands.clear.materials", targetPlayer.getNickname())); + CommandHandler.sendMessage(sender, translate(sender, "commands.clear.materials", targetPlayer.getNickname())); toDelete = playerInventory.getItems().values().stream() .filter(item3 -> item3.getItemType() == ItemType.ITEM_WEAPON) @@ -77,28 +73,28 @@ public final class ClearCommand implements CommandHandler { .filter(item3 -> !item3.isLocked() && !item3.isEquipped()) .toList(); playerInventory.removeItems(toDelete); - CommandHandler.sendMessage(sender, translate("commands.clear.weapons", targetPlayer.getNickname())); + CommandHandler.sendMessage(sender, translate(sender, "commands.clear.weapons", targetPlayer.getNickname())); toDelete = playerInventory.getItems().values().stream() .filter(item4 -> item4.getItemType() == ItemType.ITEM_FURNITURE) .filter(item4 -> !item4.isLocked() && !item4.isEquipped()) .toList(); playerInventory.removeItems(toDelete); - CommandHandler.sendMessage(sender, translate("commands.clear.furniture", targetPlayer.getNickname())); + CommandHandler.sendMessage(sender, translate(sender, "commands.clear.furniture", targetPlayer.getNickname())); toDelete = playerInventory.getItems().values().stream() .filter(item5 -> item5.getItemType() == ItemType.ITEM_DISPLAY) .filter(item5 -> !item5.isLocked() && !item5.isEquipped()) .toList(); playerInventory.removeItems(toDelete); - CommandHandler.sendMessage(sender, translate("commands.clear.displays", targetPlayer.getNickname())); + CommandHandler.sendMessage(sender, translate(sender, "commands.clear.displays", targetPlayer.getNickname())); toDelete = playerInventory.getItems().values().stream() .filter(item6 -> item6.getItemType() == ItemType.ITEM_VIRTUAL) .filter(item6 -> !item6.isLocked() && !item6.isEquipped()) .toList(); - CommandHandler.sendMessage(sender, translate("commands.clear.virtuals", targetPlayer.getNickname())); - CommandHandler.sendMessage(sender, translate("commands.clear.everything", targetPlayer.getNickname())); + CommandHandler.sendMessage(sender, translate(sender, "commands.clear.virtuals", targetPlayer.getNickname())); + CommandHandler.sendMessage(sender, translate(sender, "commands.clear.everything", targetPlayer.getNickname())); } } diff --git a/src/main/java/emu/grasscutter/command/commands/CoopCommand.java b/src/main/java/emu/grasscutter/command/commands/CoopCommand.java index 96411019b..c8961340e 100644 --- a/src/main/java/emu/grasscutter/command/commands/CoopCommand.java +++ b/src/main/java/emu/grasscutter/command/commands/CoopCommand.java @@ -9,20 +9,15 @@ import java.util.List; import static emu.grasscutter.utils.Language.translate; -@Command(label = "coop", usage = "coop [host UID]", - description = "Forces someone to join the world of others", permission = "server.coop") +@Command(label = "coop", usage = "coop [host uid]", permission = "server.coop", permissionTargeted = "server.coop.others", description = "commands.coop.description") public final class CoopCommand implements CommandHandler { + @Override public void execute(Player sender, Player targetPlayer, List args) { - if (targetPlayer == null) { - CommandHandler.sendMessage(sender, translate("commands.execution.need_target")); - return; - } - Player host = sender; switch (args.size()) { case 0: // Summon target to self - CommandHandler.sendMessage(sender, translate("commands.coop.usage")); + CommandHandler.sendMessage(sender, translate(sender, "commands.coop.usage")); if (sender == null) // Console doesn't have a self to summon to return; break; @@ -31,16 +26,16 @@ public final class CoopCommand implements CommandHandler { int hostId = Integer.parseInt(args.get(0)); host = Grasscutter.getGameServer().getPlayerByUid(hostId); if (host == null) { - CommandHandler.sendMessage(sender, translate("commands.execution.player_offline_error")); + CommandHandler.sendMessage(sender, translate(sender, "commands.execution.player_offline_error")); return; } break; } catch (NumberFormatException ignored) { - CommandHandler.sendMessage(sender, translate("commands.execution.uid_error")); + CommandHandler.sendMessage(sender, translate(sender, "commands.execution.uid_error")); return; } default: - CommandHandler.sendMessage(sender, translate("commands.coop.usage")); + CommandHandler.sendMessage(sender, translate(sender, "commands.coop.usage")); return; } @@ -50,6 +45,6 @@ public final class CoopCommand implements CommandHandler { } host.getServer().getMultiplayerManager().applyEnterMp(targetPlayer, host.getUid()); targetPlayer.getServer().getMultiplayerManager().applyEnterMpReply(host, targetPlayer.getUid(), true); - CommandHandler.sendMessage(sender, translate("commands.coop.success", targetPlayer.getNickname(), host.getNickname())); + CommandHandler.sendMessage(sender, translate(sender, "commands.coop.success", targetPlayer.getNickname(), host.getNickname())); } } diff --git a/src/main/java/emu/grasscutter/command/commands/DropCommand.java b/src/main/java/emu/grasscutter/command/commands/DropCommand.java index a33a32603..ce1a1761b 100644 --- a/src/main/java/emu/grasscutter/command/commands/DropCommand.java +++ b/src/main/java/emu/grasscutter/command/commands/DropCommand.java @@ -4,7 +4,7 @@ import emu.grasscutter.Grasscutter; import emu.grasscutter.command.Command; import emu.grasscutter.command.CommandHandler; import emu.grasscutter.data.GameData; -import emu.grasscutter.data.def.ItemData; +import emu.grasscutter.data.excels.ItemData; import emu.grasscutter.game.entity.EntityItem; import emu.grasscutter.game.player.Player; import emu.grasscutter.utils.Position; @@ -13,17 +13,11 @@ import java.util.List; import static emu.grasscutter.utils.Language.translate; -@Command(label = "drop", usage = "drop [amount]", - description = "Drops an item near you", aliases = {"d", "dropitem"}, permission = "server.drop") +@Command(label = "drop", usage = "drop [amount]", aliases = {"d", "dropitem"}, permission = "server.drop", permissionTargeted = "server.drop.others", description = "commands.drop.description") public final class DropCommand implements CommandHandler { @Override public void execute(Player sender, Player targetPlayer, List args) { - if (targetPlayer == null) { - CommandHandler.sendMessage(null, translate("commands.execution.need_target")); - return; - } - int item = 0; int amount = 1; @@ -32,25 +26,25 @@ public final class DropCommand implements CommandHandler { try { amount = Integer.parseInt(args.get(1)); } catch (NumberFormatException ignored) { - CommandHandler.sendMessage(sender, translate("commands.generic.invalid.amount")); + CommandHandler.sendMessage(sender, translate(sender, "commands.generic.invalid.amount")); return; } // Slightly cheeky here: no break, so it falls through to initialize the first argument too case 1: try { item = Integer.parseInt(args.get(0)); } catch (NumberFormatException ignored) { - CommandHandler.sendMessage(sender, translate("commands.generic.invalid.itemId")); + CommandHandler.sendMessage(sender, translate(sender, "commands.generic.invalid.itemId")); return; } break; default: - CommandHandler.sendMessage(sender, translate("commands.drop.command_usage")); + CommandHandler.sendMessage(sender, translate(sender, "commands.drop.command_usage")); return; } ItemData itemData = GameData.getItemDataMap().get(item); if (itemData == null) { - CommandHandler.sendMessage(sender, translate("commands.generic.invalid.itemId")); + CommandHandler.sendMessage(sender, translate(sender, "commands.generic.invalid.itemId")); return; } if (itemData.isEquip()) { @@ -64,6 +58,6 @@ public final class DropCommand implements CommandHandler { EntityItem entity = new EntityItem(targetPlayer.getScene(), targetPlayer, itemData, targetPlayer.getPos().clone().addY(3f), amount); targetPlayer.getScene().addEntity(entity); } - CommandHandler.sendMessage(sender, translate("commands.drop.success", Integer.toString(amount), Integer.toString(item))); + CommandHandler.sendMessage(sender, translate(sender, "commands.drop.success", Integer.toString(amount), Integer.toString(item))); } -} \ No newline at end of file +} diff --git a/src/main/java/emu/grasscutter/command/commands/EnterDungeonCommand.java b/src/main/java/emu/grasscutter/command/commands/EnterDungeonCommand.java index 434e80c8f..33e25dca3 100644 --- a/src/main/java/emu/grasscutter/command/commands/EnterDungeonCommand.java +++ b/src/main/java/emu/grasscutter/command/commands/EnterDungeonCommand.java @@ -8,36 +8,31 @@ import java.util.List; import static emu.grasscutter.utils.Language.translate; -@Command(label = "enterdungeon", usage = "enterdungeon ", - description = "Enter a dungeon", aliases = {"dungeon"}, permission = "player.enterdungeon") +@Command(label = "enterdungeon", usage = "enterdungeon ", aliases = {"dungeon"}, permission = "player.enterdungeon", permissionTargeted = "player.enterdungeon.others", description = "commands.enter_dungeon.description") public final class EnterDungeonCommand implements CommandHandler { + @Override public void execute(Player sender, Player targetPlayer, List args) { - if (targetPlayer == null) { - CommandHandler.sendMessage(null, translate("commands.execution.need_target")); - return; - } - if (args.size() < 1) { - CommandHandler.sendMessage(sender, translate("commands.enter_dungeon.usage")); + CommandHandler.sendMessage(sender, translate(sender, "commands.enter_dungeon.usage")); return; } try { int dungeonId = Integer.parseInt(args.get(0)); if (dungeonId == targetPlayer.getSceneId()) { - CommandHandler.sendMessage(sender, translate("commands.enter_dungeon.in_dungeon_error")); + CommandHandler.sendMessage(sender, translate(sender, "commands.enter_dungeon.in_dungeon_error")); return; } boolean result = targetPlayer.getServer().getDungeonManager().enterDungeon(targetPlayer.getSession().getPlayer(), 0, dungeonId); - CommandHandler.sendMessage(sender, translate("commands.enter_dungeon.changed", dungeonId)); + CommandHandler.sendMessage(sender, translate(sender, "commands.enter_dungeon.changed", dungeonId)); if (!result) { - CommandHandler.sendMessage(sender, translate("commands.enter_dungeon.not_found_error")); + CommandHandler.sendMessage(sender, translate(sender, "commands.enter_dungeon.not_found_error")); } } catch (Exception e) { - CommandHandler.sendMessage(sender, translate("commands.enter_dungeon.usage")); + CommandHandler.sendMessage(sender, translate(sender, "commands.enter_dungeon.usage")); } } } diff --git a/src/main/java/emu/grasscutter/command/commands/GiveAllCommand.java b/src/main/java/emu/grasscutter/command/commands/GiveAllCommand.java index bb11de3c2..12c8526a5 100644 --- a/src/main/java/emu/grasscutter/command/commands/GiveAllCommand.java +++ b/src/main/java/emu/grasscutter/command/commands/GiveAllCommand.java @@ -4,8 +4,8 @@ import emu.grasscutter.Grasscutter; import emu.grasscutter.command.Command; import emu.grasscutter.command.CommandHandler; import emu.grasscutter.data.GameData; -import emu.grasscutter.data.def.AvatarData; -import emu.grasscutter.data.def.ItemData; +import emu.grasscutter.data.excels.AvatarData; +import emu.grasscutter.data.excels.ItemData; import emu.grasscutter.game.avatar.Avatar; import emu.grasscutter.game.inventory.GameItem; import emu.grasscutter.game.inventory.ItemType; @@ -15,16 +15,11 @@ import java.util.*; import static emu.grasscutter.utils.Language.translate; -@Command(label = "giveall", usage = "giveall [amount]", - description = "Gives all items", aliases = {"givea"}, permission = "player.giveall", threading = true) +@Command(label = "giveall", usage = "giveall [amount]", aliases = {"givea"}, permission = "player.giveall", permissionTargeted = "player.giveall.others", threading = true, description = "commands.giveAll.description") public final class GiveAllCommand implements CommandHandler { @Override public void execute(Player sender, Player targetPlayer, List args) { - if (targetPlayer == null) { - CommandHandler.sendMessage(sender, translate("commands.execution.need_target")); - return; - } int amount = 99999; switch (args.size()) { @@ -34,21 +29,21 @@ public final class GiveAllCommand implements CommandHandler { try { amount = Integer.parseInt(args.get(0)); } catch (NumberFormatException ignored) { - CommandHandler.sendMessage(sender, translate("commands.generic.invalid.amount")); + CommandHandler.sendMessage(sender, translate(sender, "commands.generic.invalid.amount")); return; } break; default: // invalid - CommandHandler.sendMessage(sender, translate("commands.giveAll.usage")); + CommandHandler.sendMessage(sender, translate(sender, "commands.giveAll.usage")); return; } this.giveAllItems(targetPlayer, amount); - CommandHandler.sendMessage(sender, translate("commands.giveAll.success", targetPlayer.getNickname())); + CommandHandler.sendMessage(sender, translate(targetPlayer, "commands.giveAll.success", targetPlayer.getNickname())); } public void giveAllItems(Player player, int amount) { - CommandHandler.sendMessage(player, translate("commands.giveAll.started")); + CommandHandler.sendMessage(player, translate(player, "commands.giveAll.started")); for (AvatarData avatarData: GameData.getAvatarDataMap().values()) { //Exclude test avatar @@ -62,7 +57,8 @@ public final class GiveAllCommand implements CommandHandler { } // This will handle stats and talents avatar.recalcStats(); - player.addAvatar(avatar); + // Don't try to add each avatar to the current team + player.addAvatar(avatar, false); } //some test items @@ -159,7 +155,7 @@ public final class GiveAllCommand implements CommandHandler { private static final Integer[] testItemsIds = new Integer[] { 210, 211, 314, 315, 317, 1005, 1007, 1105, 1107, 1201, 1202,10366, 101212, 11411, 11506, 11507, 11508, 12505, 12506, 12508, 12509, 13503, - 13506, 14411, 14503, 14505, 14508, 15411, 15504, 15505, 15506, 15508, + 13506, 14411, 14503, 14505, 14508, 15504, 15505, 15506, 20001, 10002, 10003, 10004, 10005, 10006, 10008,100231,100232,100431, 101689,105001,105004, 106000,106001,108000,110000 }; diff --git a/src/main/java/emu/grasscutter/command/commands/GiveArtifactCommand.java b/src/main/java/emu/grasscutter/command/commands/GiveArtifactCommand.java index 541cc440e..eaa0635dc 100644 --- a/src/main/java/emu/grasscutter/command/commands/GiveArtifactCommand.java +++ b/src/main/java/emu/grasscutter/command/commands/GiveArtifactCommand.java @@ -4,53 +4,158 @@ import emu.grasscutter.Grasscutter; import emu.grasscutter.command.Command; import emu.grasscutter.command.CommandHandler; import emu.grasscutter.data.GameData; -import emu.grasscutter.data.def.ItemData; +import emu.grasscutter.data.excels.ItemData; import emu.grasscutter.game.inventory.GameItem; import emu.grasscutter.game.inventory.ItemType; import emu.grasscutter.game.player.Player; import emu.grasscutter.game.props.ActionReason; +import emu.grasscutter.game.inventory.EquipType; import java.util.ArrayList; import java.util.Collections; import java.util.List; +import java.util.Map; +import static java.util.Map.entry; import static emu.grasscutter.utils.Language.translate; -@Command(label = "giveart", usage = "giveart [[,]]... [level]", description = "Gives the player a specified artifact", aliases = {"gart"}, permission = "player.giveart") +@Command(label = "giveart", usage = "giveart [[,]]... [level]", aliases = {"gart"}, permission = "player.giveart", permissionTargeted = "player.giveart.others", description = "commands.giveArtifact.description") public final class GiveArtifactCommand implements CommandHandler { + private static final Map> mainPropMap = Map.ofEntries( + entry("hp", Map.ofEntries(entry(EquipType.EQUIP_BRACER, 14001))), + entry("hp%", Map.ofEntries(entry(EquipType.EQUIP_SHOES, 10980), entry(EquipType.EQUIP_RING, 50980), entry(EquipType.EQUIP_DRESS, 30980))), + entry("atk", Map.ofEntries(entry(EquipType.EQUIP_NECKLACE, 12001))), + entry("atk%", Map.ofEntries(entry(EquipType.EQUIP_SHOES, 10990), entry(EquipType.EQUIP_RING, 50990), entry(EquipType.EQUIP_DRESS, 30990))), + entry("def%", Map.ofEntries(entry(EquipType.EQUIP_SHOES, 10970), entry(EquipType.EQUIP_RING, 50970), entry(EquipType.EQUIP_DRESS, 30970))), + entry("er", Map.ofEntries(entry(EquipType.EQUIP_SHOES, 10960))), + entry("em", Map.ofEntries(entry(EquipType.EQUIP_SHOES, 10950), entry(EquipType.EQUIP_RING, 50880), entry(EquipType.EQUIP_DRESS, 30930))), + entry("hb", Map.ofEntries(entry(EquipType.EQUIP_DRESS, 30940))), + entry("cdmg", Map.ofEntries(entry(EquipType.EQUIP_DRESS, 30950))), + entry("cr", Map.ofEntries(entry(EquipType.EQUIP_DRESS, 30960))), + entry("phys%", Map.ofEntries(entry(EquipType.EQUIP_RING, 50890))), + entry("dendro%", Map.ofEntries(entry(EquipType.EQUIP_RING, 50900))), + entry("geo%", Map.ofEntries(entry(EquipType.EQUIP_RING, 50910))), + entry("anemo%", Map.ofEntries(entry(EquipType.EQUIP_RING, 50920))), + entry("hydro%", Map.ofEntries(entry(EquipType.EQUIP_RING, 50930))), + entry("cryo%", Map.ofEntries(entry(EquipType.EQUIP_RING, 50940))), + entry("electro%", Map.ofEntries(entry(EquipType.EQUIP_RING, 50950))), + entry("pyro%", Map.ofEntries(entry(EquipType.EQUIP_RING, 50960))) + ); + private static final Map appendPropMap = Map.ofEntries( + entry("hp", "0102"), + entry("hp%", "0103"), + entry("atk", "0105"), + entry("atk%", "0106"), + entry("def", "0108"), + entry("def%", "0109"), + entry("er", "0123"), + entry("em", "0124"), + entry("cr", "0120"), + entry("cdmg", "0122") + ); + + private int getAppendPropId(String substatText, ItemData itemData) { + int res; + + // If the given substat text is an integer, we just use that + // as the append prop ID. + try { + res = Integer.parseInt(substatText); + return res; + } + catch (NumberFormatException ignores) { + // No need to handle this here. We just continue with the + // possibility of the argument being a substat string. + } + + // If the argument was not an integer, we try to determine + // the append prop ID from the given text + artifact information. + // A substat string has the format `substat_tier`, with the + // `_tier` part being optional. + String[] substatArgs = substatText.split("_"); + String substatType; + int substatTier; + + if (substatArgs.length == 1) { + substatType = substatArgs[0]; + substatTier = + itemData.getRankLevel() == 1 ? 2 + : itemData.getRankLevel() == 2 ? 3 + : 4; + } + else if (substatArgs.length == 2) { + substatType = substatArgs[0]; + substatTier = Integer.parseInt(substatArgs[1]); + } + else { + throw new IllegalArgumentException(); + } + + // Check if the specified tier is legal for the artifact rarity. + if (substatTier < 1 || substatTier > 4) { + throw new IllegalArgumentException(); + } + if (itemData.getRankLevel() == 1 && substatTier > 2) { + throw new IllegalArgumentException(); + } + if (itemData.getRankLevel() == 2 && substatTier > 3) { + throw new IllegalArgumentException(); + } + + // Check if the given substat type string is a legal stat. + if (!appendPropMap.containsKey(substatType)) { + throw new IllegalArgumentException(); + } + + // Build the append prop ID. + return Integer.parseInt(Integer.toString(itemData.getRankLevel()) + appendPropMap.get(substatType) + Integer.toString(substatTier)); + } + @Override public void execute(Player sender, Player targetPlayer, List args) { - if (targetPlayer == null) { - CommandHandler.sendMessage(sender, translate("commands.execution.need_target")); - return; - } - + // Sanity check if (args.size() < 2) { - CommandHandler.sendMessage(sender, translate("commands.giveArtifact.usage")); + CommandHandler.sendMessage(sender, translate(sender, "commands.giveArtifact.usage")); return; } + // Get the artifact piece ID from the arguments. int itemId; try { itemId = Integer.parseInt(args.remove(0)); } catch (NumberFormatException ignored) { - CommandHandler.sendMessage(sender, translate("commands.giveArtifact.id_error")); + CommandHandler.sendMessage(sender, translate(sender, "commands.giveArtifact.id_error")); return; } + ItemData itemData = GameData.getItemDataMap().get(itemId); if (itemData.getItemType() != ItemType.ITEM_RELIQUARY) { - CommandHandler.sendMessage(sender, translate("commands.giveArtifact.id_error")); + CommandHandler.sendMessage(sender, translate(sender, "commands.giveArtifact.id_error")); return; } + // Get the main stat from the arguments. + // If the given argument is an integer, we use that. + // If not, we check if the argument string is in the main prop map. + String mainPropIdString = args.remove(0); int mainPropId; + try { - mainPropId = Integer.parseInt(args.remove(0)); + mainPropId = Integer.parseInt(mainPropIdString); } catch (NumberFormatException ignored) { - CommandHandler.sendMessage(sender, translate("commands.generic.execution.argument_error")); + mainPropId = -1; + } + + if (mainPropMap.containsKey(mainPropIdString) && mainPropMap.get(mainPropIdString).containsKey(itemData.getEquipType())) { + mainPropId = mainPropMap.get(mainPropIdString).get(itemData.getEquipType()); + } + + if (mainPropId == -1) { + CommandHandler.sendMessage(sender, translate(sender, "commands.execution.argument_error")); return; } + // Get the level from the arguments. int level = 1; try { int last = Integer.parseInt(args.get(args.size()-1)); @@ -61,9 +166,13 @@ public final class GiveArtifactCommand implements CommandHandler { } catch (NumberFormatException ignored) { // Could be a stat,times string so no need to panic } - List appendPropIdList = new ArrayList<>(); + // Get substats. + ArrayList appendPropIdList = new ArrayList<>(); try { + // Every remaining argument is a substat. args.forEach(it -> { + // The substat syntax permits specifying a number of rolls for the given + // substat. Split the string into stat and number if that is the case here. String[] arr; int n = 1; if ((arr = it.split(",")).length == 2) { @@ -73,13 +182,19 @@ public final class GiveArtifactCommand implements CommandHandler { n = 200; } } - appendPropIdList.addAll(Collections.nCopies(n, Integer.parseInt(it))); + + // Determine the substat ID. + int appendPropId = getAppendPropId(it, itemData); + + // Add the current substat. + appendPropIdList.addAll(Collections.nCopies(n, appendPropId)); }); } catch (Exception ignored) { - CommandHandler.sendMessage(sender, translate("commands.execution.argument_error")); + CommandHandler.sendMessage(sender, translate(sender, "commands.execution.argument_error")); return; } + // Create item for the artifact. GameItem item = new GameItem(itemData); item.setLevel(level); item.setMainPropId(mainPropId); @@ -87,7 +202,7 @@ public final class GiveArtifactCommand implements CommandHandler { item.getAppendPropIdList().addAll(appendPropIdList); targetPlayer.getInventory().addItem(item, ActionReason.SubfieldDrop); - CommandHandler.sendMessage(sender, translate("commands.giveArtifact.success", Integer.toString(itemId), Integer.toString(targetPlayer.getUid()))); + CommandHandler.sendMessage(sender, translate(sender, "commands.giveArtifact.success", Integer.toString(itemId), Integer.toString(targetPlayer.getUid()))); } } diff --git a/src/main/java/emu/grasscutter/command/commands/GiveCharCommand.java b/src/main/java/emu/grasscutter/command/commands/GiveCharCommand.java index 4b3279202..34592fb81 100644 --- a/src/main/java/emu/grasscutter/command/commands/GiveCharCommand.java +++ b/src/main/java/emu/grasscutter/command/commands/GiveCharCommand.java @@ -4,7 +4,7 @@ import emu.grasscutter.Grasscutter; import emu.grasscutter.command.Command; import emu.grasscutter.command.CommandHandler; import emu.grasscutter.data.GameData; -import emu.grasscutter.data.def.AvatarData; +import emu.grasscutter.data.excels.AvatarData; import emu.grasscutter.game.avatar.Avatar; import emu.grasscutter.game.player.Player; @@ -12,17 +12,11 @@ import java.util.List; import static emu.grasscutter.utils.Language.translate; -@Command(label = "givechar", usage = "givechar [level]", - description = "Gives the player a specified character", aliases = {"givec"}, permission = "player.givechar") +@Command(label = "givechar", usage = "givechar [level]", aliases = {"givec"}, permission = "player.givechar", permissionTargeted = "player.givechar.others", description = "commands.giveChar.description") public final class GiveCharCommand implements CommandHandler { @Override public void execute(Player sender, Player targetPlayer, List args) { - if (targetPlayer == null) { - CommandHandler.sendMessage(sender, translate("commands.execution.need_target")); - return; - } - int avatarId; int level = 1; @@ -32,7 +26,7 @@ public final class GiveCharCommand implements CommandHandler { level = Integer.parseInt(args.get(1)); } catch (NumberFormatException ignored) { // TODO: Parse from avatar name using GM Handbook. - CommandHandler.sendMessage(sender, translate("commands.execution.invalid.avatarLevel")); + CommandHandler.sendMessage(sender, translate(sender, "commands.generic.invalid.avatarLevel")); return; } // Cheeky fall-through to parse first argument too case 1: @@ -40,33 +34,34 @@ public final class GiveCharCommand implements CommandHandler { avatarId = Integer.parseInt(args.get(0)); } catch (NumberFormatException ignored) { // TODO: Parse from avatar name using GM Handbook. - CommandHandler.sendMessage(sender, translate("commands.execution.invalid.avatarId")); + CommandHandler.sendMessage(sender, translate(sender, "commands.generic.invalid.avatarId")); return; } break; default: - CommandHandler.sendMessage(sender, translate("commands.giveChar.usage")); + CommandHandler.sendMessage(sender, translate(sender, "commands.giveChar.usage")); return; } AvatarData avatarData = GameData.getAvatarDataMap().get(avatarId); if (avatarData == null) { - CommandHandler.sendMessage(sender, translate("commands.execution.invalid.avatarId")); + CommandHandler.sendMessage(sender, translate(sender, "commands.generic.invalid.avatarId")); return; } // Check level. if (level > 90) { - CommandHandler.sendMessage(sender, translate("commands.execution.invalid.avatarLevel")); + CommandHandler.sendMessage(sender, translate(sender, "commands.generic.invalid.avatarLevel")); return; } // Calculate ascension level. int ascension; if (level <= 40) { - ascension = (int) Math.ceil(level / 20f); + ascension = (int) Math.ceil(level / 20f) - 1; } else { ascension = (int) Math.ceil(level / 10f) - 3; + ascension = Math.min(ascension, 6); } Avatar avatar = new Avatar(avatarId); @@ -77,6 +72,6 @@ public final class GiveCharCommand implements CommandHandler { avatar.recalcStats(); targetPlayer.addAvatar(avatar); - CommandHandler.sendMessage(sender, translate("commands.giveChar.given", Integer.toString(avatarId), Integer.toString(level), Integer.toString(targetPlayer.getUid()))); + CommandHandler.sendMessage(sender, translate(sender, "commands.giveChar.given", Integer.toString(avatarId), Integer.toString(level), Integer.toString(targetPlayer.getUid()))); } } diff --git a/src/main/java/emu/grasscutter/command/commands/GiveCommand.java b/src/main/java/emu/grasscutter/command/commands/GiveCommand.java index 2f020f7b3..dbe02a1cd 100644 --- a/src/main/java/emu/grasscutter/command/commands/GiveCommand.java +++ b/src/main/java/emu/grasscutter/command/commands/GiveCommand.java @@ -1,10 +1,9 @@ package emu.grasscutter.command.commands; -import emu.grasscutter.Grasscutter; import emu.grasscutter.command.Command; import emu.grasscutter.command.CommandHandler; import emu.grasscutter.data.GameData; -import emu.grasscutter.data.def.ItemData; +import emu.grasscutter.data.excels.ItemData; import emu.grasscutter.game.inventory.GameItem; import emu.grasscutter.game.inventory.ItemType; import emu.grasscutter.game.player.Player; @@ -12,13 +11,13 @@ import emu.grasscutter.game.props.ActionReason; import java.util.LinkedList; import java.util.List; -import java.util.regex.Pattern; import java.util.regex.Matcher; +import java.util.regex.Pattern; import static emu.grasscutter.utils.Language.translate; -@Command(label = "give", usage = "give [amount] [level]", description = "Gives an item to you or the specified player", aliases = { - "g", "item", "giveitem"}, permission = "player.give") +@Command(label = "give", usage = "give [amount] [level]", aliases = { + "g", "item", "giveitem"}, permission = "player.give", permissionTargeted = "player.give.others", description = "commands.give.description") public final class GiveCommand implements CommandHandler { Pattern lvlRegex = Pattern.compile("l(?:vl?)?(\\d+)"); // Java is a joke of a proglang that doesn't have raw string literals Pattern refineRegex = Pattern.compile("r(\\d+)"); @@ -34,10 +33,6 @@ public final class GiveCommand implements CommandHandler { @Override public void execute(Player sender, Player targetPlayer, List args) { - if (targetPlayer == null) { - CommandHandler.sendMessage(sender, translate("commands.execution.need_target")); - return; - } int item; int lvl = 1; int amount = 1; @@ -69,21 +64,21 @@ public final class GiveCommand implements CommandHandler { try { refinement = Integer.parseInt(args.get(3)); } catch (NumberFormatException ignored) { - CommandHandler.sendMessage(sender, translate("commands.generic.invalid.itemRefinement")); + CommandHandler.sendMessage(sender, translate(sender, "commands.generic.invalid.itemRefinement")); return; } // Fallthrough case 3: // [amount] [level] try { lvl = Integer.parseInt(args.get(2)); } catch (NumberFormatException ignored) { - CommandHandler.sendMessage(sender, translate("commands.generic.invalid.itemLevel")); + CommandHandler.sendMessage(sender, translate(sender, "commands.generic.invalid.itemLevel")); return; } // Fallthrough case 2: // [amount] try { amount = Integer.parseInt(args.get(1)); } catch (NumberFormatException ignored) { - CommandHandler.sendMessage(sender, translate("commands.generic.invalid.amount")); + CommandHandler.sendMessage(sender, translate(sender, "commands.generic.invalid.amount")); return; } // Fallthrough case 1: // @@ -91,28 +86,28 @@ public final class GiveCommand implements CommandHandler { item = Integer.parseInt(args.get(0)); } catch (NumberFormatException ignored) { // TODO: Parse from item name using GM Handbook. - CommandHandler.sendMessage(sender, translate("commands.generic.invalid.itemId")); + CommandHandler.sendMessage(sender, translate(sender, "commands.generic.invalid.itemId")); return; } break; default: // *No args* - CommandHandler.sendMessage(sender, translate("commands.give.usage")); + CommandHandler.sendMessage(sender, translate(sender, "commands.give.usage")); return; } ItemData itemData = GameData.getItemDataMap().get(item); if (itemData == null) { - CommandHandler.sendMessage(sender, translate("commands.generic.invalid.itemId")); + CommandHandler.sendMessage(sender, translate(sender, "commands.generic.invalid.itemId")); return; } if (refinement != 0) { if (itemData.getItemType() == ItemType.ITEM_WEAPON) { if (refinement < 1 || refinement > 5) { - CommandHandler.sendMessage(sender, translate("commands.give.refinement_must_between_1_and_5")); + CommandHandler.sendMessage(sender, translate(sender, "commands.give.refinement_must_between_1_and_5")); return; } } else { - CommandHandler.sendMessage(sender, translate("commands.give.refinement_only_applicable_weapons")); + CommandHandler.sendMessage(sender, translate(sender, "commands.give.refinement_only_applicable_weapons")); return; } } @@ -120,11 +115,11 @@ public final class GiveCommand implements CommandHandler { this.item(targetPlayer, itemData, amount, lvl, refinement); if (!itemData.isEquip()) { - CommandHandler.sendMessage(sender, translate("commands.give.given", Integer.toString(amount), Integer.toString(item), Integer.toString(targetPlayer.getUid()))); + CommandHandler.sendMessage(sender, translate(sender, "commands.give.given", Integer.toString(amount), Integer.toString(item), Integer.toString(targetPlayer.getUid()))); } else if (itemData.getItemType() == ItemType.ITEM_WEAPON) { - CommandHandler.sendMessage(sender, translate("commands.give.given_with_level_and_refinement", Integer.toString(item), Integer.toString(lvl), Integer.toString(refinement), Integer.toString(amount), Integer.toString(targetPlayer.getUid()))); + CommandHandler.sendMessage(sender, translate(sender, "commands.give.given_with_level_and_refinement", Integer.toString(item), Integer.toString(lvl), Integer.toString(refinement), Integer.toString(amount), Integer.toString(targetPlayer.getUid()))); } else { - CommandHandler.sendMessage(sender, translate("commands.give.given_level", Integer.toString(item), Integer.toString(lvl), Integer.toString(amount))); + CommandHandler.sendMessage(sender, translate(sender, "commands.give.given_level", Integer.toString(item), Integer.toString(lvl), Integer.toString(amount), Integer.toString(targetPlayer.getUid()))); } } diff --git a/src/main/java/emu/grasscutter/command/commands/GodModeCommand.java b/src/main/java/emu/grasscutter/command/commands/GodModeCommand.java index 9abebb8db..0b635b897 100644 --- a/src/main/java/emu/grasscutter/command/commands/GodModeCommand.java +++ b/src/main/java/emu/grasscutter/command/commands/GodModeCommand.java @@ -1,6 +1,5 @@ package emu.grasscutter.command.commands; -import emu.grasscutter.Grasscutter; import emu.grasscutter.command.Command; import emu.grasscutter.command.CommandHandler; import emu.grasscutter.game.player.Player; @@ -9,17 +8,11 @@ import java.util.List; import static emu.grasscutter.utils.Language.translate; -@Command(label = "godmode", usage = "godmode [on|off|toggle]", - description = "Prevents you from taking damage. Defaults to toggle.", permission = "player.godmode") +@Command(label = "godmode", usage = "godmode [on|off|toggle]", permission = "player.godmode", permissionTargeted = "player.godmode.others", description = "commands.godmode.description") public final class GodModeCommand implements CommandHandler { @Override public void execute(Player sender, Player targetPlayer, List args) { - if (targetPlayer == null) { - CommandHandler.sendMessage(sender, translate("commands.execution.need_target")); - return; - } - boolean enabled = !targetPlayer.inGodmode(); if (args.size() == 1) { switch (args.get(0).toLowerCase()) { @@ -37,6 +30,6 @@ public final class GodModeCommand implements CommandHandler { } targetPlayer.setGodmode(enabled); - CommandHandler.sendMessage(sender, translate("commands.godmode.success", (enabled ? translate("commands.status.enabled") : translate("commands.status.disabled")), targetPlayer.getNickname())); + CommandHandler.sendMessage(sender, translate(sender, "commands.godmode.success", (enabled ? translate(sender, "commands.status.enabled") : translate(sender, "commands.status.disabled")), targetPlayer.getNickname())); } } diff --git a/src/main/java/emu/grasscutter/command/commands/HealCommand.java b/src/main/java/emu/grasscutter/command/commands/HealCommand.java index bb0b861b0..56354d676 100644 --- a/src/main/java/emu/grasscutter/command/commands/HealCommand.java +++ b/src/main/java/emu/grasscutter/command/commands/HealCommand.java @@ -11,16 +11,11 @@ import java.util.List; import static emu.grasscutter.utils.Language.translate; -@Command(label = "heal", usage = "heal|h", aliases = {"h"}, - description = "Heal all characters in your current team.", permission = "player.heal") +@Command(label = "heal", usage = "heal|h", aliases = {"h"}, permission = "player.heal", permissionTargeted = "player.heal.others", description = "commands.heal.description") public final class HealCommand implements CommandHandler { + @Override public void execute(Player sender, Player targetPlayer, List args) { - if (targetPlayer == null) { - CommandHandler.sendMessage(sender, translate("commands.execution.need_target")); - return; - } - targetPlayer.getTeamManager().getActiveTeam().forEach(entity -> { boolean isAlive = entity.isAlive(); entity.setFightProperty( @@ -32,6 +27,6 @@ public final class HealCommand implements CommandHandler { entity.getWorld().broadcastPacket(new PacketAvatarLifeStateChangeNotify(entity.getAvatar())); } }); - CommandHandler.sendMessage(sender, translate("commands.heal.success")); + CommandHandler.sendMessage(sender, translate(sender, "commands.heal.success")); } } diff --git a/src/main/java/emu/grasscutter/command/commands/HelpCommand.java b/src/main/java/emu/grasscutter/command/commands/HelpCommand.java index 93ac831b3..431890fda 100644 --- a/src/main/java/emu/grasscutter/command/commands/HelpCommand.java +++ b/src/main/java/emu/grasscutter/command/commands/HelpCommand.java @@ -10,8 +10,7 @@ import java.util.*; import static emu.grasscutter.utils.Language.translate; -@Command(label = "help", usage = "help [command]", - description = "Sends the help message or shows information about a specified command") +@Command(label = "help", usage = "help [command]", description = "commands.help.description", targetRequirement = Command.TargetRequirement.NONE) public final class HelpCommand implements CommandHandler { @Override @@ -33,16 +32,16 @@ public final class HelpCommand implements CommandHandler { } else { String command = args.get(0); CommandHandler handler = CommandMap.getInstance().getHandler(command); - StringBuilder builder = new StringBuilder(player == null ? "\n" + translate("commands.status.help") + " - " : translate("commands.status.help") + " - ").append(command).append(": \n"); + StringBuilder builder = new StringBuilder(player == null ? "\n" + translate(player, "commands.status.help") + " - " : translate(player, "commands.status.help") + " - ").append(command).append(": \n"); if (handler == null) { - builder.append(translate("commands.generic.command_exist_error")); + builder.append(translate(player, "commands.generic.command_exist_error")); } else { Command annotation = handler.getClass().getAnnotation(Command.class); - builder.append(" ").append(annotation.description()).append("\n"); - builder.append(translate("commands.help.usage")).append(annotation.usage()); + builder.append(" ").append(translate(player, annotation.description())).append("\n"); + builder.append(translate(player, "commands.help.usage")).append(annotation.usage()); if (annotation.aliases().length >= 1) { - builder.append("\n").append(translate("commands.help.aliases")); + builder.append("\n").append(translate(player, "commands.help.aliases")); for (String alias : annotation.aliases()) { builder.append(alias).append(" "); } @@ -58,13 +57,13 @@ public final class HelpCommand implements CommandHandler { void SendAllHelpMessage(Player player, List annotations) { if (player == null) { - StringBuilder builder = new StringBuilder("\n" + translate("commands.help.available_commands") + "\n"); + StringBuilder builder = new StringBuilder("\n" + translate(player, "commands.help.available_commands") + "\n"); annotations.forEach(annotation -> { builder.append(annotation.label()).append("\n"); - builder.append(" ").append(annotation.description()).append("\n"); - builder.append(translate("commands.help.usage")).append(annotation.usage()); + builder.append(" ").append(translate(player, annotation.description())).append("\n"); + builder.append(translate(player, "commands.help.usage")).append(annotation.usage()); if (annotation.aliases().length >= 1) { - builder.append("\n").append(translate("commands.help.aliases")); + builder.append("\n").append(translate(player, "commands.help.aliases")); for (String alias : annotation.aliases()) { builder.append(alias).append(" "); } @@ -75,13 +74,13 @@ public final class HelpCommand implements CommandHandler { CommandHandler.sendMessage(null, builder.toString()); } else { - CommandHandler.sendMessage(player, translate("commands.help.available_commands")); + CommandHandler.sendMessage(player, translate(player, "commands.help.available_commands")); annotations.forEach(annotation -> { StringBuilder builder = new StringBuilder(annotation.label()).append("\n"); - builder.append(" ").append(annotation.description()).append("\n"); - builder.append(translate("commands.help.usage")).append(annotation.usage()); + builder.append(" ").append(translate(player, annotation.description())).append("\n"); + builder.append(translate(player, "commands.help.usage")).append(annotation.usage()); if (annotation.aliases().length >= 1) { - builder.append("\n").append(translate("commands.help.aliases")); + builder.append("\n").append(translate(player, "commands.help.aliases")); for (String alias : annotation.aliases()) { builder.append(alias).append(" "); } diff --git a/src/main/java/emu/grasscutter/command/commands/KickCommand.java b/src/main/java/emu/grasscutter/command/commands/KickCommand.java index 270e28150..546fe905d 100644 --- a/src/main/java/emu/grasscutter/command/commands/KickCommand.java +++ b/src/main/java/emu/grasscutter/command/commands/KickCommand.java @@ -8,23 +8,17 @@ import java.util.List; import static emu.grasscutter.utils.Language.translate; -@Command(label = "kick", usage = "kick", - description = "Kicks the specified player from the server (WIP)", permission = "server.kick") +@Command(label = "kick", usage = "kick", permission = "server.kick", description = "commands.kick.description") public final class KickCommand implements CommandHandler { @Override public void execute(Player sender, Player targetPlayer, List args) { - if (targetPlayer == null) { - CommandHandler.sendMessage(sender, translate("commands.execution.need_target")); - return; - } - if (sender != null) { - CommandHandler.sendMessage(sender, translate("commands.kick.player_kick_player", - Integer.toString(sender.getAccount().getPlayerUid()), sender.getAccount().getUsername(), + CommandHandler.sendMessage(sender, translate(sender, "commands.kick.player_kick_player", + Integer.toString(sender.getUid()), sender.getAccount().getUsername(), Integer.toString(targetPlayer.getUid()), targetPlayer.getAccount().getUsername())); } else { - CommandHandler.sendMessage(null, translate("commands.kick.server_kick_player", Integer.toString(targetPlayer.getUid()), targetPlayer.getAccount().getUsername())); + CommandHandler.sendMessage(null, translate(sender, "commands.kick.server_kick_player", Integer.toString(targetPlayer.getUid()), targetPlayer.getAccount().getUsername())); } targetPlayer.getSession().close(); diff --git a/src/main/java/emu/grasscutter/command/commands/KillAllCommand.java b/src/main/java/emu/grasscutter/command/commands/KillAllCommand.java index 423c60bbd..6361a726f 100644 --- a/src/main/java/emu/grasscutter/command/commands/KillAllCommand.java +++ b/src/main/java/emu/grasscutter/command/commands/KillAllCommand.java @@ -12,17 +12,11 @@ import java.util.List; import static emu.grasscutter.utils.Language.translate; -@Command(label = "killall", usage = "killall [sceneId]", - description = "Kill all entities", permission = "server.killall") +@Command(label = "killall", usage = "killall [sceneId]", permission = "server.killall", permissionTargeted = "server.killall.others", description = "commands.killall.description") public final class KillAllCommand implements CommandHandler { @Override public void execute(Player sender, Player targetPlayer, List args) { - if (targetPlayer == null) { - CommandHandler.sendMessage(sender, translate("commands.execution.need_target")); - return; - } - Scene scene = targetPlayer.getScene(); try { switch (args.size()) { @@ -32,14 +26,14 @@ public final class KillAllCommand implements CommandHandler { scene = targetPlayer.getWorld().getSceneById(Integer.parseInt(args.get(0))); break; default: - CommandHandler.sendMessage(sender, translate("commands.kill.usage")); + CommandHandler.sendMessage(sender, translate(sender, "commands.killall.usage")); return; } } catch (NumberFormatException ignored) { - CommandHandler.sendMessage(sender, translate("commands.execution.argument_error")); + CommandHandler.sendMessage(sender, translate(sender, "commands.execution.argument_error")); } if (scene == null) { - CommandHandler.sendMessage(sender, translate("commands.kill.scene_not_found_in_player_world")); + CommandHandler.sendMessage(sender, translate(sender, "commands.killall.scene_not_found_in_player_world")); return; } @@ -49,6 +43,6 @@ public final class KillAllCommand implements CommandHandler { .filter(entity -> entity instanceof EntityMonster) .toList(); toKill.forEach(entity -> sceneF.killEntity(entity, 0)); - CommandHandler.sendMessage(sender, translate("commands.kill.kill_monsters_in_scene", Integer.toString(toKill.size()), Integer.toString(scene.getId()))); + CommandHandler.sendMessage(sender, translate(sender, "commands.killall.kill_monsters_in_scene", Integer.toString(toKill.size()), Integer.toString(scene.getId()))); } } diff --git a/src/main/java/emu/grasscutter/command/commands/KillCharacterCommand.java b/src/main/java/emu/grasscutter/command/commands/KillCharacterCommand.java index f1e0f0f8c..9f811373d 100644 --- a/src/main/java/emu/grasscutter/command/commands/KillCharacterCommand.java +++ b/src/main/java/emu/grasscutter/command/commands/KillCharacterCommand.java @@ -13,14 +13,13 @@ import java.util.List; import static emu.grasscutter.utils.Language.translate; -@Command(label = "killcharacter", usage = "killcharacter", aliases = {"suicide", "kill"}, - description = "Kills the players current character", permission = "player.killcharacter") +@Command(label = "killcharacter", usage = "killcharacter", aliases = {"suicide", "kill"}, permission = "player.killcharacter", permissionTargeted = "player.killcharacter.others", description = "commands.killCharacter.description") public final class KillCharacterCommand implements CommandHandler { @Override public void execute(Player sender, Player targetPlayer, List args) { - if (targetPlayer == null) { - CommandHandler.sendMessage(sender, translate("commands.execution.need_target")); + if (args.isEmpty()) { + CommandHandler.sendMessage(sender, translate(sender, "commands.killCharacter.usage")); return; } @@ -33,6 +32,6 @@ public final class KillCharacterCommand implements CommandHandler { targetPlayer.getScene().removeEntity(entity); entity.onDeath(0); - CommandHandler.sendMessage(sender, translate("commands.killCharacter.success", targetPlayer.getNickname())); + CommandHandler.sendMessage(sender, translate(sender, "commands.killCharacter.success", targetPlayer.getNickname())); } } diff --git a/src/main/java/emu/grasscutter/command/commands/LanguageCommand.java b/src/main/java/emu/grasscutter/command/commands/LanguageCommand.java new file mode 100644 index 000000000..bd5c6f3ec --- /dev/null +++ b/src/main/java/emu/grasscutter/command/commands/LanguageCommand.java @@ -0,0 +1,57 @@ +package emu.grasscutter.command.commands; + +import emu.grasscutter.Grasscutter; +import emu.grasscutter.command.Command; +import emu.grasscutter.command.CommandHandler; +import emu.grasscutter.database.DatabaseHelper; +import emu.grasscutter.game.Account; +import emu.grasscutter.game.player.Player; +import emu.grasscutter.utils.Utils; + +import java.util.List; +import java.util.Locale; + +import static emu.grasscutter.utils.Language.translate; + +@Command(label = "language", usage = "language [language code]", description = "commands.language.description", aliases = {"lang"}, targetRequirement = Command.TargetRequirement.NONE) +public final class LanguageCommand implements CommandHandler { + + @Override + public void execute(Player sender, Player targetPlayer, List args) { + if (args.isEmpty()) { + String curLangCode = null; + if (sender != null) { + curLangCode = Utils.getLanguageCode(sender.getAccount().getLocale()); + } + else { + curLangCode = Grasscutter.getLanguage().getLanguageCode(); + } + CommandHandler.sendMessage(sender, translate(sender, "commands.language.current_language", curLangCode)); + return; + } + + String langCode = args.get(0); + + var languageInst = Grasscutter.getLanguage(langCode); + var actualLangCode = languageInst.getLanguageCode(); + var locale = Locale.forLanguageTag(actualLangCode); + if (sender != null) { + var account = sender.getAccount(); + account.setLocale(locale); + account.save(); + } + else { + Grasscutter.setLanguage(languageInst); + var config = Grasscutter.getConfig(); + config.language.language = locale; + Grasscutter.saveConfig(config); + } + + if (!langCode.equals(actualLangCode)) { + CommandHandler.sendMessage(sender, translate(sender, "commands.language.language_not_found", langCode)); + } + + CommandHandler.sendMessage(sender, translate(sender, "commands.language.language_changed", actualLangCode)); + + } +} diff --git a/src/main/java/emu/grasscutter/command/commands/ListCommand.java b/src/main/java/emu/grasscutter/command/commands/ListCommand.java index bc35e65e1..1e6906cc8 100644 --- a/src/main/java/emu/grasscutter/command/commands/ListCommand.java +++ b/src/main/java/emu/grasscutter/command/commands/ListCommand.java @@ -10,8 +10,7 @@ import java.util.Map; import static emu.grasscutter.utils.Language.translate; -@Command(label = "list", usage = "list [uid]", - description = "List online players", aliases = {"players"}) +@Command(label = "list", usage = "list [uid]", aliases = {"players"}, description = "commands.list.description", targetRequirement = Command.TargetRequirement.NONE) public final class ListCommand implements CommandHandler { @Override @@ -23,7 +22,7 @@ public final class ListCommand implements CommandHandler { needUID = args.get(0).equals("uid"); } - CommandHandler.sendMessage(sender, translate("commands.list.success", Integer.toString(playersMap.size()))); + CommandHandler.sendMessage(sender, translate(sender, "commands.list.success", Integer.toString(playersMap.size()))); if (playersMap.size() != 0) { StringBuilder playerSet = new StringBuilder(); diff --git a/src/main/java/emu/grasscutter/command/commands/NoStaminaCommand.java b/src/main/java/emu/grasscutter/command/commands/NoStaminaCommand.java new file mode 100644 index 000000000..4a1ef8873 --- /dev/null +++ b/src/main/java/emu/grasscutter/command/commands/NoStaminaCommand.java @@ -0,0 +1,34 @@ +package emu.grasscutter.command.commands; + +import emu.grasscutter.command.Command; +import emu.grasscutter.command.CommandHandler; +import emu.grasscutter.game.player.Player; + +import java.util.List; + +import static emu.grasscutter.utils.Language.translate; + + +@Command(label = "nostamina", usage = "nostamina [on|off|toggle]", aliases = {"ns"}, permission = "player.nostamina", permissionTargeted = "player.nostamina.others", description = "commands.nostamina.description") +public final class NoStaminaCommand implements CommandHandler { + @Override + public void execute(Player sender, Player targetPlayer, List args) { + boolean stamina = !targetPlayer.getStamina(); + if (args.size() == 1) { + switch (args.get(0).toLowerCase()) { + case "on": + stamina = true; + break; + case "off": + stamina = false; + break; + default: + // toggled + break; + } + } + targetPlayer.setStamina(stamina); //Set + + CommandHandler.sendMessage(sender, translate(sender, "commands.nostamina.success", (stamina ? translate(sender, "commands.status.enabled") : translate(sender, "commands.status.disabled")), targetPlayer.getNickname())); + } +} diff --git a/src/main/java/emu/grasscutter/command/commands/PermissionCommand.java b/src/main/java/emu/grasscutter/command/commands/PermissionCommand.java index 69c8ce899..8efc8de43 100644 --- a/src/main/java/emu/grasscutter/command/commands/PermissionCommand.java +++ b/src/main/java/emu/grasscutter/command/commands/PermissionCommand.java @@ -10,19 +10,13 @@ import java.util.List; import static emu.grasscutter.utils.Language.translate; -@Command(label = "permission", usage = "permission ", - description = "Grants or removes a permission for a user", permission = "*") +@Command(label = "permission", usage = "permission ", permission = "permission", description = "commands.permission.description") public final class PermissionCommand implements CommandHandler { @Override public void execute(Player sender, Player targetPlayer, List args) { - if (targetPlayer == null) { - CommandHandler.sendMessage(sender, translate("commands.execution.need_target")); - return; - } - if (args.size() != 2) { - CommandHandler.sendMessage(sender, translate("commands.permission.usage")); + CommandHandler.sendMessage(sender, translate(sender, "commands.permission.usage")); return; } @@ -31,26 +25,26 @@ public final class PermissionCommand implements CommandHandler { Account account = targetPlayer.getAccount(); if (account == null) { - CommandHandler.sendMessage(sender, translate("commands.permission.account_error")); + CommandHandler.sendMessage(sender, translate(sender, "commands.permission.account_error")); return; } switch (action) { default: - CommandHandler.sendMessage(sender, translate("commands.permission.usage")); + CommandHandler.sendMessage(sender, translate(sender, "commands.permission.usage")); break; case "add": if (account.addPermission(permission)) { - CommandHandler.sendMessage(sender, translate("commands.permission.add")); - } else CommandHandler.sendMessage(sender, translate("commands.permission.has_error")); + CommandHandler.sendMessage(sender, translate(sender, "commands.permission.add")); + } else CommandHandler.sendMessage(sender, translate(sender, "commands.permission.has_error")); break; case "remove": if (account.removePermission(permission)) { - CommandHandler.sendMessage(sender, translate("commands.permission.remove")); - } else CommandHandler.sendMessage(sender, translate("commands.permission.not_have_error")); + CommandHandler.sendMessage(sender, translate(sender, "commands.permission.remove")); + } else CommandHandler.sendMessage(sender, translate(sender, "commands.permission.not_have_error")); break; } account.save(); } -} \ No newline at end of file +} diff --git a/src/main/java/emu/grasscutter/command/commands/PositionCommand.java b/src/main/java/emu/grasscutter/command/commands/PositionCommand.java index 7f6548c5b..221afe801 100644 --- a/src/main/java/emu/grasscutter/command/commands/PositionCommand.java +++ b/src/main/java/emu/grasscutter/command/commands/PositionCommand.java @@ -9,19 +9,13 @@ import java.util.List; import static emu.grasscutter.utils.Language.translate; -@Command(label = "position", usage = "position", aliases = {"pos"}, - description = "Get coordinates.") +@Command(label = "position", usage = "position", aliases = {"pos"}, description = "commands.position.description") public final class PositionCommand implements CommandHandler { @Override public void execute(Player sender, Player targetPlayer, List args) { - if (targetPlayer == null) { - CommandHandler.sendMessage(sender, translate("commands.execution.need_target")); - return; - } - Position pos = targetPlayer.getPos(); - CommandHandler.sendMessage(sender, translate("commands.position.success", + CommandHandler.sendMessage(sender, translate(sender, "commands.position.success", Float.toString(pos.getX()), Float.toString(pos.getY()), Float.toString(pos.getZ()), Integer.toString(targetPlayer.getSceneId()))); } diff --git a/src/main/java/emu/grasscutter/command/commands/QuestCommand.java b/src/main/java/emu/grasscutter/command/commands/QuestCommand.java new file mode 100644 index 000000000..f9a555149 --- /dev/null +++ b/src/main/java/emu/grasscutter/command/commands/QuestCommand.java @@ -0,0 +1,61 @@ +package emu.grasscutter.command.commands; + +import emu.grasscutter.Grasscutter; +import emu.grasscutter.command.Command; +import emu.grasscutter.command.CommandHandler; +import emu.grasscutter.game.player.Player; +import emu.grasscutter.game.quest.GameQuest; + +import java.util.List; + +import static emu.grasscutter.utils.Language.translate; + +@Command(label = "quest", usage = "quest [questId]", permission = "player.quest", permissionTargeted = "player.quest.others", description = "commands.quest.description") +public final class QuestCommand implements CommandHandler { + + @Override + public void execute(Player sender, Player targetPlayer, List args) { + if (args.size() != 2) { + CommandHandler.sendMessage(sender, translate(sender, "commands.quest.usage")); + return; + } + + String cmd = args.get(0).toLowerCase(); + int questId; + + try { + questId = Integer.parseInt(args.get(1)); + } catch (Exception e) { + CommandHandler.sendMessage(sender, translate(sender, "commands.quest.invalid_id")); + return; + } + + switch (cmd) { + case "add" -> { + GameQuest quest = targetPlayer.getQuestManager().addQuest(questId); + + if (quest != null) { + CommandHandler.sendMessage(sender, translate(sender, "commands.quest.added", questId)); + return; + } + + CommandHandler.sendMessage(sender, translate(sender, "commands.quest.not_found")); + } + case "finish" -> { + GameQuest quest = targetPlayer.getQuestManager().getQuestById(questId); + + if (quest == null) { + CommandHandler.sendMessage(sender, translate(sender, "commands.quest.not_found")); + return; + } + + quest.finish(); + + CommandHandler.sendMessage(sender, translate(sender, "commands.quest.finished", questId)); + } + default -> { + CommandHandler.sendMessage(sender, translate(sender, "commands.quest.usage")); + } + } + } +} diff --git a/src/main/java/emu/grasscutter/command/commands/ReloadCommand.java b/src/main/java/emu/grasscutter/command/commands/ReloadCommand.java index 6c85d2024..d18567f9b 100644 --- a/src/main/java/emu/grasscutter/command/commands/ReloadCommand.java +++ b/src/main/java/emu/grasscutter/command/commands/ReloadCommand.java @@ -9,21 +9,19 @@ import java.util.List; import static emu.grasscutter.utils.Language.translate; -@Command(label = "reload", usage = "reload", - description = "Reload server config", permission = "server.reload") +@Command(label = "reload", usage = "reload", permission = "server.reload", description = "commands.reload.description", targetRequirement = Command.TargetRequirement.NONE) public final class ReloadCommand implements CommandHandler { @Override public void execute(Player sender, Player targetPlayer, List args) { - CommandHandler.sendMessage(sender, translate("commands.reload.reload_start")); + CommandHandler.sendMessage(sender, translate(sender, "commands.reload.reload_start")); Grasscutter.loadConfig(); Grasscutter.loadLanguage(); Grasscutter.getGameServer().getGachaManager().load(); Grasscutter.getGameServer().getDropManager().load(); Grasscutter.getGameServer().getShopManager().load(); - Grasscutter.getDispatchServer().loadQueries(); - CommandHandler.sendMessage(sender, translate("commands.reload.reload_done")); + CommandHandler.sendMessage(sender, translate(sender, "commands.reload.reload_done")); } } diff --git a/src/main/java/emu/grasscutter/command/commands/ResetConstCommand.java b/src/main/java/emu/grasscutter/command/commands/ResetConstCommand.java index 706fb95e0..0bca27768 100644 --- a/src/main/java/emu/grasscutter/command/commands/ResetConstCommand.java +++ b/src/main/java/emu/grasscutter/command/commands/ResetConstCommand.java @@ -11,20 +11,14 @@ import java.util.List; import static emu.grasscutter.utils.Language.translate; @Command(label = "resetconst", usage = "resetconst [all]", - description = "Resets the constellation level on your current active character, will need to relog after using the command to see any changes.", - aliases = {"resetconstellation"}, permission = "player.resetconstellation") + aliases = {"resetconstellation"}, permission = "player.resetconstellation", permissionTargeted = "player.resetconstellation.others", description = "commands.resetConst.description") public final class ResetConstCommand implements CommandHandler { @Override public void execute(Player sender, Player targetPlayer, List args) { - if (targetPlayer == null) { - CommandHandler.sendMessage(sender, translate("commands.execution.need_target")); - return; - } - if (args.size() > 0 && args.get(0).equalsIgnoreCase("all")) { targetPlayer.getAvatars().forEach(this::resetConstellation); - CommandHandler.sendMessage(sender, translate("commands.resetConst.reset_all")); + CommandHandler.sendMessage(sender, translate(sender, "commands.resetConst.reset_all")); } else { EntityAvatar entity = targetPlayer.getTeamManager().getCurrentAvatarEntity(); if (entity == null) { @@ -34,7 +28,7 @@ public final class ResetConstCommand implements CommandHandler { Avatar avatar = entity.getAvatar(); this.resetConstellation(avatar); - CommandHandler.sendMessage(sender, translate("commands.resetConst.success", avatar.getAvatarData().getName())); + CommandHandler.sendMessage(sender, translate(sender, "commands.resetConst.success", avatar.getAvatarData().getName())); } } @@ -44,4 +38,4 @@ public final class ResetConstCommand implements CommandHandler { avatar.recalcStats(); avatar.save(); } -} \ No newline at end of file +} diff --git a/src/main/java/emu/grasscutter/command/commands/ResetShopLimitCommand.java b/src/main/java/emu/grasscutter/command/commands/ResetShopLimitCommand.java index aeae0abbf..51695b551 100644 --- a/src/main/java/emu/grasscutter/command/commands/ResetShopLimitCommand.java +++ b/src/main/java/emu/grasscutter/command/commands/ResetShopLimitCommand.java @@ -9,18 +9,18 @@ import java.util.List; import static emu.grasscutter.utils.Language.translate; -@Command(label = "resetshop", usage = "resetshop", - description = "Reset target player's shop refresh time.", permission = "server.resetshop") +@Command(label = "resetshop", usage = "resetshop", permission = "server.resetshop", permissionTargeted = "server.resetshop.others", description = "commands.resetShopLimit.description") public final class ResetShopLimitCommand implements CommandHandler { + @Override public void execute(Player sender, Player targetPlayer, List args) { - if (targetPlayer == null) { - CommandHandler.sendMessage(sender, translate("commands.execution.need_target")); + if (args.isEmpty()) { + CommandHandler.sendMessage(sender, translate(sender, "commands.resetShopLimit.usage")); return; } targetPlayer.getShopLimit().forEach(x -> x.setNextRefreshTime(0)); targetPlayer.save(); - CommandHandler.sendMessage(sender, translate("commands.status.success")); + CommandHandler.sendMessage(sender, translate(sender, "commands.resetShopLimit.success")); } } diff --git a/src/main/java/emu/grasscutter/command/commands/RestartCommand.java b/src/main/java/emu/grasscutter/command/commands/RestartCommand.java index e3b8b2747..3f73b6f3f 100644 --- a/src/main/java/emu/grasscutter/command/commands/RestartCommand.java +++ b/src/main/java/emu/grasscutter/command/commands/RestartCommand.java @@ -6,7 +6,9 @@ import emu.grasscutter.game.player.Player; import java.util.List; -@Command(label = "restart", usage = "restart - Restarts the current session") +import static emu.grasscutter.utils.Language.translate; + +@Command(label = "restart", usage = "restart", description = "commands.restart.description", targetRequirement = Command.TargetRequirement.NONE) public final class RestartCommand implements CommandHandler { @Override diff --git a/src/main/java/emu/grasscutter/command/commands/SendMailCommand.java b/src/main/java/emu/grasscutter/command/commands/SendMailCommand.java index 838bea567..c4369fa86 100644 --- a/src/main/java/emu/grasscutter/command/commands/SendMailCommand.java +++ b/src/main/java/emu/grasscutter/command/commands/SendMailCommand.java @@ -13,8 +13,7 @@ import java.util.List; import static emu.grasscutter.utils.Language.translate; @SuppressWarnings("ConstantConditions") -@Command(label = "sendmail", usage = "sendmail [templateId]", - description = "Sends mail to the specified user. The usage of this command changes based on it's composition state.", permission = "server.sendmail") +@Command(label = "sendmail", usage = "sendmail [templateId]", permission = "server.sendmail", description = "commands.sendMail.description", targetRequirement = Command.TargetRequirement.NONE) public final class SendMailCommand implements CommandHandler { // TODO: You should be able to do /sendmail and then just send subsequent messages until you finish @@ -40,24 +39,24 @@ public final class SendMailCommand implements CommandHandler { MailBuilder mailBuilder; switch (args.get(0).toLowerCase()) { case "help" -> { - CommandHandler.sendMessage(sender, this.getClass().getAnnotation(Command.class).description() + "\nUsage: " + this.getClass().getAnnotation(Command.class).usage()); + CommandHandler.sendMessage(sender, translate(sender, "commands.sendMail.usage")); return; } case "all" -> mailBuilder = new MailBuilder(true, new Mail()); default -> { - if (DatabaseHelper.getPlayerById(Integer.parseInt(args.get(0))) != null) { + if (DatabaseHelper.getPlayerByUid(Integer.parseInt(args.get(0))) != null) { mailBuilder = new MailBuilder(Integer.parseInt(args.get(0)), new Mail()); } else { - CommandHandler.sendMessage(sender, translate("commands.sendMail.user_not_exist", args.get(0))); + CommandHandler.sendMessage(sender, translate(sender, "commands.sendMail.user_not_exist", args.get(0))); return; } } } mailBeingConstructed.put(senderId, mailBuilder); - CommandHandler.sendMessage(sender, translate("commands.sendMail.start_composition")); + CommandHandler.sendMessage(sender, translate(sender, "commands.sendMail.start_composition")); } - case 2 -> CommandHandler.sendMessage(sender, translate("commands.sendMail.templates")); - default -> CommandHandler.sendMessage(sender, translate("commands.sendMail.invalid_arguments")); + case 2 -> CommandHandler.sendMessage(sender, translate(sender, "commands.sendMail.templates")); + default -> CommandHandler.sendMessage(sender, translate(sender, "commands.sendMail.invalid_arguments")); } } else { MailBuilder mailBuilder = mailBeingConstructed.get(senderId); @@ -66,28 +65,28 @@ public final class SendMailCommand implements CommandHandler { switch (args.get(0).toLowerCase()) { case "stop" -> { mailBeingConstructed.remove(senderId); - CommandHandler.sendMessage(sender, translate("commands.sendMail.sendCancel")); + CommandHandler.sendMessage(sender, translate(sender, "commands.sendMail.sendCancel")); return; } case "finish" -> { if (mailBuilder.constructionStage == 3) { if (!mailBuilder.sendToAll) { Grasscutter.getGameServer().getPlayerByUid(mailBuilder.recipient, true).sendMail(mailBuilder.mail); - CommandHandler.sendMessage(sender, translate("commands.sendMail.send_done", Integer.toString(mailBuilder.recipient))); + CommandHandler.sendMessage(sender, translate(sender, "commands.sendMail.send_done", Integer.toString(mailBuilder.recipient))); } else { for (Player player : DatabaseHelper.getAllPlayers()) { Grasscutter.getGameServer().getPlayerByUid(player.getUid(), true).sendMail(mailBuilder.mail); } - CommandHandler.sendMessage(sender, translate("commands.sendMail.send_all_done")); + CommandHandler.sendMessage(sender, translate(sender, "commands.sendMail.send_all_done")); } mailBeingConstructed.remove(senderId); } else { - CommandHandler.sendMessage(sender, translate("commands.sendMail.not_composition_end", getConstructionArgs(mailBuilder.constructionStage))); + CommandHandler.sendMessage(sender, translate(sender, "commands.sendMail.not_composition_end", getConstructionArgs(mailBuilder.constructionStage, sender))); } return; } case "help" -> { - CommandHandler.sendMessage(sender, translate("commands.sendMail.please_use", getConstructionArgs(mailBuilder.constructionStage))); + CommandHandler.sendMessage(sender, translate(sender, "commands.sendMail.please_use", getConstructionArgs(mailBuilder.constructionStage, sender))); return; } default -> { @@ -95,19 +94,19 @@ public final class SendMailCommand implements CommandHandler { case 0 -> { String title = String.join(" ", args.subList(0, args.size())); mailBuilder.mail.mailContent.title = title; - CommandHandler.sendMessage(sender, translate("commands.sendMail.set_title", title)); + CommandHandler.sendMessage(sender, translate(sender, "commands.sendMail.set_title", title)); mailBuilder.constructionStage++; } case 1 -> { String contents = String.join(" ", args.subList(0, args.size())); mailBuilder.mail.mailContent.content = contents; - CommandHandler.sendMessage(sender, translate("commands.sendMail.set_contents", contents)); + CommandHandler.sendMessage(sender, translate(sender, "commands.sendMail.set_contents", contents)); mailBuilder.constructionStage++; } case 2 -> { String msgSender = String.join(" ", args.subList(0, args.size())); mailBuilder.mail.mailContent.sender = msgSender; - CommandHandler.sendMessage(sender, translate("commands.sendMail.set_message_sender", msgSender)); + CommandHandler.sendMessage(sender, translate(sender, "commands.sendMail.set_message_sender", msgSender)); mailBuilder.constructionStage++; } case 3 -> { @@ -120,21 +119,21 @@ public final class SendMailCommand implements CommandHandler { try { refinement = Integer.parseInt(args.get(3)); } catch (NumberFormatException ignored) { - CommandHandler.sendMessage(sender, translate("commands.generic.invalid.itemRefinement")); + CommandHandler.sendMessage(sender, translate(sender, "commands.generic.invalid.itemRefinement")); return; } // Fallthrough case 3: // [amount] [level] try { lvl = Integer.parseInt(args.get(2)); } catch (NumberFormatException ignored) { - CommandHandler.sendMessage(sender, translate("commands.generic.invalid.itemLevel")); + CommandHandler.sendMessage(sender, translate(sender, "commands.generic.invalid.itemLevel")); return; } // Fallthrough case 2: // [amount] try { amount = Integer.parseInt(args.get(1)); } catch (NumberFormatException ignored) { - CommandHandler.sendMessage(sender, translate("commands.generic.invalid.amount")); + CommandHandler.sendMessage(sender, translate(sender, "commands.generic.invalid.amount")); return; } // Fallthrough case 1: // @@ -142,33 +141,33 @@ public final class SendMailCommand implements CommandHandler { item = Integer.parseInt(args.get(0)); } catch (NumberFormatException ignored) { // TODO: Parse from item name using GM Handbook. - CommandHandler.sendMessage(sender, translate("commands.generic.invalid.itemId")); + CommandHandler.sendMessage(sender, translate(sender, "commands.generic.invalid.itemId")); return; } break; default: // *No args* - CommandHandler.sendMessage(sender, translate("commands.give.usage")); + CommandHandler.sendMessage(sender, translate(sender, "commands.give.usage")); return; } mailBuilder.mail.itemList.add(new Mail.MailItem(item, amount, lvl)); - CommandHandler.sendMessage(sender, translate("commands.sendMail.send", Integer.toString(amount), Integer.toString(item), Integer.toString(lvl))); + CommandHandler.sendMessage(sender, translate(sender, "commands.sendMail.send", Integer.toString(amount), Integer.toString(item), Integer.toString(lvl))); } } } } } else { - CommandHandler.sendMessage(sender, translate("commands.sendMail.invalid_arguments_please_use", getConstructionArgs(mailBuilder.constructionStage))); + CommandHandler.sendMessage(sender, translate(sender, "commands.sendMail.invalid_arguments_please_use", getConstructionArgs(mailBuilder.constructionStage, sender))); } } } - private String getConstructionArgs(int stage) { + private String getConstructionArgs(int stage, Player sender) { return switch(stage) { - case 0 -> translate("commands.sendMail.title"); - case 1 -> translate("commands.sendMail.message"); - case 2 -> translate("commands.sendMail.sender"); - case 3 -> translate("commands.sendMail.arguments"); - default -> translate("commands.sendMail.error", Integer.toString(stage)); + case 0 -> translate(sender, "commands.sendMail.title"); + case 1 -> translate(sender, "commands.sendMail.message"); + case 2 -> translate(sender, "commands.sendMail.sender"); + case 3 -> translate(sender, "commands.sendMail.arguments"); + default -> translate(sender, "commands.sendMail.error", Integer.toString(stage)); }; } diff --git a/src/main/java/emu/grasscutter/command/commands/SendMessageCommand.java b/src/main/java/emu/grasscutter/command/commands/SendMessageCommand.java index acf63dea0..d8051f7c7 100644 --- a/src/main/java/emu/grasscutter/command/commands/SendMessageCommand.java +++ b/src/main/java/emu/grasscutter/command/commands/SendMessageCommand.java @@ -8,23 +8,19 @@ import java.util.List; import static emu.grasscutter.utils.Language.translate; -@Command(label = "say", usage = "say ", description = "Sends a message to a player as the server", - aliases = {"sendservmsg", "sendservermessage", "sendmessage"}, permission = "server.sendmessage") +@Command(label = "sendmessage", usage = "sendmessage ", + aliases = {"say", "sendservmsg", "sendservermessage"}, permission = "server.sendmessage", permissionTargeted = "server.sendmessage.others", description = "commands.sendMessage.description") public final class SendMessageCommand implements CommandHandler { @Override public void execute(Player sender, Player targetPlayer, List args) { - if (targetPlayer == null) { - CommandHandler.sendMessage(sender, translate("commands.execution.need_target")); - return; - } if (args.size() == 0) { - CommandHandler.sendMessage(null, translate("commands.sendMessage.usage")); + CommandHandler.sendMessage(sender, translate(sender, "commands.sendMessage.usage")); return; } String message = String.join(" ", args); CommandHandler.sendMessage(targetPlayer, message); - CommandHandler.sendMessage(sender, translate("commands.sendMessage.success")); + CommandHandler.sendMessage(sender, translate(sender, "commands.sendMessage.success")); } -} \ No newline at end of file +} diff --git a/src/main/java/emu/grasscutter/command/commands/SetFetterLevelCommand.java b/src/main/java/emu/grasscutter/command/commands/SetFetterLevelCommand.java index 7184c679c..863989d75 100644 --- a/src/main/java/emu/grasscutter/command/commands/SetFetterLevelCommand.java +++ b/src/main/java/emu/grasscutter/command/commands/SetFetterLevelCommand.java @@ -12,26 +12,20 @@ import emu.grasscutter.server.packet.send.PacketAvatarFetterDataNotify; import static emu.grasscutter.utils.Language.translate; @Command(label = "setfetterlevel", usage = "setfetterlevel ", - description = "Sets your fetter level for your current active character", - aliases = {"setfetterlvl", "setfriendship"}, permission = "player.setfetterlevel") + aliases = {"setfetterlvl", "setfriendship"}, permission = "player.setfetterlevel", permissionTargeted = "player.setfetterlevel.others", description = "commands.setFetterLevel.description") public final class SetFetterLevelCommand implements CommandHandler { @Override public void execute(Player sender, Player targetPlayer, List args) { - if (targetPlayer == null) { - CommandHandler.sendMessage(sender, translate("commands.execution.need_target")); - return; - } - if (args.size() != 1) { - CommandHandler.sendMessage(sender, translate("commands.setFetterLevel.usage")); + CommandHandler.sendMessage(sender, translate(sender, "commands.setFetterLevel.usage")); return; } try { int fetterLevel = Integer.parseInt(args.get(0)); if (fetterLevel < 0 || fetterLevel > 10) { - CommandHandler.sendMessage(sender, translate("commands.setFetterLevel.range_error")); + CommandHandler.sendMessage(sender, translate(sender, "commands.setFetterLevel.range_error")); return; } Avatar avatar = targetPlayer.getTeamManager().getCurrentAvatarEntity().getAvatar(); @@ -43,9 +37,9 @@ public final class SetFetterLevelCommand implements CommandHandler { avatar.save(); targetPlayer.sendPacket(new PacketAvatarFetterDataNotify(avatar)); - CommandHandler.sendMessage(sender, translate("commands.setFetterLevel.success", fetterLevel)); + CommandHandler.sendMessage(sender, translate(sender, "commands.setFetterLevel.success", fetterLevel)); } catch (NumberFormatException ignored) { - CommandHandler.sendMessage(sender, translate("commands.setFetterLevel.level_error")); + CommandHandler.sendMessage(sender, translate(sender, "commands.setFetterLevel.level_error")); } } diff --git a/src/main/java/emu/grasscutter/command/commands/SetStatsCommand.java b/src/main/java/emu/grasscutter/command/commands/SetStatsCommand.java index 233eb4d73..643b313f8 100644 --- a/src/main/java/emu/grasscutter/command/commands/SetStatsCommand.java +++ b/src/main/java/emu/grasscutter/command/commands/SetStatsCommand.java @@ -15,8 +15,7 @@ import emu.grasscutter.utils.Language; import static emu.grasscutter.utils.Language.translate; -@Command(label = "setstats", usage = "setstats|stats ", - description = "Set fight property for your current active character", aliases = {"stats"}, permission = "player.setstats") +@Command(label = "setstats", usage = "setstats|stats ", aliases = {"stats"}, permission = "player.setstats", permissionTargeted = "player.setstats.others", description = "commands.setStats.description") public final class SetStatsCommand implements CommandHandler { static class Stat { String name; @@ -176,16 +175,11 @@ public final class SetStatsCommand implements CommandHandler { @Override public void execute(Player sender, Player targetPlayer, List args) { - String syntax = sender == null ? translate("commands.setStats.usage_console") : translate("commands.setStats.ingame"); - String usage = syntax + translate("commands.setStats.help_message"); + String syntax = sender == null ? translate(sender, "commands.setStats.usage_console") : translate(sender, "commands.setStats.usage_ingame"); + String usage = syntax + translate(sender, "commands.setStats.help_message"); String statStr; String valueStr; - if (targetPlayer == null) { - CommandHandler.sendMessage(sender, translate("commands.execution.need_target")); - return; - } - if (args.size() == 2) { statStr = args.get(0).toLowerCase(); valueStr = args.get(1); @@ -204,7 +198,7 @@ public final class SetStatsCommand implements CommandHandler { value = Float.parseFloat(valueStr); } } catch (NumberFormatException ignored) { - CommandHandler.sendMessage(sender, translate("commands.setStats.value_error")); + CommandHandler.sendMessage(sender, translate(sender, "commands.setStats.value_error")); return; } @@ -218,10 +212,10 @@ public final class SetStatsCommand implements CommandHandler { valueStr = String.format("%.0f", value); } if (targetPlayer == sender) { - CommandHandler.sendMessage(sender, translate("commands.setStats.set_self", stat.name, valueStr)); + CommandHandler.sendMessage(sender, translate(sender, "commands.setStats.set_self", stat.name, valueStr)); } else { String uidStr = targetPlayer.getAccount().getId(); - CommandHandler.sendMessage(sender, translate("commands.setStats.set_self", stat.name, uidStr, valueStr)); + CommandHandler.sendMessage(sender, translate(sender, "commands.setStats.set_for_uid", stat.name, uidStr, valueStr)); } } else { CommandHandler.sendMessage(sender, usage); diff --git a/src/main/java/emu/grasscutter/command/commands/SetWorldLevelCommand.java b/src/main/java/emu/grasscutter/command/commands/SetWorldLevelCommand.java index 914d8cecc..c142dc01d 100644 --- a/src/main/java/emu/grasscutter/command/commands/SetWorldLevelCommand.java +++ b/src/main/java/emu/grasscutter/command/commands/SetWorldLevelCommand.java @@ -10,26 +10,20 @@ import java.util.List; import static emu.grasscutter.utils.Language.translate; @Command(label = "setworldlevel", usage = "setworldlevel ", - description = "Sets your world level (Relog to see proper effects)", - aliases = {"setworldlvl"}, permission = "player.setworldlevel") + aliases = {"setworldlvl"}, permission = "player.setworldlevel", permissionTargeted = "player.setworldlevel.others", description = "commands.setWorldLevel.description") public final class SetWorldLevelCommand implements CommandHandler { @Override public void execute(Player sender, Player targetPlayer, List args) { - if (targetPlayer == null) { - CommandHandler.sendMessage(sender, translate("commands.execution.need_target")); - return; - } - if (args.size() < 1) { - CommandHandler.sendMessage(sender, translate("commands.setWorldLevel.usage")); + CommandHandler.sendMessage(sender, translate(sender, "commands.setWorldLevel.usage")); return; } try { int level = Integer.parseInt(args.get(0)); if (level > 8 || level < 0) { - CommandHandler.sendMessage(sender, translate("commands.setWorldLevel.value_error")); + CommandHandler.sendMessage(sender, translate(sender, "commands.setWorldLevel.value_error")); return; } @@ -37,9 +31,9 @@ public final class SetWorldLevelCommand implements CommandHandler { targetPlayer.getWorld().setWorldLevel(level); targetPlayer.setWorldLevel(level); - CommandHandler.sendMessage(sender, translate("commands.setWorldLevel.success", Integer.toString(level))); + CommandHandler.sendMessage(sender, translate(sender, "commands.setWorldLevel.success", Integer.toString(level))); } catch (NumberFormatException ignored) { - CommandHandler.sendMessage(null, translate("commands.setWorldLevel.invalid_world_level")); + CommandHandler.sendMessage(null, translate(sender, "commands.setWorldLevel.invalid_world_level")); } } } diff --git a/src/main/java/emu/grasscutter/command/commands/SpawnCommand.java b/src/main/java/emu/grasscutter/command/commands/SpawnCommand.java index c66a45b50..7bf320383 100644 --- a/src/main/java/emu/grasscutter/command/commands/SpawnCommand.java +++ b/src/main/java/emu/grasscutter/command/commands/SpawnCommand.java @@ -4,10 +4,10 @@ import emu.grasscutter.Grasscutter; import emu.grasscutter.command.Command; import emu.grasscutter.command.CommandHandler; import emu.grasscutter.data.GameData; -import emu.grasscutter.data.def.AvatarData; -import emu.grasscutter.data.def.GadgetData; -import emu.grasscutter.data.def.ItemData; -import emu.grasscutter.data.def.MonsterData; +import emu.grasscutter.data.excels.AvatarData; +import emu.grasscutter.data.excels.GadgetData; +import emu.grasscutter.data.excels.ItemData; +import emu.grasscutter.data.excels.MonsterData; import emu.grasscutter.game.avatar.Avatar; import emu.grasscutter.game.entity.*; import emu.grasscutter.game.player.Player; @@ -20,59 +20,75 @@ import javax.swing.text.html.parser.Entity; import java.util.List; import java.util.Random; +import static emu.grasscutter.Configuration.*; import static emu.grasscutter.utils.Language.translate; -@Command(label = "spawn", usage = "spawn [amount] [level(monster only)]", - description = "Spawns an entity near you", permission = "server.spawn") +@Command(label = "spawn", usage = "spawn [amount] [level(monster only)] [ (monster only, optional)]", permission = "server.spawn", permissionTargeted = "server.spawn.others", description = "commands.spawn.description") public final class SpawnCommand implements CommandHandler { @Override public void execute(Player sender, Player targetPlayer, List args) { - if (targetPlayer == null) { - CommandHandler.sendMessage(sender, translate("commands.execution.need_target")); - return; - } - int id = 0; // This is just to shut up the linter, it's not a real default int amount = 1; int level = 1; + float x = 0, y = 0, z = 0; switch (args.size()) { + case 6: + try { + x = Float.parseFloat(args.get(3)); + y = Float.parseFloat(args.get(4)); + z = Float.parseFloat(args.get(5)); + } catch (NumberFormatException ignored) { + CommandHandler.sendMessage(sender, translate(sender, "commands.execution.argument_error")); + } // Fallthrough case 3: try { level = Integer.parseInt(args.get(2)); } catch (NumberFormatException ignored) { - CommandHandler.sendMessage(sender, translate("commands.execution.argument_error")); + CommandHandler.sendMessage(sender, translate(sender, "commands.execution.argument_error")); } // Fallthrough case 2: try { amount = Integer.parseInt(args.get(1)); } catch (NumberFormatException ignored) { - CommandHandler.sendMessage(sender, translate("commands.generic.error.amount")); + CommandHandler.sendMessage(sender, translate(sender, "commands.generic.invalid.amount")); } // Fallthrough case 1: try { id = Integer.parseInt(args.get(0)); } catch (NumberFormatException ignored) { - CommandHandler.sendMessage(sender, translate("commands.generic.error.entityId")); + CommandHandler.sendMessage(sender, translate(sender, "commands.generic.invalid.entityId")); } break; default: - CommandHandler.sendMessage(sender, translate("commands.spawn.usage")); + CommandHandler.sendMessage(sender, translate(sender, "commands.spawn.usage")); return; } - + MonsterData monsterData = GameData.getMonsterDataMap().get(id); GadgetData gadgetData = GameData.getGadgetDataMap().get(id); ItemData itemData = GameData.getItemDataMap().get(id); if (monsterData == null && gadgetData == null && itemData == null) { - CommandHandler.sendMessage(sender, translate("commands.generic.error.entityId")); + CommandHandler.sendMessage(sender, translate(sender, "commands.generic.invalid.entityId")); return; } + Scene scene = targetPlayer.getScene(); + + if (scene.getEntities().size() + amount > GAME_OPTIONS.sceneEntityLimit) { + amount = Math.max(Math.min(GAME_OPTIONS.sceneEntityLimit - scene.getEntities().size(), amount), 0); + CommandHandler.sendMessage(sender, translate(sender, "commands.spawn.limit_reached", amount)); + if (amount <= 0) { + return; + } + } double maxRadius = Math.sqrt(amount * 0.2 / Math.PI); for (int i = 0; i < amount; i++) { Position pos = GetRandomPositionInCircle(targetPlayer.getPos(), maxRadius).addY(3); + if(x != 0 && y != 0 && z != 0) { + pos = GetRandomPositionInCircle(new Position(x, y, z), maxRadius).addY(3); + } GameEntity entity = null; if (itemData != null) { entity = new EntityItem(scene, null, itemData, pos, 1, true); @@ -101,7 +117,7 @@ public final class SpawnCommand implements CommandHandler { scene.addEntity(entity); } - CommandHandler.sendMessage(sender, translate("commands.spawn.success", Integer.toString(amount), Integer.toString(id))); + CommandHandler.sendMessage(sender, translate(sender, "commands.spawn.success", Integer.toString(amount), Integer.toString(id))); } private Position GetRandomPositionInCircle(Position origin, double radius){ diff --git a/src/main/java/emu/grasscutter/command/commands/StopCommand.java b/src/main/java/emu/grasscutter/command/commands/StopCommand.java index ad4903107..ca7a80ec5 100644 --- a/src/main/java/emu/grasscutter/command/commands/StopCommand.java +++ b/src/main/java/emu/grasscutter/command/commands/StopCommand.java @@ -9,15 +9,14 @@ import java.util.List; import static emu.grasscutter.utils.Language.translate; -@Command(label = "stop", usage = "stop", - description = "Stops the server", permission = "server.stop") +@Command(label = "stop", usage = "stop", permission = "server.stop", description = "commands.stop.description", targetRequirement = Command.TargetRequirement.NONE) public final class StopCommand implements CommandHandler { @Override public void execute(Player sender, Player targetPlayer, List args) { CommandHandler.sendMessage(null, translate("commands.stop.success")); for (Player p : Grasscutter.getGameServer().getPlayers().values()) { - CommandHandler.sendMessage(p, translate("commands.stop.success")); + CommandHandler.sendMessage(p, translate(p, "commands.stop.success")); } System.exit(1000); diff --git a/src/main/java/emu/grasscutter/command/commands/TalentCommand.java b/src/main/java/emu/grasscutter/command/commands/TalentCommand.java index c9b2e8931..27ed8de4e 100644 --- a/src/main/java/emu/grasscutter/command/commands/TalentCommand.java +++ b/src/main/java/emu/grasscutter/command/commands/TalentCommand.java @@ -3,7 +3,7 @@ package emu.grasscutter.command.commands; import emu.grasscutter.Grasscutter; import emu.grasscutter.command.Command; import emu.grasscutter.command.CommandHandler; -import emu.grasscutter.data.def.AvatarSkillDepotData; +import emu.grasscutter.data.excels.AvatarSkillDepotData; import emu.grasscutter.game.avatar.Avatar; import emu.grasscutter.game.entity.EntityAvatar; import emu.grasscutter.game.player.Player; @@ -14,18 +14,17 @@ import java.util.List; import static emu.grasscutter.utils.Language.translate; -@Command(label = "talent", usage = "talent ", - description = "Set talent level for your current active character", permission = "player.settalent") +@Command(label = "talent", usage = "talent ", permission = "player.settalent", permissionTargeted = "player.settalent.others", description = "commands.talent.description") public final class TalentCommand implements CommandHandler { private void setTalentLevel(Player sender, Player player, Avatar avatar, int talentId, int talentLevel) { int oldLevel = avatar.getSkillLevelMap().get(talentId); if (talentLevel < 0 || talentLevel > 15) { - CommandHandler.sendMessage(sender, translate("commands.talent.lower_16")); + CommandHandler.sendMessage(sender, translate(sender, "commands.talent.lower_16")); return; } // Upgrade skill - avatar.getSkillLevelMap().put(talentLevel, talentLevel); + avatar.getSkillLevelMap().put(talentId, talentLevel); avatar.save(); // Packet @@ -41,20 +40,15 @@ public final class TalentCommand implements CommandHandler { } else if (talentId == depot.getEnergySkill()) { successMessage = "commands.talent.set_q"; } - CommandHandler.sendMessage(sender, translate(successMessage, talentLevel)); + CommandHandler.sendMessage(sender, translate(sender, successMessage, talentLevel)); } @Override public void execute(Player sender, Player targetPlayer, List args) { - if (targetPlayer == null) { - CommandHandler.sendMessage(sender, translate("commands.execution.need_target")); - return; - } - if (args.size() < 1){ - CommandHandler.sendMessage(sender, translate("commands.talent.usage_1")); - CommandHandler.sendMessage(sender, translate("commands.talent.usage_2")); - CommandHandler.sendMessage(sender, translate("commands.talent.usage_3")); + CommandHandler.sendMessage(sender, translate(sender, "commands.talent.usage_1")); + CommandHandler.sendMessage(sender, translate(sender, "commands.talent.usage_2")); + CommandHandler.sendMessage(sender, translate(sender, "commands.talent.usage_3")); return; } @@ -63,15 +57,15 @@ public final class TalentCommand implements CommandHandler { String cmdSwitch = args.get(0); switch (cmdSwitch) { default -> { - CommandHandler.sendMessage(sender, translate("commands.talent.usage_1")); - CommandHandler.sendMessage(sender, translate("commands.talent.usage_2")); - CommandHandler.sendMessage(sender, translate("commands.talent.usage_3")); + CommandHandler.sendMessage(sender, translate(sender, "commands.talent.usage_1")); + CommandHandler.sendMessage(sender, translate(sender, "commands.talent.usage_2")); + CommandHandler.sendMessage(sender, translate(sender, "commands.talent.usage_3")); return; } case "set" -> { if (args.size() < 3) { - CommandHandler.sendMessage(sender, translate("commands.talent.usage_1")); - CommandHandler.sendMessage(sender, translate("commands.talent.usage_3")); + CommandHandler.sendMessage(sender, translate(sender, "commands.talent.usage_1")); + CommandHandler.sendMessage(sender, translate(sender, "commands.talent.usage_3")); return; } try { @@ -79,13 +73,13 @@ public final class TalentCommand implements CommandHandler { int newLevel = Integer.parseInt(args.get(2)); setTalentLevel(sender, targetPlayer, avatar, skillId, newLevel); } catch (NumberFormatException ignored) { - CommandHandler.sendMessage(sender, translate("commands.talent.invalid_skill_id")); + CommandHandler.sendMessage(sender, translate(sender, "commands.talent.invalid_skill_id")); return; } } case "n", "e", "q" -> { if (args.size() < 2) { - CommandHandler.sendMessage(sender, translate("commands.talent.usage_2")); + CommandHandler.sendMessage(sender, translate(sender, "commands.talent.usage_2")); return; } AvatarSkillDepotData SkillDepot = avatar.getData().getSkillDepot(); @@ -98,7 +92,7 @@ public final class TalentCommand implements CommandHandler { int newLevel = Integer.parseInt(args.get(1)); setTalentLevel(sender, targetPlayer, avatar, skillId, newLevel); } catch (NumberFormatException ignored) { - CommandHandler.sendMessage(sender, translate("commands.talent.invalid_level")); + CommandHandler.sendMessage(sender, translate(sender, "commands.talent.invalid_level")); return; } } @@ -106,9 +100,9 @@ public final class TalentCommand implements CommandHandler { int skillIdNorAtk = avatar.getData().getSkillDepot().getSkills().get(0); int skillIdE = avatar.getData().getSkillDepot().getSkills().get(1); int skillIdQ = avatar.getData().getSkillDepot().getEnergySkill(); - CommandHandler.sendMessage(sender, translate("commands.talent.normal_attack_id", Integer.toString(skillIdNorAtk))); - CommandHandler.sendMessage(sender, translate("commands.talent.e_skill_id", Integer.toString(skillIdE))); - CommandHandler.sendMessage(sender, translate("commands.talent.q_skill_id", Integer.toString(skillIdQ))); + CommandHandler.sendMessage(sender, translate(sender, "commands.talent.normal_attack_id", Integer.toString(skillIdNorAtk))); + CommandHandler.sendMessage(sender, translate(sender, "commands.talent.e_skill_id", Integer.toString(skillIdE))); + CommandHandler.sendMessage(sender, translate(sender, "commands.talent.q_skill_id", Integer.toString(skillIdQ))); } } } diff --git a/src/main/java/emu/grasscutter/command/commands/TeamCommand.java b/src/main/java/emu/grasscutter/command/commands/TeamCommand.java new file mode 100644 index 000000000..c425059f7 --- /dev/null +++ b/src/main/java/emu/grasscutter/command/commands/TeamCommand.java @@ -0,0 +1,254 @@ +package emu.grasscutter.command.commands; + +import emu.grasscutter.command.Command; +import emu.grasscutter.command.CommandHandler; +import emu.grasscutter.game.player.Player; +import emu.grasscutter.server.packet.send.PacketChangeMpTeamAvatarRsp; + +import java.util.List; +import java.util.ArrayList; +import java.util.HashSet; + +import static emu.grasscutter.Configuration.*; + +@Command(label = "team", usage = "team [avatarId,...] [index|first|last|index-index,...]", +permission = "player.team", permissionTargeted = "player.team.others", description = "commands.team.description") +public final class TeamCommand implements CommandHandler { + private static final int BASE_AVATARID = 10000000; + + @Override + public void execute(Player sender, Player targetPlayer, List args) { + if (args.isEmpty()) { + CommandHandler.sendTranslatedMessage(sender, "commands.team.usage"); + return; + } + + switch (args.get(0)) { + case "add": + if (!addCommand(sender, targetPlayer, args)) return; + break; + + case "remove": + if (!removeCommand(sender, targetPlayer, args)) return; + break; + + case "set": + if (!setCommand(sender, targetPlayer, args)) return; + break; + + default: + CommandHandler.sendTranslatedMessage(sender, "commands.team.invalid_usage"); + CommandHandler.sendTranslatedMessage(sender, "commands.team.usage"); + return; + } + + targetPlayer.getTeamManager().updateTeamEntities( + new PacketChangeMpTeamAvatarRsp(targetPlayer, targetPlayer.getTeamManager().getCurrentTeamInfo())); + } + + private boolean addCommand(Player sender, Player targetPlayer, List args) { + if (args.size() < 2) { + CommandHandler.sendTranslatedMessage(sender, "commands.team.invalid_usage"); + CommandHandler.sendTranslatedMessage(sender, "commands.team.add_usage"); + return false; + } + + int index = -1; + if (args.size() > 2) { + try { + index = Integer.parseInt(args.get(2)) - 1; + if (index < 0) index = 0; + } catch (Exception e) { + CommandHandler.sendTranslatedMessage(sender, "commands.team.invalid_index"); + return false; + } + } + + var avatarIds = args.get(1).split(","); + var currentTeamAvatars = targetPlayer.getTeamManager().getCurrentTeamInfo().getAvatars(); + + if (currentTeamAvatars.size() + avatarIds.length > GAME_OPTIONS.avatarLimits.singlePlayerTeam) { + CommandHandler.sendTranslatedMessage(sender, "commands.team.add_too_much", GAME_OPTIONS.avatarLimits.singlePlayerTeam); + return false; + } + + for (var avatarId: avatarIds) { + int id = Integer.parseInt(avatarId); + var success = addAvatar(sender, targetPlayer, id, index); + if (index > 0) ++index; + } + return true; + } + + private boolean removeCommand(Player sender, Player targetPlayer, List args) { + if (args.size() < 2) { + CommandHandler.sendTranslatedMessage(sender, "commands.team.invalid_usage"); + CommandHandler.sendTranslatedMessage(sender, "commands.team.remove_usage"); + return false; + } + + var currentTeamAvatars = targetPlayer.getTeamManager().getCurrentTeamInfo().getAvatars(); + var avatarCount = currentTeamAvatars.size(); + + var metaIndexList = args.get(1).split(","); + var indexes = new HashSet(); + var ignoreList = new ArrayList(); + for (var metaIndex: metaIndexList) { + // step 1: parse metaIndex to indexes + var subIndexes = transformToIndexes(metaIndex, avatarCount); + if (subIndexes == null) { + CommandHandler.sendTranslatedMessage(sender, "commands.team.failed_to_parse_index", metaIndex); + continue; + } + + // step 2: get all of the avatar id through indexes + for (var avatarIndex: subIndexes) { + try { + indexes.add(currentTeamAvatars.get(avatarIndex - 1)); + } catch (Exception e) { + ignoreList.add(avatarIndex); + continue; + } + } + } + + // step 3: check if user remove all of the avatar + if (indexes.size() >= avatarCount) { + CommandHandler.sendTranslatedMessage(sender, "commands.team.remove_too_much"); + return false; + } + + // step 4: hint user for ignore index + if (!ignoreList.isEmpty()) { + CommandHandler.sendTranslatedMessage(sender, "commands.team.ignore_index", ignoreList); + } + + // step 5: remove + currentTeamAvatars.removeAll(indexes); + return true; + } + + private boolean setCommand(Player sender, Player targetPlayer, List args) { + if (args.size() < 3) { + CommandHandler.sendTranslatedMessage(sender, "commands.team.invalid_usage"); + CommandHandler.sendTranslatedMessage(sender, "commands.team.set_usage"); + return false; + } + + var currentTeamAvatars = targetPlayer.getTeamManager().getCurrentTeamInfo().getAvatars(); + + int index; + try { + index = Integer.parseInt(args.get(1)) - 1; + if (index < 0) index = 0; + } catch(Exception e) { + CommandHandler.sendTranslatedMessage(sender, "commands.team.failed_to_parse_index", args.get(1)); + return false; + } + + if (index + 1 > currentTeamAvatars.size()) { + CommandHandler.sendTranslatedMessage(sender, "commands.team.index_out_of_range"); + return false; + } + + int avatarId; + try { + avatarId = Integer.parseInt(args.get(2)); + } catch(Exception e) { + CommandHandler.sendTranslatedMessage(sender, "commands.team.failed_parse_avatar_id", args.get(2)); + return false; + } + if (avatarId < BASE_AVATARID) { + avatarId += BASE_AVATARID; + } + + if (currentTeamAvatars.contains(avatarId)) { + CommandHandler.sendTranslatedMessage(sender, "commands.team.avatar_already_in_team", avatarId); + return false; + } + + if (!targetPlayer.getAvatars().hasAvatar(avatarId)) { + CommandHandler.sendTranslatedMessage(sender, "commands.team.avatar_not_found", avatarId); + return false; + } + + currentTeamAvatars.set(index, avatarId); + return true; + } + + private boolean addAvatar(Player sender, Player targetPlayer, int avatarId, int index) { + if (avatarId < BASE_AVATARID) { + avatarId += BASE_AVATARID; + } + var currentTeamAvatars = targetPlayer.getTeamManager().getCurrentTeamInfo().getAvatars(); + if (currentTeamAvatars.contains(avatarId)) { + CommandHandler.sendTranslatedMessage(sender, "commands.team.avatar_already_in_team", avatarId); + return false; + } + if (!targetPlayer.getAvatars().hasAvatar(avatarId)) { + CommandHandler.sendTranslatedMessage(sender, "commands.team.avatar_not_found", avatarId); + return false; + } + if (index < 0) { + currentTeamAvatars.add(avatarId); + } else { + currentTeamAvatars.add(index, avatarId); + } + return true; + } + + private List transformToIndexes(String metaIndexes, int listLength) { + // step 1: check if metaIndexes is a special constants + if (metaIndexes.equals("first")) { + return List.of(1); + } else if (metaIndexes.equals("last")) { + return List.of(listLength); + } + + // step 2: check if metaIndexes is a range + if (metaIndexes.contains("-")) { + var range = metaIndexes.split("-"); + if (range.length < 2) { + return null; + } + + int min, max; + try { + min = switch (range[0]) { + case "first" -> 1; + case "last" -> listLength; + default -> Integer.parseInt(range[0]); + }; + + max = switch (range[1]) { + case "first" -> 1; + case "last" -> listLength; + default -> Integer.parseInt(range[1]); + }; + } catch (Exception e) { + return null; + } + + if (min > max) { + min ^= max; + max ^= min; + min ^= max; + } + + var indexes = new ArrayList(); + for (int i = min; i <= max; ++i) { + indexes.add(i); + } + return indexes; + } + + // step 3: index is a value, simply return + try { + int index = Integer.parseInt(metaIndexes); + return List.of(index); + } catch (Exception e) { + return null; + } + } + +} diff --git a/src/main/java/emu/grasscutter/command/commands/TeleportAllCommand.java b/src/main/java/emu/grasscutter/command/commands/TeleportAllCommand.java index 54c6101f7..5a28383a3 100644 --- a/src/main/java/emu/grasscutter/command/commands/TeleportAllCommand.java +++ b/src/main/java/emu/grasscutter/command/commands/TeleportAllCommand.java @@ -10,18 +10,13 @@ import java.util.List; import static emu.grasscutter.utils.Language.translate; -@Command(label = "tpall", usage = "tpall", - description = "Teleports all players in your world to your position", permission = "player.tpall") +@Command(label = "tpall", usage = "tpall", permission = "player.tpall", permissionTargeted = "player.tpall.others", description = "commands.teleportAll.description") public final class TeleportAllCommand implements CommandHandler { + @Override public void execute(Player sender, Player targetPlayer, List args) { - if (targetPlayer == null) { - CommandHandler.sendMessage(sender, translate("commands.execution.need_target")); - return; - } - if (!targetPlayer.getWorld().isMultiplayer()) { - CommandHandler.sendMessage(sender, translate("commands.teleportAll.error")); + CommandHandler.sendMessage(sender, translate(sender, "commands.teleportAll.error")); return; } @@ -33,6 +28,6 @@ public final class TeleportAllCommand implements CommandHandler { player.getWorld().transferPlayerToScene(player, targetPlayer.getSceneId(), pos); } - CommandHandler.sendMessage(sender, translate("commands.teleportAll.success")); + CommandHandler.sendMessage(sender, translate(sender, "commands.teleportAll.success")); } } diff --git a/src/main/java/emu/grasscutter/command/commands/TeleportCommand.java b/src/main/java/emu/grasscutter/command/commands/TeleportCommand.java index 06b669a17..4f0e14040 100644 --- a/src/main/java/emu/grasscutter/command/commands/TeleportCommand.java +++ b/src/main/java/emu/grasscutter/command/commands/TeleportCommand.java @@ -10,8 +10,7 @@ import java.util.List; import static emu.grasscutter.utils.Language.translate; -@Command(label = "teleport", usage = "teleport [scene id]", aliases = {"tp"}, - description = "Change the player's position.", permission = "player.teleport") +@Command(label = "teleport", usage = "teleport [sceneId]", aliases = {"tp"}, permission = "player.teleport", permissionTargeted = "player.teleport.others", description = "commands.teleport.description") public final class TeleportCommand implements CommandHandler { private float parseRelative(String input, Float current) { // TODO: Maybe this will be useful elsewhere later @@ -27,11 +26,6 @@ public final class TeleportCommand implements CommandHandler { @Override public void execute(Player sender, Player targetPlayer, List args) { - if (targetPlayer == null) { - CommandHandler.sendMessage(sender, translate("commands.execution.need_target")); - return; - } - Position pos = targetPlayer.getPos(); float x = pos.getX(); float y = pos.getY(); @@ -43,7 +37,7 @@ public final class TeleportCommand implements CommandHandler { try { sceneId = Integer.parseInt(args.get(3)); }catch (NumberFormatException ignored) { - CommandHandler.sendMessage(sender, translate("commands.execution.argument_error")); + CommandHandler.sendMessage(sender, translate(sender, "commands.execution.argument_error")); } // Fallthrough case 3: try { @@ -51,20 +45,20 @@ public final class TeleportCommand implements CommandHandler { y = parseRelative(args.get(1), y); z = parseRelative(args.get(2), z); } catch (NumberFormatException ignored) { - CommandHandler.sendMessage(sender, translate("commands.teleport.invalid_position")); + CommandHandler.sendMessage(sender, translate(sender, "commands.teleport.invalid_position")); } break; default: - CommandHandler.sendMessage(sender, translate("commands.teleport.usage")); + CommandHandler.sendMessage(sender, translate(sender, "commands.teleport.usage")); return; } Position target_pos = new Position(x, y, z); boolean result = targetPlayer.getWorld().transferPlayerToScene(targetPlayer, sceneId, target_pos); if (!result) { - CommandHandler.sendMessage(sender, translate("commands.teleport.invalid_position")); + CommandHandler.sendMessage(sender, translate(sender, "commands.teleport.invalid_position")); } else { - CommandHandler.sendMessage(sender, translate("commands.teleport.success", + CommandHandler.sendMessage(sender, translate(sender, "commands.teleport.success", targetPlayer.getNickname(), Float.toString(x), Float.toString(y), Float.toString(z), Integer.toString(sceneId)) ); diff --git a/src/main/java/emu/grasscutter/command/commands/UnlimitEnergyCommand.java b/src/main/java/emu/grasscutter/command/commands/UnlimitEnergyCommand.java new file mode 100644 index 000000000..943a21ea1 --- /dev/null +++ b/src/main/java/emu/grasscutter/command/commands/UnlimitEnergyCommand.java @@ -0,0 +1,55 @@ +package emu.grasscutter.command.commands; + +import emu.grasscutter.Grasscutter; +import emu.grasscutter.command.Command; +import emu.grasscutter.command.CommandHandler; +import emu.grasscutter.game.avatar.Avatar; +import emu.grasscutter.game.entity.EntityAvatar; +import emu.grasscutter.game.managers.EnergyManager.EnergyManager; +import emu.grasscutter.game.player.Player; +import emu.grasscutter.game.player.TeamManager; +import emu.grasscutter.game.props.ElementType; +import emu.grasscutter.net.proto.PropChangeReasonOuterClass; +import emu.grasscutter.utils.Position; + +import java.util.List; + +import static emu.grasscutter.Configuration.GAME_OPTIONS; +import static emu.grasscutter.utils.Language.translate; + +@Command(label = "unlimitenergy", usage = "unlimitenergy [on|off|toggle]", aliases = {"ule"}, permission = "player.unlimitenergy", permissionTargeted = "player.unlimitenergy.others", description = "commands.unlimitenergy.description") +public final class UnlimitEnergyCommand implements CommandHandler { + + @Override + public void execute(Player sender, Player targetPlayer, List args) { + if(!GAME_OPTIONS.energyUsage){ + CommandHandler.sendMessage(sender, translate(sender, "commands.unlimitenergy.config_error")); + return; + } + Boolean status = targetPlayer.getEnergyManager().getEnergyUsage(); + if (args.size() == 1) { + switch (args.get(0).toLowerCase()) { + case "on": + status = true; + break; + case "off": + status = false; + break; + default: + status = !status; + break; + } + } + EnergyManager energyManager=targetPlayer.getEnergyManager(); + energyManager.setEnergyUsage(!status); + // if unlimitEnergy is enable , make currentActiveTeam's Avatar full-energy + if (status) { + for (EntityAvatar entityAvatar : targetPlayer.getTeamManager().getActiveTeam()) { + entityAvatar.addEnergy(1000, + PropChangeReasonOuterClass.PropChangeReason.PROP_CHANGE_REASON_GM,true); + } + } + + CommandHandler.sendMessage(sender, translate(sender, "commands.unlimitenergy.success", (status ? translate(sender, "commands.status.enabled") : translate(sender, "commands.status.disabled")), targetPlayer.getNickname())); + } +} diff --git a/src/main/java/emu/grasscutter/command/commands/UnlockTowerCommand.java b/src/main/java/emu/grasscutter/command/commands/UnlockTowerCommand.java new file mode 100644 index 000000000..acb1489a2 --- /dev/null +++ b/src/main/java/emu/grasscutter/command/commands/UnlockTowerCommand.java @@ -0,0 +1,32 @@ +package emu.grasscutter.command.commands; + +import emu.grasscutter.command.Command; +import emu.grasscutter.command.CommandHandler; +import emu.grasscutter.game.player.Player; +import emu.grasscutter.game.tower.TowerLevelRecord; + +import java.util.List; + +import static emu.grasscutter.utils.Language.translate; + +@Command(label = "unlocktower", usage = "unlocktower", aliases = {"ut"}, + description = "commands.unlocktower.description", permission = "player.tower") +public class UnlockTowerCommand implements CommandHandler { + + @Override + public void execute(Player sender, Player targetPlayer, List args) { + unlockFloor(targetPlayer, targetPlayer.getServer().getTowerScheduleManager() + .getCurrentTowerScheduleData().getEntranceFloorId()); + + unlockFloor(targetPlayer, targetPlayer.getServer().getTowerScheduleManager() + .getScheduleFloors()); + + CommandHandler.sendMessage(sender, translate(sender, "commands.unlocktower.success")); + } + + public void unlockFloor(Player player, List floors){ + floors.stream() + .filter(id -> !player.getTowerManager().getRecordMap().containsKey(id)) + .forEach(id -> player.getTowerManager().getRecordMap().put(id, new TowerLevelRecord(id))); + } +} diff --git a/src/main/java/emu/grasscutter/command/commands/WeatherCommand.java b/src/main/java/emu/grasscutter/command/commands/WeatherCommand.java index df8a6a01f..78a284968 100644 --- a/src/main/java/emu/grasscutter/command/commands/WeatherCommand.java +++ b/src/main/java/emu/grasscutter/command/commands/WeatherCommand.java @@ -11,17 +11,11 @@ import java.util.List; import static emu.grasscutter.utils.Language.translate; -@Command(label = "weather", usage = "weather [climateId]", - description = "Changes the weather.", aliases = {"w"}, permission = "player.weather") +@Command(label = "weather", usage = "weather ", aliases = {"w"}, permission = "player.weather", permissionTargeted = "player.weather.others", description = "commands.weather.description") public final class WeatherCommand implements CommandHandler { @Override public void execute(Player sender, Player targetPlayer, List args) { - if (targetPlayer == null) { - CommandHandler.sendMessage(sender, translate("commands.execution.need_target")); - return; - } - int weatherId = 0; int climateId = 1; switch (args.size()) { @@ -29,17 +23,17 @@ public final class WeatherCommand implements CommandHandler { try { climateId = Integer.parseInt(args.get(1)); } catch (NumberFormatException ignored) { - CommandHandler.sendMessage(sender, translate("commands.weather.invalid_id")); + CommandHandler.sendMessage(sender, translate(sender, "commands.weather.invalid_id")); } case 1: try { weatherId = Integer.parseInt(args.get(0)); } catch (NumberFormatException ignored) { - CommandHandler.sendMessage(sender, translate("commands.weather.invalid_id")); + CommandHandler.sendMessage(sender, translate(sender, "commands.weather.invalid_id")); } break; default: - CommandHandler.sendMessage(sender, translate("commands.weather.usage")); + CommandHandler.sendMessage(sender, translate(sender, "commands.weather.usage")); return; } @@ -48,6 +42,6 @@ public final class WeatherCommand implements CommandHandler { targetPlayer.getScene().setWeather(weatherId); targetPlayer.getScene().setClimate(climate); targetPlayer.getScene().broadcastPacket(new PacketSceneAreaWeatherNotify(targetPlayer)); - CommandHandler.sendMessage(sender, translate("commands.weather.success", Integer.toString(weatherId), Integer.toString(climateId))); + CommandHandler.sendMessage(sender, translate(sender, "commands.weather.success", Integer.toString(weatherId), Integer.toString(climateId))); } } diff --git a/src/main/java/emu/grasscutter/data/DataLoader.java b/src/main/java/emu/grasscutter/data/DataLoader.java new file mode 100644 index 000000000..69a17491c --- /dev/null +++ b/src/main/java/emu/grasscutter/data/DataLoader.java @@ -0,0 +1,106 @@ +package emu.grasscutter.data; + +import emu.grasscutter.Grasscutter; +import emu.grasscutter.server.http.handlers.GachaHandler; +import emu.grasscutter.tools.Tools; +import emu.grasscutter.utils.FileUtils; +import emu.grasscutter.utils.Utils; + +import java.io.*; +import java.nio.file.FileSystems; +import java.nio.file.Path; +import java.util.List; +import java.util.regex.Pattern; + +import static emu.grasscutter.Configuration.DATA; + +public class DataLoader { + + /** + * Load a data file by its name. If the file isn't found within the /data directory then it will fallback to the default within the jar resources + * @see #load(String, boolean) + * @param resourcePath The path to the data file to be loaded. + * @return InputStream of the data file. + * @throws FileNotFoundException + */ + public static InputStream load(String resourcePath) throws FileNotFoundException { + return load(resourcePath, true); + } + + /** + * Load a data file by its name. + * @param resourcePath The path to the data file to be loaded. + * @param useFallback If the file does not exist in the /data directory, should it use the default file in the jar? + * @return InputStream of the data file. + * @throws FileNotFoundException + */ + public static InputStream load(String resourcePath, boolean useFallback) throws FileNotFoundException { + if(Utils.fileExists(DATA(resourcePath))) { + // Data is in the resource directory + return new FileInputStream(DATA(resourcePath)); + } else { + if(useFallback) { + return FileUtils.readResourceAsStream("/defaults/data/" + resourcePath); + } + } + + return null; + } + + public static void CheckAllFiles() { + try { + List filenames = FileUtils.getPathsFromResource("/defaults/data/"); + + if (filenames == null) { + Grasscutter.getLogger().error("We were unable to locate your default data files."); + } + + for (Path file : filenames) { + String relativePath = String.valueOf(file).split("defaults[\\\\\\/]data[\\\\\\/]")[1]; + + CheckAndCopyData(relativePath); + } + } catch (Exception e) { + Grasscutter.getLogger().error("An error occurred while trying to check the data folder.", e); + } + + GenerateGachaMappings(); + } + + private static void CheckAndCopyData(String name) { + String filePath = Utils.toFilePath(DATA(name)); + + if (!Utils.fileExists(filePath)) { + // Check if file is in subdirectory + if (name.indexOf("/") != -1) { + String[] path = name.split("/"); + + String folder = ""; + for(int i = 0; i < (path.length - 1); i++) { + folder += path[i] + "/"; + + // Make sure the current folder exists + String folderToCreate = Utils.toFilePath(DATA(folder)); + if(!Utils.fileExists(folderToCreate)) { + Grasscutter.getLogger().info("Creating data folder '" + folder + "'"); + Utils.createFolder(folderToCreate); + } + } + } + + Grasscutter.getLogger().info("Creating default '" + name + "' data"); + FileUtils.copyResource("/defaults/data/" + name, filePath); + } + } + + private static void GenerateGachaMappings() { + if (!Utils.fileExists(GachaHandler.gachaMappings)) { + try { + Grasscutter.getLogger().info("Creating default '" + GachaHandler.gachaMappings + "' data"); + Tools.createGachaMapping(GachaHandler.gachaMappings); + } catch (Exception exception) { + Grasscutter.getLogger().warn("Failed to create gacha mappings. \n" + exception); + } + } + } +} diff --git a/src/main/java/emu/grasscutter/data/GameData.java b/src/main/java/emu/grasscutter/data/GameData.java index 76a7f1652..1a3663784 100644 --- a/src/main/java/emu/grasscutter/data/GameData.java +++ b/src/main/java/emu/grasscutter/data/GameData.java @@ -8,10 +8,12 @@ import java.util.Map; import emu.grasscutter.Grasscutter; import emu.grasscutter.utils.Utils; -import emu.grasscutter.data.custom.AbilityEmbryoEntry; -import emu.grasscutter.data.custom.OpenConfigEntry; -import emu.grasscutter.data.custom.ScenePointEntry; -import emu.grasscutter.data.def.*; +import emu.grasscutter.data.binout.AbilityEmbryoEntry; +import emu.grasscutter.data.binout.AbilityModifierEntry; +import emu.grasscutter.data.binout.MainQuestData; +import emu.grasscutter.data.binout.OpenConfigEntry; +import emu.grasscutter.data.binout.ScenePointEntry; +import emu.grasscutter.data.excels.*; import it.unimi.dsi.fastutil.ints.Int2ObjectLinkedOpenHashMap; import it.unimi.dsi.fastutil.ints.Int2ObjectMap; import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap; @@ -22,8 +24,10 @@ public class GameData { // BinOutputs private static final Int2ObjectMap abilityHashes = new Int2ObjectOpenHashMap<>(); private static final Map abilityEmbryos = new HashMap<>(); + private static final Map abilityModifiers = new HashMap<>(); private static final Map openConfigEntries = new HashMap<>(); private static final Map scenePointEntries = new HashMap<>(); + private static final Int2ObjectMap mainQuestData = new Int2ObjectOpenHashMap<>(); // ExcelConfigs private static final Int2ObjectMap playerLevelDataMap = new Int2ObjectOpenHashMap<>(); @@ -47,7 +51,8 @@ public class GameData { private static final Int2ObjectMap weaponPromoteDataMap = new Int2ObjectOpenHashMap<>(); private static final Int2ObjectMap weaponCurveDataMap = new Int2ObjectOpenHashMap<>(); private static final Int2ObjectMap equipAffixDataMap = new Int2ObjectOpenHashMap<>(); - + + private static final Int2ObjectMap envAnimalGatherConfigDataMap = new Int2ObjectOpenHashMap<>(); private static final Int2ObjectMap monsterDataMap = new Int2ObjectOpenHashMap<>(); private static final Int2ObjectMap npcDataMap = new Int2ObjectOpenHashMap<>(); private static final Int2ObjectMap gadgetDataMap = new Int2ObjectOpenHashMap<>(); @@ -60,16 +65,28 @@ public class GameData { private static final Int2ObjectMap sceneDataMap = new Int2ObjectLinkedOpenHashMap<>(); private static final Int2ObjectMap fetterDataMap = new Int2ObjectOpenHashMap<>(); + private static final Int2ObjectMap codexQuestDataMap = new Int2ObjectOpenHashMap<>(); + private static final Int2ObjectMap codexQuestDataIdMap = new Int2ObjectOpenHashMap<>(); + private static final Int2ObjectMap codexAnimalDataMap = new Int2ObjectOpenHashMap<>(); + private static final Int2ObjectMap codexWeaponDataMap = new Int2ObjectOpenHashMap<>(); + private static final Int2ObjectMap codexWeaponDataIdMap = new Int2ObjectOpenHashMap<>(); + private static final Int2ObjectMap codexMaterialDataMap = new Int2ObjectOpenHashMap<>(); + private static final Int2ObjectMap codexMaterialDataIdMap = new Int2ObjectOpenHashMap<>(); + private static final Int2ObjectMap codexReliquaryDataMap = new Int2ObjectOpenHashMap<>(); + private static final Int2ObjectMap codexReliquaryDataIdMap = new Int2ObjectOpenHashMap<>(); + private static final ArrayList codexReliquaryArrayList = new ArrayList<>(); private static final Int2ObjectMap fetterCharacterCardDataMap = new Int2ObjectOpenHashMap<>(); private static final Int2ObjectMap rewardDataMap = new Int2ObjectOpenHashMap<>(); private static final Int2ObjectMap worldLevelDataMap = new Int2ObjectOpenHashMap<>(); private static final Int2ObjectMap dailyDungeonDataMap = new Int2ObjectOpenHashMap<>(); private static final Int2ObjectMap dungeonDataMap = new Int2ObjectOpenHashMap<>(); + private static final Int2ObjectMap questDataMap = new Int2ObjectOpenHashMap<>(); private static final Int2ObjectMap shopGoodsDataMap = new Int2ObjectOpenHashMap<>(); private static final Int2ObjectMap combineDataMap = new Int2ObjectOpenHashMap<>(); private static final Int2ObjectMap rewardPreviewDataMap = new Int2ObjectOpenHashMap<>(); private static final Int2ObjectMap towerFloorDataMap = new Int2ObjectOpenHashMap<>(); private static final Int2ObjectMap towerLevelDataMap = new Int2ObjectOpenHashMap<>(); + private static final Int2ObjectMap towerScheduleDataMap = new Int2ObjectOpenHashMap<>(); // Cache private static Map> fetters = new HashMap<>(); @@ -101,6 +118,10 @@ public class GameData { return abilityEmbryos; } + public static Map getAbilityModifiers() { + return abilityModifiers; + } + public static Map getOpenConfigEntries() { return openConfigEntries; } @@ -114,6 +135,10 @@ public class GameData { return getScenePointEntries().get(sceneId + "_" + pointId); } + public static Int2ObjectMap getMainQuestDataMap() { + return mainQuestData; + } + public static Int2ObjectMap getAvatarDataMap() { return avatarDataMap; } @@ -216,6 +241,9 @@ public class GameData { public static Int2ObjectMap getMonsterDataMap() { return monsterDataMap; } + public static Int2ObjectMap getEnvAnimalGatherConfigDataMap() { + return envAnimalGatherConfigDataMap; + } public static Int2ObjectMap getNpcDataMap() { return npcDataMap; @@ -278,6 +306,18 @@ public class GameData { return fetters; } + public static Int2ObjectMap getCodexQuestDataIdMap(){return codexQuestDataIdMap;} + + public static Int2ObjectMap getCodexAnimalDataMap(){return codexAnimalDataMap;} + + public static Int2ObjectMap getCodexWeaponDataIdMap(){return codexWeaponDataIdMap;} + + public static Int2ObjectMap getCodexMaterialDataIdMap(){return codexMaterialDataIdMap;} + + public static Int2ObjectMap getcodexReliquaryIdMap(){return codexReliquaryDataIdMap;} + + public static ArrayList getcodexReliquaryArrayList(){return codexReliquaryArrayList;} + public static Int2ObjectMap getWorldLevelDataMap() { return worldLevelDataMap; } @@ -320,4 +360,11 @@ public class GameData { public static Int2ObjectMap getTowerLevelDataMap(){ return towerLevelDataMap; } + public static Int2ObjectMap getTowerScheduleDataMap(){ + return towerScheduleDataMap; + } + + public static Int2ObjectMap getQuestDataMap() { + return questDataMap; + } } diff --git a/src/main/java/emu/grasscutter/data/GameDepot.java b/src/main/java/emu/grasscutter/data/GameDepot.java index fa5aecba5..851e6c464 100644 --- a/src/main/java/emu/grasscutter/data/GameDepot.java +++ b/src/main/java/emu/grasscutter/data/GameDepot.java @@ -7,8 +7,8 @@ import org.danilopianini.util.FlexibleQuadTree; import org.danilopianini.util.SpatialIndex; import emu.grasscutter.Grasscutter; -import emu.grasscutter.data.def.ReliquaryAffixData; -import emu.grasscutter.data.def.ReliquaryMainPropData; +import emu.grasscutter.data.excels.ReliquaryAffixData; +import emu.grasscutter.data.excels.ReliquaryMainPropData; import emu.grasscutter.game.world.SpawnDataEntry; import emu.grasscutter.game.world.SpawnDataEntry.SpawnGroupEntry; import emu.grasscutter.utils.WeightedList; diff --git a/src/main/java/emu/grasscutter/data/ResourceLoader.java b/src/main/java/emu/grasscutter/data/ResourceLoader.java index b1e3da9ff..b0892b28f 100644 --- a/src/main/java/emu/grasscutter/data/ResourceLoader.java +++ b/src/main/java/emu/grasscutter/data/ResourceLoader.java @@ -1,7 +1,6 @@ package emu.grasscutter.data; -import java.io.File; -import java.io.FileReader; +import java.io.*; import java.util.*; import java.util.Map.Entry; import java.util.regex.Matcher; @@ -12,20 +11,30 @@ import emu.grasscutter.utils.Utils; import org.reflections.Reflections; import com.google.gson.JsonElement; +import com.google.gson.annotations.SerializedName; import com.google.gson.reflect.TypeToken; import emu.grasscutter.Grasscutter; +import emu.grasscutter.data.binout.AbilityEmbryoEntry; +import emu.grasscutter.data.binout.AbilityModifier; +import emu.grasscutter.data.binout.AbilityModifierEntry; +import emu.grasscutter.data.binout.MainQuestData; +import emu.grasscutter.data.binout.OpenConfigEntry; +import emu.grasscutter.data.binout.ScenePointEntry; +import emu.grasscutter.data.binout.AbilityModifier.AbilityConfigData; +import emu.grasscutter.data.binout.AbilityModifier.AbilityModifierAction; +import emu.grasscutter.data.binout.AbilityModifier.AbilityModifierActionType; import emu.grasscutter.data.common.PointData; import emu.grasscutter.data.common.ScenePointConfig; -import emu.grasscutter.data.custom.AbilityEmbryoEntry; -import emu.grasscutter.data.custom.OpenConfigEntry; -import emu.grasscutter.data.custom.ScenePointEntry; -import emu.grasscutter.game.world.SpawnDataEntry; -import emu.grasscutter.game.world.SpawnDataEntry.SpawnGroupEntry; +import emu.grasscutter.game.world.SpawnDataEntry.*; import it.unimi.dsi.fastutil.ints.Int2ObjectMap; +import static emu.grasscutter.Configuration.*; + public class ResourceLoader { + private static List loadedResources = new ArrayList(); + public static List> getResourceDefClasses() { Reflections reflections = new Reflections(ResourceLoader.class.getPackage().getName()); Set classes = reflections.getSubTypesOf(GameResource.class); @@ -47,12 +56,14 @@ public class ResourceLoader { // Load ability lists loadAbilityEmbryos(); loadOpenConfig(); + loadAbilityModifiers(); // Load resources loadResources(); // Process into depots GameDepot.load(); - // Load spawn data + // Load spawn data and quests loadSpawnData(); + loadQuests(); // Load scene points - must be done AFTER resources are loaded loadScenePoints(); // Custom - TODO move this somewhere else @@ -89,6 +100,10 @@ public class ResourceLoader { } public static void loadResources() { + loadResources(false); + } + + public static void loadResources(boolean doReload) { for (Class resourceDefinition : getResourceDefClasses()) { ResourceType type = resourceDefinition.getAnnotation(ResourceType.class); @@ -104,7 +119,7 @@ public class ResourceLoader { } try { - loadFromResource(resourceDefinition, type, map); + loadFromResource(resourceDefinition, type, map, doReload); } catch (Exception e) { Grasscutter.getLogger().error("Error loading resource file: " + Arrays.toString(type.name()), e); } @@ -112,30 +127,32 @@ public class ResourceLoader { } @SuppressWarnings("rawtypes") - protected static void loadFromResource(Class c, ResourceType type, Int2ObjectMap map) throws Exception { - for (String name : type.name()) { - loadFromResource(c, name, map); + protected static void loadFromResource(Class c, ResourceType type, Int2ObjectMap map, boolean doReload) throws Exception { + if(!loadedResources.contains(c.getSimpleName()) || doReload) { + for (String name : type.name()) { + loadFromResource(c, name, map); + } + Grasscutter.getLogger().info("Loaded " + map.size() + " " + c.getSimpleName() + "s."); + loadedResources.add(c.getSimpleName()); } - Grasscutter.getLogger().info("Loaded " + map.size() + " " + c.getSimpleName() + "s."); } - + @SuppressWarnings({"rawtypes", "unchecked"}) protected static void loadFromResource(Class c, String fileName, Int2ObjectMap map) throws Exception { - FileReader fileReader = new FileReader(Grasscutter.getConfig().RESOURCE_FOLDER + "ExcelBinOutput/" + fileName); - Gson gson = Grasscutter.getGsonFactory(); - List list = gson.fromJson(fileReader, List.class); + try (FileReader fileReader = new FileReader(RESOURCE("ExcelBinOutput/" + fileName))) { + List list = Grasscutter.getGsonFactory().fromJson(fileReader, TypeToken.getParameterized(Collection.class, c).getType()); - for (Object o : list) { - Map tempMap = Utils.switchPropertiesUpperLowerCase((Map) o, c); - GameResource res = gson.fromJson(gson.toJson(tempMap), TypeToken.get(c).getType()); - res.onLoad(); - map.put(res.getId(), res); + for (Object o : list) { + GameResource res = (GameResource) o; + res.onLoad(); + map.put(res.getId(), res); + } } } private static void loadScenePoints() { Pattern pattern = Pattern.compile("(?<=scene)(.*?)(?=_point.json)"); - File folder = new File(Grasscutter.getConfig().RESOURCE_FOLDER + "BinOutput/Scene/Point"); + File folder = new File(RESOURCE("BinOutput/Scene/Point")); if (!folder.isDirectory() || !folder.exists() || folder.listFiles() == null) { Grasscutter.getLogger().error("Scene point files cannot be found, you cannot use teleport waypoints!"); @@ -144,8 +161,7 @@ public class ResourceLoader { List scenePointList = new ArrayList<>(); for (File file : Objects.requireNonNull(folder.listFiles())) { - ScenePointConfig config = null; - Integer sceneId = null; + ScenePointConfig config; Integer sceneId; Matcher matcher = pattern.matcher(file.getName()); if (matcher.find()) { @@ -183,23 +199,19 @@ public class ResourceLoader { } private static void loadAbilityEmbryos() { - // Read from cached file if exists - File embryoCache = new File(Grasscutter.getConfig().DATA_FOLDER + "AbilityEmbryos.json"); List embryoList = null; - - if (embryoCache.exists()) { - // Load from cache - try (FileReader fileReader = new FileReader(embryoCache)) { - embryoList = Grasscutter.getGsonFactory().fromJson(fileReader, TypeToken.getParameterized(Collection.class, AbilityEmbryoEntry.class).getType()); - } catch (Exception e) { - e.printStackTrace(); - } - } else { + + // Read from cached file if exists + try(InputStream embryoCache = DataLoader.load("AbilityEmbryos.json", false)) { + embryoList = Grasscutter.getGsonFactory().fromJson(new InputStreamReader(embryoCache), TypeToken.getParameterized(Collection.class, AbilityEmbryoEntry.class).getType()); + } catch(Exception ignored) {} + + if(embryoList == null) { // Load from BinOutput Pattern pattern = Pattern.compile("(?<=ConfigAvatar_)(.*?)(?=.json)"); embryoList = new LinkedList<>(); - File folder = new File(Utils.toFilePath(Grasscutter.getConfig().RESOURCE_FOLDER + "BinOutput/Avatar/")); + File folder = new File(Utils.toFilePath(RESOURCE("BinOutput/Avatar/"))); File[] files = folder.listFiles(); if(files == null) { Grasscutter.getLogger().error("Error loading ability embryos: no files found in " + folder.getAbsolutePath()); @@ -244,19 +256,76 @@ public class ResourceLoader { } } - private static void loadSpawnData() { - // Read from cached file if exists - File spawnDataEntries = new File(Grasscutter.getConfig().DATA_FOLDER + "Spawns.json"); - List spawnEntryList = null; - - if (spawnDataEntries.exists()) { - // Load from cache - try (FileReader fileReader = new FileReader(spawnDataEntries)) { - spawnEntryList = Grasscutter.getGsonFactory().fromJson(fileReader, TypeToken.getParameterized(Collection.class, SpawnGroupEntry.class).getType()); + private static void loadAbilityModifiers() { + // Load from BinOutput + File folder = new File(Utils.toFilePath(RESOURCE("BinOutput/Ability/Temp/AvatarAbilities/"))); + File[] files = folder.listFiles(); + if (files == null) { + Grasscutter.getLogger().error("Error loading ability modifiers: no files found in " + folder.getAbsolutePath()); + return; + } + + for (File file : files) { + List abilityConfigList; + + try (FileReader fileReader = new FileReader(file)) { + abilityConfigList = Grasscutter.getGsonFactory().fromJson(fileReader, TypeToken.getParameterized(Collection.class, AbilityConfigData.class).getType()); } catch (Exception e) { e.printStackTrace(); + continue; + } + + for (AbilityConfigData data : abilityConfigList) { + if (data.Default.modifiers == null || data.Default.modifiers.size() == 0) { + continue; + } + + AbilityModifierEntry modifierEntry = new AbilityModifierEntry(data.Default.abilityName); + + for (Entry entry : data.Default.modifiers.entrySet()) { + AbilityModifier modifier = entry.getValue(); + + // Stare. + if (modifier.onAdded != null) { + for (AbilityModifierAction action : modifier.onAdded) { + if (action.$type.contains("HealHP")) { + action.type = AbilityModifierActionType.HealHP; + modifierEntry.getOnAdded().add(action); + } + } + } + + if (modifier.onThinkInterval != null) { + for (AbilityModifierAction action : modifier.onThinkInterval) { + if (action.$type.contains("HealHP")) { + action.type = AbilityModifierActionType.HealHP; + modifierEntry.getOnThinkInterval().add(action); + } + } + } + + if (modifier.onRemoved != null) { + for (AbilityModifierAction action : modifier.onRemoved) { + if (action.$type.contains("HealHP")) { + action.type = AbilityModifierActionType.HealHP; + modifierEntry.getOnRemoved().add(action); + } + } + } + } + + GameData.getAbilityModifiers().put(modifierEntry.getName(), modifierEntry); } } + } + + private static void loadSpawnData() { + List spawnEntryList = null; + + // Read from cached file if exists + try(InputStream spawnDataEntries = DataLoader.load("Spawns.json")) { + spawnEntryList = Grasscutter.getGsonFactory().fromJson(new InputStreamReader(spawnDataEntries), TypeToken.getParameterized(Collection.class, SpawnGroupEntry.class).getType()); + } catch (Exception ignored) {} if (spawnEntryList == null || spawnEntryList.isEmpty()) { Grasscutter.getLogger().error("No spawn data loaded!"); @@ -264,31 +333,26 @@ public class ResourceLoader { } for (SpawnGroupEntry entry : spawnEntryList) { - entry.getSpawns().stream().forEach(s -> { - s.setGroup(entry); - }); + entry.getSpawns().forEach(s -> s.setGroup(entry)); GameDepot.getSpawnListById(entry.getSceneId()).insert(entry, entry.getPos().getX(), entry.getPos().getZ()); } } private static void loadOpenConfig() { // Read from cached file if exists - File openConfigCache = new File(Grasscutter.getConfig().DATA_FOLDER + "OpenConfig.json"); List list = null; - - if (openConfigCache.exists()) { - try (FileReader fileReader = new FileReader(openConfigCache)) { - list = Grasscutter.getGsonFactory().fromJson(fileReader, TypeToken.getParameterized(Collection.class, OpenConfigEntry.class).getType()); - } catch (Exception e) { - e.printStackTrace(); - } - } else { + + try(InputStream openConfigCache = DataLoader.load("OpenConfig.json", false)) { + list = Grasscutter.getGsonFactory().fromJson(new InputStreamReader(openConfigCache), TypeToken.getParameterized(Collection.class, SpawnGroupEntry.class).getType()); + } catch (Exception ignored) {} + + if (list == null) { Map map = new TreeMap<>(); java.lang.reflect.Type type = new TypeToken>() {}.getType(); String[] folderNames = {"BinOutput/Talent/EquipTalents/", "BinOutput/Talent/AvatarTalents/"}; for (String name : folderNames) { - File folder = new File(Utils.toFilePath(Grasscutter.getConfig().RESOURCE_FOLDER + name)); + File folder = new File(Utils.toFilePath(RESOURCE(name))); File[] files = folder.listFiles(); if(files == null) { Grasscutter.getLogger().error("Error loading open config: no files found in " + folder.getAbsolutePath()); return; @@ -327,6 +391,29 @@ public class ResourceLoader { GameData.getOpenConfigEntries().put(entry.getName(), entry); } } + + private static void loadQuests() { + File folder = new File(RESOURCE("BinOutput/Quest/")); + + if (!folder.exists()) { + return; + } + + for (File file : folder.listFiles()) { + MainQuestData mainQuest = null; + + try (FileReader fileReader = new FileReader(file)) { + mainQuest = Grasscutter.getGsonFactory().fromJson(fileReader, MainQuestData.class); + } catch (Exception e) { + e.printStackTrace(); + continue; + } + + GameData.getMainQuestDataMap().put(mainQuest.getId(), mainQuest); + } + + Grasscutter.getLogger().info("Loaded " + GameData.getMainQuestDataMap().size() + " MainQuestDatas."); + } // BinOutput configs @@ -348,8 +435,14 @@ public class ResourceLoader { public static class OpenConfigData { public String $type; public String abilityName; + + @SerializedName(value="talentIndex", alternate={"OJOFFKLNAHN"}) public int talentIndex; + + @SerializedName(value="skillID", alternate={"overtime"}) public int skillID; + + @SerializedName(value="pointDelta", alternate={"IGEBKIHPOIF"}) public int pointDelta; } } diff --git a/src/main/java/emu/grasscutter/data/custom/AbilityEmbryoEntry.java b/src/main/java/emu/grasscutter/data/binout/AbilityEmbryoEntry.java similarity index 90% rename from src/main/java/emu/grasscutter/data/custom/AbilityEmbryoEntry.java rename to src/main/java/emu/grasscutter/data/binout/AbilityEmbryoEntry.java index 9c5510145..e0a047a58 100644 --- a/src/main/java/emu/grasscutter/data/custom/AbilityEmbryoEntry.java +++ b/src/main/java/emu/grasscutter/data/binout/AbilityEmbryoEntry.java @@ -1,4 +1,4 @@ -package emu.grasscutter.data.custom; +package emu.grasscutter.data.binout; public class AbilityEmbryoEntry { private String name; diff --git a/src/main/java/emu/grasscutter/data/binout/AbilityModifier.java b/src/main/java/emu/grasscutter/data/binout/AbilityModifier.java new file mode 100644 index 000000000..38e82db01 --- /dev/null +++ b/src/main/java/emu/grasscutter/data/binout/AbilityModifier.java @@ -0,0 +1,36 @@ +package emu.grasscutter.data.binout; + +import java.util.Map; + +public class AbilityModifier { + public AbilityModifierAction[] onAdded; + public AbilityModifierAction[] onThinkInterval; + public AbilityModifierAction[] onRemoved; + + public static class AbilityConfigData { + public AbilityData Default; + } + + public static class AbilityData { + public String abilityName; + public Map modifiers; + } + + public static class AbilityModifierAction { + public String $type; + public AbilityModifierActionType type; + public String target; + public AbilityModifierValue amount; + public AbilityModifierValue amountByTargetCurrentHPRatio; + } + + public static class AbilityModifierValue { + public boolean isFormula; + public boolean isDynamic; + public String dynamicKey; + } + + public enum AbilityModifierActionType { + HealHP, ApplyModifier, LoseHP; + } +} diff --git a/src/main/java/emu/grasscutter/data/binout/AbilityModifierEntry.java b/src/main/java/emu/grasscutter/data/binout/AbilityModifierEntry.java new file mode 100644 index 000000000..348c0f2c4 --- /dev/null +++ b/src/main/java/emu/grasscutter/data/binout/AbilityModifierEntry.java @@ -0,0 +1,37 @@ +package emu.grasscutter.data.binout; + +import java.util.ArrayList; +import java.util.List; + +import emu.grasscutter.data.binout.AbilityModifier.AbilityModifierAction; + +public class AbilityModifierEntry { + private String name; // Custom value + public List onModifierAdded; + public List onThinkInterval; + public List onRemoved; + + public AbilityModifierEntry(String name) { + this.name = name; + this.onModifierAdded = new ArrayList<>(); + this.onThinkInterval = new ArrayList<>(); + this.onRemoved = new ArrayList<>(); + } + + public String getName() { + return name; + } + + public List getOnAdded() { + return onModifierAdded; + } + + public List getOnThinkInterval() { + return onThinkInterval; + } + + public List getOnRemoved() { + return onRemoved; + } + +} diff --git a/src/main/java/emu/grasscutter/data/binout/MainQuestData.java b/src/main/java/emu/grasscutter/data/binout/MainQuestData.java new file mode 100644 index 000000000..96075bd61 --- /dev/null +++ b/src/main/java/emu/grasscutter/data/binout/MainQuestData.java @@ -0,0 +1,53 @@ +package emu.grasscutter.data.binout; + +import emu.grasscutter.game.quest.enums.LogicType; +import emu.grasscutter.game.quest.enums.QuestTrigger; +import emu.grasscutter.game.quest.enums.QuestType; + +public class MainQuestData { + private int id; + private int series; + private QuestType type; + + private long titleTextMapHash; + private int[] suggestTrackMainQuestList; + private int[] rewardIdList; + + private SubQuestData[] subQuests; + + public int getId() { + return id; + } + + public int getSeries() { + return series; + } + + public QuestType getType() { + return type; + } + + public long getTitleTextMapHash() { + return titleTextMapHash; + } + + public int[] getSuggestTrackMainQuestList() { + return suggestTrackMainQuestList; + } + + public int[] getRewardIdList() { + return rewardIdList; + } + + public SubQuestData[] getSubQuests() { + return subQuests; + } + + public static class SubQuestData { + private int subId; + + public int getSubId() { + return subId; + } + } +} diff --git a/src/main/java/emu/grasscutter/data/custom/OpenConfigEntry.java b/src/main/java/emu/grasscutter/data/binout/OpenConfigEntry.java similarity index 97% rename from src/main/java/emu/grasscutter/data/custom/OpenConfigEntry.java rename to src/main/java/emu/grasscutter/data/binout/OpenConfigEntry.java index 8ff646fa9..01d9c9833 100644 --- a/src/main/java/emu/grasscutter/data/custom/OpenConfigEntry.java +++ b/src/main/java/emu/grasscutter/data/binout/OpenConfigEntry.java @@ -1,4 +1,4 @@ -package emu.grasscutter.data.custom; +package emu.grasscutter.data.binout; import java.util.ArrayList; import java.util.List; diff --git a/src/main/java/emu/grasscutter/data/custom/ScenePointEntry.java b/src/main/java/emu/grasscutter/data/binout/ScenePointEntry.java similarity index 86% rename from src/main/java/emu/grasscutter/data/custom/ScenePointEntry.java rename to src/main/java/emu/grasscutter/data/binout/ScenePointEntry.java index 3e904f5c8..603701246 100644 --- a/src/main/java/emu/grasscutter/data/custom/ScenePointEntry.java +++ b/src/main/java/emu/grasscutter/data/binout/ScenePointEntry.java @@ -1,21 +1,21 @@ -package emu.grasscutter.data.custom; - -import emu.grasscutter.data.common.PointData; - -public class ScenePointEntry { - private String name; - private PointData pointData; - - public ScenePointEntry(String name, PointData pointData) { - this.name = name; - this.pointData = pointData; - } - - public String getName() { - return name; - } - - public PointData getPointData() { - return pointData; - } -} +package emu.grasscutter.data.binout; + +import emu.grasscutter.data.common.PointData; + +public class ScenePointEntry { + private String name; + private PointData pointData; + + public ScenePointEntry(String name, PointData pointData) { + this.name = name; + this.pointData = pointData; + } + + public String getName() { + return name; + } + + public PointData getPointData() { + return pointData; + } +} diff --git a/src/main/java/emu/grasscutter/data/common/CurveInfo.java b/src/main/java/emu/grasscutter/data/common/CurveInfo.java index 00c3de3f8..bd53b9a42 100644 --- a/src/main/java/emu/grasscutter/data/common/CurveInfo.java +++ b/src/main/java/emu/grasscutter/data/common/CurveInfo.java @@ -1,17 +1,17 @@ package emu.grasscutter.data.common; public class CurveInfo { - private String Type; - private String Arith; - private float Value; + private String type; + private String arith; + private float value; public String getType() { - return Type; + return type; } public String getArith() { - return Arith; + return arith; } public float getValue() { - return Value; + return value; } } diff --git a/src/main/java/emu/grasscutter/data/common/FightPropData.java b/src/main/java/emu/grasscutter/data/common/FightPropData.java index aee15a6c5..aed67227b 100644 --- a/src/main/java/emu/grasscutter/data/common/FightPropData.java +++ b/src/main/java/emu/grasscutter/data/common/FightPropData.java @@ -3,16 +3,16 @@ package emu.grasscutter.data.common; import emu.grasscutter.game.props.FightProperty; public class FightPropData { - private String PropType; + private String propType; private FightProperty prop; - private float Value; + private float value; public String getPropType() { - return PropType; + return propType; } public float getValue() { - return Value; + return value; } public FightProperty getProp() { @@ -20,6 +20,6 @@ public class FightPropData { } public void onLoad() { - this.prop = FightProperty.getPropByName(PropType); + this.prop = FightProperty.getPropByName(propType); } } \ No newline at end of file diff --git a/src/main/java/emu/grasscutter/data/common/ItemParamData.java b/src/main/java/emu/grasscutter/data/common/ItemParamData.java index a962c4618..cc96240bb 100644 --- a/src/main/java/emu/grasscutter/data/common/ItemParamData.java +++ b/src/main/java/emu/grasscutter/data/common/ItemParamData.java @@ -1,20 +1,33 @@ package emu.grasscutter.data.common; +import com.google.gson.annotations.SerializedName; + public class ItemParamData { - private int Id; - private int Count; + @SerializedName(value="id", alternate={"itemId"}) + private int id; + + @SerializedName(value="count", alternate={"itemCount"}) + private int count; public ItemParamData() {} public ItemParamData(int id, int count) { - this.Id = id; - this.Count = count; + this.id = id; + this.count = count; } public int getId() { - return Id; + return id; + } + + public int getItemId() { + return id; } public int getCount() { - return Count; + return count; + } + + public int getItemCount() { + return count; } } diff --git a/src/main/java/emu/grasscutter/data/common/ItemParamStringData.java b/src/main/java/emu/grasscutter/data/common/ItemParamStringData.java index 5fad12511..573065925 100644 --- a/src/main/java/emu/grasscutter/data/common/ItemParamStringData.java +++ b/src/main/java/emu/grasscutter/data/common/ItemParamStringData.java @@ -1,26 +1,26 @@ package emu.grasscutter.data.common; public class ItemParamStringData { - private int Id; - private String Count; + private int id; + private String count; public ItemParamStringData() {} public int getId() { - return Id; + return id; } public String getCount() { - return Count; + return count; } public ItemParamData toItemParamData() { - if (Count.contains(";")) { - String[] split = Count.split(";"); - Count = Count.split(";")[split.length - 1]; - } else if (Count.contains(".")) { - return new ItemParamData(Id, (int) Math.ceil(Double.parseDouble(Count))); + if (count.contains(";")) { + String[] split = count.split(";"); + count = count.split(";")[split.length - 1]; + } else if (count.contains(".")) { + return new ItemParamData(id, (int) Math.ceil(Double.parseDouble(count))); } - return new ItemParamData(Id, Integer.parseInt(Count)); + return new ItemParamData(id, Integer.parseInt(count)); } } diff --git a/src/main/java/emu/grasscutter/data/common/ItemUseData.java b/src/main/java/emu/grasscutter/data/common/ItemUseData.java new file mode 100644 index 000000000..7f905c206 --- /dev/null +++ b/src/main/java/emu/grasscutter/data/common/ItemUseData.java @@ -0,0 +1,24 @@ +package emu.grasscutter.data.common; + +import java.util.List; + +public class ItemUseData { + private String useOp; + private List useParam; + + public String getUseOp() { + return useOp; + } + + public void setUseOp(String useOp) { + this.useOp = useOp; + } + + public List getUseParam() { + return useParam; + } + + public void setUseParam(List useParam) { + this.useParam = useParam; + } +} diff --git a/src/main/java/emu/grasscutter/data/common/OpenCondData.java b/src/main/java/emu/grasscutter/data/common/OpenCondData.java index 9e7b62b11..112bed1ac 100644 --- a/src/main/java/emu/grasscutter/data/common/OpenCondData.java +++ b/src/main/java/emu/grasscutter/data/common/OpenCondData.java @@ -3,22 +3,22 @@ package emu.grasscutter.data.common; import java.util.List; public class OpenCondData { - private String CondType; - private List ParamList; + private String condType; + private List paramList; public String getCondType() { - return CondType; + return condType; } public void setCondType(String condType) { - CondType = condType; + condType = condType; } public List getParamList() { - return ParamList; + return paramList; } public void setParamList(List paramList) { - ParamList = paramList; + paramList = paramList; } } diff --git a/src/main/java/emu/grasscutter/data/common/PointData.java b/src/main/java/emu/grasscutter/data/common/PointData.java index 492f1fc60..13c51cac7 100644 --- a/src/main/java/emu/grasscutter/data/common/PointData.java +++ b/src/main/java/emu/grasscutter/data/common/PointData.java @@ -1,8 +1,10 @@ package emu.grasscutter.data.common; +import com.google.gson.annotations.SerializedName; + import emu.grasscutter.Grasscutter; import emu.grasscutter.data.GameData; -import emu.grasscutter.data.def.DailyDungeonData; +import emu.grasscutter.data.excels.DailyDungeonData; import emu.grasscutter.utils.Position; import it.unimi.dsi.fastutil.ints.IntArrayList; import it.unimi.dsi.fastutil.ints.IntList; @@ -11,7 +13,11 @@ public class PointData { private int id; private String $type; private Position tranPos; + + @SerializedName(value="dungeonIds", alternate={"JHHFPGJNMIN"}) private int[] dungeonIds; + + @SerializedName(value="dungeonRandomList", alternate={"OIBKFJNBLHO"}) private int[] dungeonRandomList; private int tranSceneId; diff --git a/src/main/java/emu/grasscutter/data/common/PropGrowCurve.java b/src/main/java/emu/grasscutter/data/common/PropGrowCurve.java index b8f11233c..ac8cac5f0 100644 --- a/src/main/java/emu/grasscutter/data/common/PropGrowCurve.java +++ b/src/main/java/emu/grasscutter/data/common/PropGrowCurve.java @@ -1,13 +1,15 @@ package emu.grasscutter.data.common; public class PropGrowCurve { - private String Type; - private String GrowCurve; + private String type; + private String growCurve; public String getType(){ - return this.Type; + return this.type; } + public String getGrowCurve(){ - return this.GrowCurve; + return this.growCurve; } + } diff --git a/src/main/java/emu/grasscutter/data/common/RewardItemData.java b/src/main/java/emu/grasscutter/data/common/RewardItemData.java deleted file mode 100644 index 024b89f7f..000000000 --- a/src/main/java/emu/grasscutter/data/common/RewardItemData.java +++ /dev/null @@ -1,22 +0,0 @@ -package emu.grasscutter.data.common; - -public class RewardItemData { - private int ItemId; - private int ItemCount; - - public int getItemId() { - return ItemId; - } - - public void setItemId(int itemId) { - ItemId = itemId; - } - - public int getItemCount() { - return ItemCount; - } - - public void setItemCount(int itemCount) { - ItemCount = itemCount; - } -} diff --git a/src/main/java/emu/grasscutter/data/def/AvatarSkillData.java b/src/main/java/emu/grasscutter/data/def/AvatarSkillData.java deleted file mode 100644 index 3efc2a142..000000000 --- a/src/main/java/emu/grasscutter/data/def/AvatarSkillData.java +++ /dev/null @@ -1,84 +0,0 @@ -package emu.grasscutter.data.def; - -import java.util.List; - -import emu.grasscutter.data.GameResource; -import emu.grasscutter.data.ResourceType; -import emu.grasscutter.data.ResourceType.LoadPriority; - -@ResourceType(name = "AvatarSkillExcelConfigData.json", loadPriority = LoadPriority.HIGHEST) -public class AvatarSkillData extends GameResource { - private int Id; - private float CdTime; - private int CostElemVal; - private int MaxChargeNum; - private int TriggerID; - private boolean IsAttackCameraLock; - private int ProudSkillGroupId; - private String CostElemType; - private List LockWeightParams; - - private long NameTextMapHash; - - private String AbilityName; - private String LockShape; - private String GlobalValueKey; - - @Override - public int getId(){ - return this.Id; - } - - public float getCdTime() { - return CdTime; - } - - public int getCostElemVal() { - return CostElemVal; - } - - public int getMaxChargeNum() { - return MaxChargeNum; - } - - public int getTriggerID() { - return TriggerID; - } - - public boolean isIsAttackCameraLock() { - return IsAttackCameraLock; - } - - public int getProudSkillGroupId() { - return ProudSkillGroupId; - } - - public String getCostElemType() { - return CostElemType; - } - - public List getLockWeightParams() { - return LockWeightParams; - } - - public long getNameTextMapHash() { - return NameTextMapHash; - } - - public String getAbilityName() { - return AbilityName; - } - - public String getLockShape() { - return LockShape; - } - - public String getGlobalValueKey() { - return GlobalValueKey; - } - - @Override - public void onLoad() { - - } -} diff --git a/src/main/java/emu/grasscutter/data/def/CombineData.java b/src/main/java/emu/grasscutter/data/def/CombineData.java deleted file mode 100644 index bfcf6af68..000000000 --- a/src/main/java/emu/grasscutter/data/def/CombineData.java +++ /dev/null @@ -1,173 +0,0 @@ -package emu.grasscutter.data.def; - -import emu.grasscutter.data.GameResource; -import emu.grasscutter.data.ResourceType; - -import java.util.List; -import java.util.stream.Collectors; - -@ResourceType(name = "CombineExcelConfigData.json") -public class CombineData extends GameResource { - - private int CombineId; - - private int PlayerLevel; - - private boolean IsDefaultShow; - - private int CombineType; - - private int SubCombineType; - - private int ResultItemId; - - private int ResultItemCount; - - private int ScoinCost; - - private List RandomItems; - - private List MaterialItems; - - private long EffectDescTextMapHash; - - private String RecipeType; - - @Override - public int getId() { - return this.CombineId; - } - - @Override - public void onLoad() { - super.onLoad(); - // clean data - RandomItems = RandomItems.stream().filter(item -> item.Id > 0).collect(Collectors.toList()); - MaterialItems = MaterialItems.stream().filter(item -> item.Id > 0).collect(Collectors.toList()); - } - - public static class CombineItemPair { - - private int Id; - - private int Count; - - public CombineItemPair(int id, int count) { - Id = id; - Count = count; - } - - public int getId() { - return Id; - } - - public void setId(int id) { - Id = id; - } - - public int getCount() { - return Count; - } - - public void setCount(int count) { - Count = count; - } - } - - public int getCombineId() { - return CombineId; - } - - public void setCombineId(int combineId) { - CombineId = combineId; - } - - public int getPlayerLevel() { - return PlayerLevel; - } - - public void setPlayerLevel(int playerLevel) { - PlayerLevel = playerLevel; - } - - public boolean isDefaultShow() { - return IsDefaultShow; - } - - public void setDefaultShow(boolean defaultShow) { - IsDefaultShow = defaultShow; - } - - public int getCombineType() { - return CombineType; - } - - public void setCombineType(int combineType) { - CombineType = combineType; - } - - public int getSubCombineType() { - return SubCombineType; - } - - public void setSubCombineType(int subCombineType) { - SubCombineType = subCombineType; - } - - public int getResultItemId() { - return ResultItemId; - } - - public void setResultItemId(int resultItemId) { - ResultItemId = resultItemId; - } - - public int getResultItemCount() { - return ResultItemCount; - } - - public void setResultItemCount(int resultItemCount) { - ResultItemCount = resultItemCount; - } - - public int getScoinCost() { - return ScoinCost; - } - - public void setScoinCost(int scoinCost) { - ScoinCost = scoinCost; - } - - public List getRandomItems() { - return RandomItems; - } - - public void setRandomItems(List randomItems) { - RandomItems = randomItems; - } - - public List getMaterialItems() { - return MaterialItems; - } - - public void setMaterialItems(List materialItems) { - MaterialItems = materialItems; - } - - public long getEffectDescTextMapHash() { - return EffectDescTextMapHash; - } - - public void setEffectDescTextMapHash(long effectDescTextMapHash) { - EffectDescTextMapHash = effectDescTextMapHash; - } - - public String getRecipeType() { - return RecipeType; - } - - public void setRecipeType(String recipeType) { - RecipeType = recipeType; - } -} - diff --git a/src/main/java/emu/grasscutter/data/def/ItemData.java b/src/main/java/emu/grasscutter/data/def/ItemData.java deleted file mode 100644 index 5cd1df751..000000000 --- a/src/main/java/emu/grasscutter/data/def/ItemData.java +++ /dev/null @@ -1,258 +0,0 @@ -package emu.grasscutter.data.def; - -import emu.grasscutter.data.GameResource; -import emu.grasscutter.data.ResourceType; -import emu.grasscutter.game.props.FightProperty; -import it.unimi.dsi.fastutil.ints.IntOpenHashSet; -import it.unimi.dsi.fastutil.ints.IntSet; - -@ResourceType(name = {"MaterialExcelConfigData.json", "WeaponExcelConfigData.json", "ReliquaryExcelConfigData.json"}) -public class ItemData extends GameResource { - - private int Id; - private int StackLimit = 1; - private int MaxUseCount; - private int RankLevel; - private String EffectName; - private int[] SatiationParams; - private int Rank; - private int Weight; - private int GadgetId; - - private int[] DestroyReturnMaterial; - private int[] DestroyReturnMaterialCount; - - // Food - private String FoodQuality; - private String UseTarget; - private String[] UseParam; - - // String enums - private String ItemType; - private String MaterialType; - private String EquipType; - private String EffectType; - private String DestroyRule; - - // Relic - private int MainPropDepotId; - private int AppendPropDepotId; - private int AppendPropNum; - private int SetId; - private int[] AddPropLevels; - private int BaseConvExp; - private int MaxLevel; - - // Weapon - private int WeaponPromoteId; - private int WeaponBaseExp; - private int StoryId; - private int AvatarPromoteId; - private int AwakenMaterial; - private int[] AwakenCosts; - private int[] SkillAffix; - private WeaponProperty[] WeaponProp; - - // Hash - private String Icon; - private long NameTextMapHash; - - // Post load - private transient emu.grasscutter.game.inventory.MaterialType materialType; - private transient emu.grasscutter.game.inventory.ItemType itemType; - private transient emu.grasscutter.game.inventory.EquipType equipType; - - private IntSet addPropLevelSet; - - @Override - public int getId(){ - return this.Id; - } - - public String getMaterialTypeString(){ - return this.MaterialType; - } - - public int getStackLimit(){ - return this.StackLimit; - } - - public int getMaxUseCount(){ - return this.MaxUseCount; - } - - public String getUseTarget(){ - return this.UseTarget; - } - - public String[] getUseParam(){ - return this.UseParam; - } - - public int getRankLevel(){ - return this.RankLevel; - } - - public String getFoodQuality(){ - return this.FoodQuality; - } - - public String getEffectName(){ - return this.EffectName; - } - - public int[] getSatiationParams(){ - return this.SatiationParams; - } - - public int[] getDestroyReturnMaterial(){ - return this.DestroyReturnMaterial; - } - - public int[] getDestroyReturnMaterialCount(){ - return this.DestroyReturnMaterialCount; - } - - public long getNameTextMapHash(){ - return this.NameTextMapHash; - } - - public String getIcon(){ - return this.Icon; - } - - public String getItemTypeString(){ - return this.ItemType; - } - - public int getRank(){ - return this.Rank; - } - - public int getGadgetId() { - return GadgetId; - } - - public int getBaseConvExp() { - return BaseConvExp; - } - - public int getMainPropDepotId() { - return MainPropDepotId; - } - - public int getAppendPropDepotId() { - return AppendPropDepotId; - } - - public int getAppendPropNum() { - return AppendPropNum; - } - - public int getSetId() { - return SetId; - } - - public int getWeaponPromoteId() { - return WeaponPromoteId; - } - - public int getWeaponBaseExp() { - return WeaponBaseExp; - } - - public int getAwakenMaterial() { - return AwakenMaterial; - } - - public int[] getAwakenCosts() { - return AwakenCosts; - } - - public IntSet getAddPropLevelSet() { - return addPropLevelSet; - } - - public int[] getSkillAffix() { - return SkillAffix; - } - - public WeaponProperty[] getWeaponProperties() { - return WeaponProp; - } - - public int getMaxLevel() { - return MaxLevel; - } - - public emu.grasscutter.game.inventory.ItemType getItemType() { - return this.itemType; - } - - public emu.grasscutter.game.inventory.MaterialType getMaterialType() { - return this.materialType; - } - - public emu.grasscutter.game.inventory.EquipType getEquipType() { - return this.equipType; - } - - public boolean canAddRelicProp(int level) { - return this.addPropLevelSet != null & this.addPropLevelSet.contains(level); - } - - public boolean isEquip() { - return this.itemType == emu.grasscutter.game.inventory.ItemType.ITEM_RELIQUARY || this.itemType == emu.grasscutter.game.inventory.ItemType.ITEM_WEAPON; - } - - @Override - public void onLoad() { - this.itemType = emu.grasscutter.game.inventory.ItemType.getTypeByName(getItemTypeString()); - this.materialType = emu.grasscutter.game.inventory.MaterialType.getTypeByName(getMaterialTypeString()); - - if (this.itemType == emu.grasscutter.game.inventory.ItemType.ITEM_RELIQUARY) { - this.equipType = emu.grasscutter.game.inventory.EquipType.getTypeByName(this.EquipType); - if (this.AddPropLevels != null && this.AddPropLevels.length > 0) { - this.addPropLevelSet = new IntOpenHashSet(this.AddPropLevels); - } - } else if (this.itemType == emu.grasscutter.game.inventory.ItemType.ITEM_WEAPON) { - this.equipType = emu.grasscutter.game.inventory.EquipType.EQUIP_WEAPON; - } else { - this.equipType = emu.grasscutter.game.inventory.EquipType.EQUIP_NONE; - } - - if (this.getWeaponProperties() != null) { - for (WeaponProperty weaponProperty : this.getWeaponProperties()) { - weaponProperty.onLoad(); - } - } - } - - public static class WeaponProperty { - private FightProperty fightProp; - private String PropType; - private float InitValue; - private String Type; - - public String getPropType(){ - return this.PropType; - } - - public float getInitValue(){ - return this.InitValue; - } - - public String getType(){ - return this.Type; - } - - public FightProperty getFightProp() { - return fightProp; - } - - public void onLoad() { - this.fightProp = FightProperty.getPropByName(PropType); - } - - } -} diff --git a/src/main/java/emu/grasscutter/data/def/RewardData.java b/src/main/java/emu/grasscutter/data/def/RewardData.java deleted file mode 100644 index d8ea58415..000000000 --- a/src/main/java/emu/grasscutter/data/def/RewardData.java +++ /dev/null @@ -1,27 +0,0 @@ -package emu.grasscutter.data.def; - -import java.util.List; - -import emu.grasscutter.data.GameResource; -import emu.grasscutter.data.ResourceType; -import emu.grasscutter.data.common.RewardItemData; - -@ResourceType(name = "RewardExcelConfigData.json") -public class RewardData extends GameResource { - public int RewardId; - public List RewardItemList; - - @Override - public int getId() { - return RewardId; - } - - public List getRewardItemList() { - return RewardItemList; - } - - @Override - public void onLoad() { - - } -} diff --git a/src/main/java/emu/grasscutter/data/def/TowerFloorData.java b/src/main/java/emu/grasscutter/data/def/TowerFloorData.java deleted file mode 100644 index d9d0082c7..000000000 --- a/src/main/java/emu/grasscutter/data/def/TowerFloorData.java +++ /dev/null @@ -1,73 +0,0 @@ -package emu.grasscutter.data.def; - -import emu.grasscutter.data.GameResource; -import emu.grasscutter.data.ResourceType; - -@ResourceType(name = "TowerFloorExcelConfigData.json") -public class TowerFloorData extends GameResource { - - private int FloorId; - private int FloorIndex; - private int LevelId; - private int OverrideMonsterLevel; - private int TeamNum; - private int FloorLevelConfigId; - - @Override - public int getId() { - return this.FloorId; - } - - @Override - public void onLoad() { - super.onLoad(); - } - - public int getFloorId() { - return FloorId; - } - - public void setFloorId(int floorId) { - FloorId = floorId; - } - - public int getFloorIndex() { - return FloorIndex; - } - - public void setFloorIndex(int floorIndex) { - FloorIndex = floorIndex; - } - - public int getLevelId() { - return LevelId; - } - - public void setLevelId(int levelId) { - LevelId = levelId; - } - - public int getOverrideMonsterLevel() { - return OverrideMonsterLevel; - } - - public void setOverrideMonsterLevel(int overrideMonsterLevel) { - OverrideMonsterLevel = overrideMonsterLevel; - } - - public int getTeamNum() { - return TeamNum; - } - - public void setTeamNum(int teamNum) { - TeamNum = teamNum; - } - - public int getFloorLevelConfigId() { - return FloorLevelConfigId; - } - - public void setFloorLevelConfigId(int floorLevelConfigId) { - FloorLevelConfigId = floorLevelConfigId; - } -} diff --git a/src/main/java/emu/grasscutter/data/def/TowerLevelData.java b/src/main/java/emu/grasscutter/data/def/TowerLevelData.java deleted file mode 100644 index 6cc45cc06..000000000 --- a/src/main/java/emu/grasscutter/data/def/TowerLevelData.java +++ /dev/null @@ -1,55 +0,0 @@ -package emu.grasscutter.data.def; - -import emu.grasscutter.data.GameResource; -import emu.grasscutter.data.ResourceType; - -@ResourceType(name = "TowerLevelExcelConfigData.json") -public class TowerLevelData extends GameResource { - - private int ID; - private int LevelId; - private int LevelIndex; - private int DungeonId; - - @Override - public int getId() { - return this.ID; - } - - @Override - public void onLoad() { - super.onLoad(); - } - - public int getID() { - return ID; - } - - public void setID(int ID) { - this.ID = ID; - } - - public int getLevelId() { - return LevelId; - } - - public void setLevelId(int levelId) { - LevelId = levelId; - } - - public int getLevelIndex() { - return LevelIndex; - } - - public void setLevelIndex(int levelIndex) { - LevelIndex = levelIndex; - } - - public int getDungeonId() { - return DungeonId; - } - - public void setDungeonId(int dungeonId) { - DungeonId = dungeonId; - } -} diff --git a/src/main/java/emu/grasscutter/data/def/AvatarCostumeData.java b/src/main/java/emu/grasscutter/data/excels/AvatarCostumeData.java similarity index 72% rename from src/main/java/emu/grasscutter/data/def/AvatarCostumeData.java rename to src/main/java/emu/grasscutter/data/excels/AvatarCostumeData.java index c6166bd71..342a30652 100644 --- a/src/main/java/emu/grasscutter/data/def/AvatarCostumeData.java +++ b/src/main/java/emu/grasscutter/data/excels/AvatarCostumeData.java @@ -1,4 +1,4 @@ -package emu.grasscutter.data.def; +package emu.grasscutter.data.excels; import emu.grasscutter.data.GameData; import emu.grasscutter.data.GameResource; @@ -6,21 +6,21 @@ import emu.grasscutter.data.ResourceType; @ResourceType(name = "AvatarCostumeExcelConfigData.json") public class AvatarCostumeData extends GameResource { - private int CostumeId; - private int ItemId; - private int AvatarId; + private int costumeId; + private int itemId; + private int avatarId; @Override public int getId() { - return this.CostumeId; + return this.costumeId; } public int getItemId() { - return this.ItemId; + return this.itemId; } public int getAvatarId() { - return AvatarId; + return avatarId; } @Override diff --git a/src/main/java/emu/grasscutter/data/def/AvatarCurveData.java b/src/main/java/emu/grasscutter/data/excels/AvatarCurveData.java similarity index 59% rename from src/main/java/emu/grasscutter/data/def/AvatarCurveData.java rename to src/main/java/emu/grasscutter/data/excels/AvatarCurveData.java index 586c0517d..9fb219da4 100644 --- a/src/main/java/emu/grasscutter/data/def/AvatarCurveData.java +++ b/src/main/java/emu/grasscutter/data/excels/AvatarCurveData.java @@ -1,4 +1,4 @@ -package emu.grasscutter.data.def; +package emu.grasscutter.data.excels; import java.util.HashMap; import java.util.Map; @@ -10,27 +10,27 @@ import emu.grasscutter.data.common.CurveInfo; @ResourceType(name = "AvatarCurveExcelConfigData.json") public class AvatarCurveData extends GameResource { - private int Level; - private CurveInfo[] CurveInfos; + private int level; + private CurveInfo[] curveInfos; - private Map curveInfos; + private Map curveInfoMap; @Override public int getId() { - return this.Level; + return this.level; } public int getLevel() { - return Level; + return level; } public Map getCurveInfos() { - return curveInfos; + return curveInfoMap; } @Override public void onLoad() { - this.curveInfos = new HashMap<>(); - Stream.of(this.CurveInfos).forEach(info -> this.curveInfos.put(info.getType(), info.getValue())); + this.curveInfoMap = new HashMap<>(); + Stream.of(this.curveInfos).forEach(info -> this.curveInfoMap.put(info.getType(), info.getValue())); } } diff --git a/src/main/java/emu/grasscutter/data/def/AvatarData.java b/src/main/java/emu/grasscutter/data/excels/AvatarData.java similarity index 65% rename from src/main/java/emu/grasscutter/data/def/AvatarData.java rename to src/main/java/emu/grasscutter/data/excels/AvatarData.java index 27555fc0d..b9b4cfb22 100644 --- a/src/main/java/emu/grasscutter/data/def/AvatarData.java +++ b/src/main/java/emu/grasscutter/data/excels/AvatarData.java @@ -1,4 +1,4 @@ -package emu.grasscutter.data.def; +package emu.grasscutter.data.excels; import java.util.List; @@ -6,9 +6,10 @@ import emu.grasscutter.data.GameData; import emu.grasscutter.data.GameResource; import emu.grasscutter.data.ResourceType; import emu.grasscutter.data.ResourceType.LoadPriority; +import emu.grasscutter.data.binout.AbilityEmbryoEntry; import emu.grasscutter.data.common.PropGrowCurve; -import emu.grasscutter.data.custom.AbilityEmbryoEntry; import emu.grasscutter.game.props.FightProperty; +import emu.grasscutter.game.props.WeaponType; import emu.grasscutter.utils.Utils; import it.unimi.dsi.fastutil.ints.Int2ObjectMap; import it.unimi.dsi.fastutil.ints.IntArrayList; @@ -17,38 +18,36 @@ import it.unimi.dsi.fastutil.ints.IntList; @ResourceType(name = "AvatarExcelConfigData.json", loadPriority = LoadPriority.LOW) public class AvatarData extends GameResource { - private String name; - private String IconName; - private String BodyType; - private String QualityType; - private int ChargeEfficiency; - private int InitialWeapon; - private String WeaponType; - private String ImageName; - private int AvatarPromoteId; - private String CutsceneShow; - private int SkillDepotId; - private int StaminaRecoverSpeed; - private List CandSkillDepotIds; - private long DescTextMapHash; - private String AvatarIdentityType; - private List AvatarPromoteRewardLevelList; - private List AvatarPromoteRewardIdList; - private int FeatureTagGroupID; - - private long NameTextMapHash; - private long GachaImageNameHashSuffix; - private long InfoDescTextMapHash; - - private float HpBase; - private float AttackBase; - private float DefenseBase; - private float Critical; - private float CriticalHurt; + private String iconName; + private String bodyType; + private String qualityType; + private int chargeEfficiency; + private int initialWeapon; + private WeaponType weaponType; + private String imageName; + private int avatarPromoteId; + private String cutsceneShow; + private int skillDepotId; + private int staminaRecoverSpeed; + private List candSkillDepotIds; + private String avatarIdentityType; + private List avatarPromoteRewardLevelList; + private List avatarPromoteRewardIdList; - private List PropGrowCurves; - private int Id; + private long nameTextMapHash; + private float hpBase; + private float attackBase; + private float defenseBase; + private float critical; + private float criticalHurt; + + private List propGrowCurves; + private int id; + + // Transient + private String name; + private Int2ObjectMap growthCurveMap; private float[] hpGrowthCurve; private float[] attackGrowthCurve; @@ -58,11 +57,11 @@ public class AvatarData extends GameResource { private List fetters; private int nameCardRewardId; - private int nameCardId; + private int nameCardId; @Override public int getId(){ - return this.Id; + return this.id; } public String getName() { @@ -70,107 +69,91 @@ public class AvatarData extends GameResource { } public String getBodyType(){ - return this.BodyType; + return this.bodyType; } public String getQualityType(){ - return this.QualityType; + return this.qualityType; } public int getChargeEfficiency(){ - return this.ChargeEfficiency; + return this.chargeEfficiency; } public int getInitialWeapon(){ - return this.InitialWeapon; + return this.initialWeapon; } - public String getWeaponType(){ - return this.WeaponType; + public WeaponType getWeaponType(){ + return this.weaponType; } public String getImageName(){ - return this.ImageName; + return this.imageName; } public int getAvatarPromoteId(){ - return this.AvatarPromoteId; - } - - public long getGachaImageNameHashSuffix(){ - return this.GachaImageNameHashSuffix; + return this.avatarPromoteId; } public String getCutsceneShow(){ - return this.CutsceneShow; + return this.cutsceneShow; } public int getSkillDepotId(){ - return this.SkillDepotId; + return this.skillDepotId; } public int getStaminaRecoverSpeed(){ - return this.StaminaRecoverSpeed; + return this.staminaRecoverSpeed; } public List getCandSkillDepotIds(){ - return this.CandSkillDepotIds; + return this.candSkillDepotIds; } - - public long getDescTextMapHash(){ - return this.DescTextMapHash; - } - + public String getAvatarIdentityType(){ - return this.AvatarIdentityType; + return this.avatarIdentityType; } public List getAvatarPromoteRewardLevelList(){ - return this.AvatarPromoteRewardLevelList; + return this.avatarPromoteRewardLevelList; } public List getAvatarPromoteRewardIdList(){ - return this.AvatarPromoteRewardIdList; + return this.avatarPromoteRewardIdList; } - public int getFeatureTagGroupID(){ - return this.FeatureTagGroupID; - } - - public long getInfoDescTextMapHash(){ - return this.InfoDescTextMapHash; - } - public float getBaseHp(int level){ try { - return this.HpBase * this.hpGrowthCurve[level - 1]; + return this.hpBase * this.hpGrowthCurve[level - 1]; } catch (Exception e) { - return this.HpBase; + return this.hpBase; } } public float getBaseAttack(int level){ try { - return this.AttackBase * this.attackGrowthCurve[level - 1]; + return this.attackBase * this.attackGrowthCurve[level - 1]; } catch (Exception e) { - return this.AttackBase; + return this.attackBase; } } public float getBaseDefense(int level){ try { - return this.DefenseBase * this.defenseGrowthCurve[level - 1]; + return this.defenseBase * this.defenseGrowthCurve[level - 1]; } catch (Exception e) { - return this.DefenseBase; + return this.defenseBase; } } public float getBaseCritical(){ - return this.Critical; + return this.critical; } public float getBaseCriticalHurt(){ - return this.CriticalHurt; + return this.criticalHurt; } public float getGrowthCurveById(int level, FightProperty prop) { @@ -186,7 +169,7 @@ public class AvatarData extends GameResource { } public long getNameTextMapHash(){ - return this.NameTextMapHash; + return this.nameTextMapHash; } public AvatarSkillDepotData getSkillDepot() { @@ -211,13 +194,13 @@ public class AvatarData extends GameResource { @Override public void onLoad() { - this.skillDepot = GameData.getAvatarSkillDepotDataMap().get(this.SkillDepotId); + this.skillDepot = GameData.getAvatarSkillDepotDataMap().get(this.skillDepotId); // Get fetters from GameData - this.fetters = GameData.getFetterDataEntries().get(this.Id); + this.fetters = GameData.getFetterDataEntries().get(this.id); - if (GameData.getFetterCharacterCardDataMap().get(this.Id) != null) { - this.nameCardRewardId = GameData.getFetterCharacterCardDataMap().get(this.Id).getRewardId(); + if (GameData.getFetterCharacterCardDataMap().get(this.id) != null) { + this.nameCardRewardId = GameData.getFetterCharacterCardDataMap().get(this.id).getRewardId(); } if (GameData.getRewardDataMap().get(this.nameCardRewardId) != null) { @@ -230,7 +213,7 @@ public class AvatarData extends GameResource { this.defenseGrowthCurve = new float[size]; for (AvatarCurveData curveData : GameData.getAvatarCurveDataMap().values()) { int level = curveData.getLevel() - 1; - for (PropGrowCurve growCurve : this.PropGrowCurves) { + for (PropGrowCurve growCurve : this.propGrowCurves) { FightProperty prop = FightProperty.getPropByName(growCurve.getType()); switch (prop) { case FIGHT_PROP_BASE_HP: @@ -256,7 +239,7 @@ public class AvatarData extends GameResource { */ // Cache abilities - String[] split = this.IconName.split("_"); + String[] split = this.iconName.split("_"); if (split.length > 0) { this.name = split[split.length - 1]; diff --git a/src/main/java/emu/grasscutter/data/def/AvatarFetterLevelData.java b/src/main/java/emu/grasscutter/data/excels/AvatarFetterLevelData.java similarity index 63% rename from src/main/java/emu/grasscutter/data/def/AvatarFetterLevelData.java rename to src/main/java/emu/grasscutter/data/excels/AvatarFetterLevelData.java index a02915069..efc44147a 100644 --- a/src/main/java/emu/grasscutter/data/def/AvatarFetterLevelData.java +++ b/src/main/java/emu/grasscutter/data/excels/AvatarFetterLevelData.java @@ -1,23 +1,23 @@ -package emu.grasscutter.data.def; - -import emu.grasscutter.data.GameResource; -import emu.grasscutter.data.ResourceType; - -@ResourceType(name = "AvatarFettersLevelExcelConfigData.json") -public class AvatarFetterLevelData extends GameResource { - private int FetterLevel; - private int NeedExp; - - @Override - public int getId() { - return this.FetterLevel; - } - - public int getLevel() { - return FetterLevel; - } - - public int getExp() { - return NeedExp; - } -} +package emu.grasscutter.data.excels; + +import emu.grasscutter.data.GameResource; +import emu.grasscutter.data.ResourceType; + +@ResourceType(name = "AvatarFettersLevelExcelConfigData.json") +public class AvatarFetterLevelData extends GameResource { + private int fetterLevel; + private int needExp; + + @Override + public int getId() { + return this.fetterLevel; + } + + public int getLevel() { + return fetterLevel; + } + + public int getExp() { + return needExp; + } +} diff --git a/src/main/java/emu/grasscutter/data/def/AvatarFlycloakData.java b/src/main/java/emu/grasscutter/data/excels/AvatarFlycloakData.java similarity index 68% rename from src/main/java/emu/grasscutter/data/def/AvatarFlycloakData.java rename to src/main/java/emu/grasscutter/data/excels/AvatarFlycloakData.java index 100a6c87c..ab8106eab 100644 --- a/src/main/java/emu/grasscutter/data/def/AvatarFlycloakData.java +++ b/src/main/java/emu/grasscutter/data/excels/AvatarFlycloakData.java @@ -1,20 +1,20 @@ -package emu.grasscutter.data.def; +package emu.grasscutter.data.excels; import emu.grasscutter.data.GameResource; import emu.grasscutter.data.ResourceType; @ResourceType(name = "AvatarFlycloakExcelConfigData.json") public class AvatarFlycloakData extends GameResource { - private int FlycloakId; - private long NameTextMapHash; + private int flycloakId; + private long nameTextMapHash; @Override public int getId() { - return this.FlycloakId; + return this.flycloakId; } public long getNameTextMapHash() { - return NameTextMapHash; + return nameTextMapHash; } @Override diff --git a/src/main/java/emu/grasscutter/data/def/AvatarLevelData.java b/src/main/java/emu/grasscutter/data/excels/AvatarLevelData.java similarity index 69% rename from src/main/java/emu/grasscutter/data/def/AvatarLevelData.java rename to src/main/java/emu/grasscutter/data/excels/AvatarLevelData.java index bc6ca671c..3b37c3886 100644 --- a/src/main/java/emu/grasscutter/data/def/AvatarLevelData.java +++ b/src/main/java/emu/grasscutter/data/excels/AvatarLevelData.java @@ -1,23 +1,23 @@ -package emu.grasscutter.data.def; +package emu.grasscutter.data.excels; import emu.grasscutter.data.GameResource; import emu.grasscutter.data.ResourceType; @ResourceType(name = "AvatarLevelExcelConfigData.json") public class AvatarLevelData extends GameResource { - private int Level; - private int Exp; + private int level; + private int exp; @Override public int getId() { - return this.Level; + return this.level; } public int getLevel() { - return Level; + return level; } public int getExp() { - return Exp; + return exp; } } diff --git a/src/main/java/emu/grasscutter/data/def/AvatarPromoteData.java b/src/main/java/emu/grasscutter/data/excels/AvatarPromoteData.java similarity index 66% rename from src/main/java/emu/grasscutter/data/def/AvatarPromoteData.java rename to src/main/java/emu/grasscutter/data/excels/AvatarPromoteData.java index c397902e4..226105aac 100644 --- a/src/main/java/emu/grasscutter/data/def/AvatarPromoteData.java +++ b/src/main/java/emu/grasscutter/data/excels/AvatarPromoteData.java @@ -1,4 +1,4 @@ -package emu.grasscutter.data.def; +package emu.grasscutter.data.excels; import java.util.ArrayList; import emu.grasscutter.data.GameResource; @@ -9,45 +9,45 @@ import emu.grasscutter.data.common.ItemParamData; @ResourceType(name = "AvatarPromoteExcelConfigData.json") public class AvatarPromoteData extends GameResource { - private int AvatarPromoteId; - private int PromoteLevel; - private int ScoinCost; - private ItemParamData[] CostItems; - private int UnlockMaxLevel; - private FightPropData[] AddProps; - private int RequiredPlayerLevel; + private int avatarPromoteId; + private int promoteLevel; + private int scoinCost; + private ItemParamData[] costItems; + private int unlockMaxLevel; + private FightPropData[] addProps; + private int requiredPlayerLevel; @Override public int getId() { - return (AvatarPromoteId << 8) + PromoteLevel; + return (avatarPromoteId << 8) + promoteLevel; } public int getAvatarPromoteId() { - return AvatarPromoteId; + return avatarPromoteId; } public int getPromoteLevel() { - return PromoteLevel; + return promoteLevel; } public ItemParamData[] getCostItems() { - return CostItems; + return costItems; } public int getCoinCost() { - return ScoinCost; + return scoinCost; } public FightPropData[] getAddProps() { - return AddProps; + return addProps; } public int getUnlockMaxLevel() { - return UnlockMaxLevel; + return unlockMaxLevel; } public int getRequiredPlayerLevel() { - return RequiredPlayerLevel; + return requiredPlayerLevel; } @Override @@ -60,7 +60,7 @@ public class AvatarPromoteData extends GameResource { } trim.add(itemParam); } - this.CostItems = trim.toArray(new ItemParamData[trim.size()]); + this.costItems = trim.toArray(new ItemParamData[trim.size()]); // Trim fight prop data (just in case) ArrayList parsed = new ArrayList<>(getAddProps().length); for (FightPropData prop : getAddProps()) { @@ -69,6 +69,6 @@ public class AvatarPromoteData extends GameResource { parsed.add(prop); } } - this.AddProps = parsed.toArray(new FightPropData[parsed.size()]); + this.addProps = parsed.toArray(new FightPropData[parsed.size()]); } } diff --git a/src/main/java/emu/grasscutter/data/excels/AvatarSkillData.java b/src/main/java/emu/grasscutter/data/excels/AvatarSkillData.java new file mode 100644 index 000000000..76c56ef2e --- /dev/null +++ b/src/main/java/emu/grasscutter/data/excels/AvatarSkillData.java @@ -0,0 +1,85 @@ +package emu.grasscutter.data.excels; + +import java.util.List; + +import emu.grasscutter.data.GameResource; +import emu.grasscutter.data.ResourceType; +import emu.grasscutter.data.ResourceType.LoadPriority; +import emu.grasscutter.game.props.ElementType; + +@ResourceType(name = "AvatarSkillExcelConfigData.json", loadPriority = LoadPriority.HIGHEST) +public class AvatarSkillData extends GameResource { + private int id; + private float cdTime; + private int costElemVal; + private int maxChargeNum; + private int triggerID; + private boolean isAttackCameraLock; + private int proudSkillGroupId; + private ElementType costElemType; + private List lockWeightParams; + + private long nameTextMapHash; + + private String abilityName; + private String lockShape; + private String globalValueKey; + + @Override + public int getId(){ + return this.id; + } + + public float getCdTime() { + return cdTime; + } + + public int getCostElemVal() { + return costElemVal; + } + + public int getMaxChargeNum() { + return maxChargeNum; + } + + public int getTriggerID() { + return triggerID; + } + + public boolean isIsAttackCameraLock() { + return isAttackCameraLock; + } + + public int getProudSkillGroupId() { + return proudSkillGroupId; + } + + public ElementType getCostElemType() { + return costElemType; + } + + public List getLockWeightParams() { + return lockWeightParams; + } + + public long getNameTextMapHash() { + return nameTextMapHash; + } + + public String getAbilityName() { + return abilityName; + } + + public String getLockShape() { + return lockShape; + } + + public String getGlobalValueKey() { + return globalValueKey; + } + + @Override + public void onLoad() { + + } +} diff --git a/src/main/java/emu/grasscutter/data/def/AvatarSkillDepotData.java b/src/main/java/emu/grasscutter/data/excels/AvatarSkillDepotData.java similarity index 59% rename from src/main/java/emu/grasscutter/data/def/AvatarSkillDepotData.java rename to src/main/java/emu/grasscutter/data/excels/AvatarSkillDepotData.java index 7916bfd45..152ec31bd 100644 --- a/src/main/java/emu/grasscutter/data/def/AvatarSkillDepotData.java +++ b/src/main/java/emu/grasscutter/data/excels/AvatarSkillDepotData.java @@ -1,4 +1,4 @@ -package emu.grasscutter.data.def; +package emu.grasscutter.data.excels; import java.util.List; @@ -6,7 +6,7 @@ import emu.grasscutter.data.GameData; import emu.grasscutter.data.GameResource; import emu.grasscutter.data.ResourceType; import emu.grasscutter.data.ResourceType.LoadPriority; -import emu.grasscutter.data.custom.AbilityEmbryoEntry; +import emu.grasscutter.data.binout.AbilityEmbryoEntry; import emu.grasscutter.game.props.ElementType; import emu.grasscutter.utils.Utils; import it.unimi.dsi.fastutil.ints.IntArrayList; @@ -15,62 +15,63 @@ import it.unimi.dsi.fastutil.ints.IntList; @ResourceType(name = "AvatarSkillDepotExcelConfigData.json", loadPriority = LoadPriority.HIGH) public class AvatarSkillDepotData extends GameResource { - private int Id; - private int EnergySkill; - private int AttackModeSkill; + private int id; + private int energySkill; + private int attackModeSkill; - private List Skills; - private List SubSkills; - private List ExtraAbilities; - private List Talents; - private List InherentProudSkillOpens; + private List skills; + private List subSkills; + private List extraAbilities; + private List talents; + private List inherentProudSkillOpens; - private String TalentStarName; - private String SkillDepotAbilityGroup; + private String talentStarName; + private String skillDepotAbilityGroup; + // Transient private AvatarSkillData energySkillData; private ElementType elementType; private IntList abilities; @Override public int getId(){ - return this.Id; + return this.id; } public int getEnergySkill(){ - return this.EnergySkill; + return this.energySkill; } public List getSkills(){ - return this.Skills; + return this.skills; } public List getSubSkills(){ - return this.SubSkills; + return this.subSkills; } public int getAttackModeSkill(){ - return this.AttackModeSkill; + return this.attackModeSkill; } public List getExtraAbilities(){ - return this.ExtraAbilities; + return this.extraAbilities; } public List getTalents(){ - return this.Talents; + return this.talents; } public String getTalentStarName(){ - return this.TalentStarName; + return this.talentStarName; } public List getInherentProudSkillOpens(){ - return this.InherentProudSkillOpens; + return this.inherentProudSkillOpens; } public String getSkillDepotAbilityGroup(){ - return this.SkillDepotAbilityGroup; + return this.skillDepotAbilityGroup; } public AvatarSkillData getEnergySkillData() { @@ -94,30 +95,24 @@ public class AvatarSkillDepotData extends GameResource { @Override public void onLoad() { - this.energySkillData = GameData.getAvatarSkillDataMap().get(this.EnergySkill); + this.energySkillData = GameData.getAvatarSkillDataMap().get(this.energySkill); if (getEnergySkillData() != null) { - this.elementType = ElementType.getTypeByName(getEnergySkillData().getCostElemType()); + this.elementType = getEnergySkillData().getCostElemType(); } else { this.elementType = ElementType.None; } } public static class InherentProudSkillOpens { - private int ProudSkillGroupId; + private int proudSkillGroupId; + private int needAvatarPromoteLevel; - private int NeedAvatarPromoteLevel; - - public void setProudSkillGroupId(int ProudSkillGroupId){ - this.ProudSkillGroupId = ProudSkillGroupId; - } public int getProudSkillGroupId(){ - return this.ProudSkillGroupId; - } - public void setNeedAvatarPromoteLevel(int NeedAvatarPromoteLevel){ - this.NeedAvatarPromoteLevel = NeedAvatarPromoteLevel; + return this.proudSkillGroupId; } + public int getNeedAvatarPromoteLevel(){ - return this.NeedAvatarPromoteLevel; + return this.needAvatarPromoteLevel; } } } diff --git a/src/main/java/emu/grasscutter/data/def/AvatarTalentData.java b/src/main/java/emu/grasscutter/data/excels/AvatarTalentData.java similarity index 63% rename from src/main/java/emu/grasscutter/data/def/AvatarTalentData.java rename to src/main/java/emu/grasscutter/data/excels/AvatarTalentData.java index fdf96875d..f02d2e50a 100644 --- a/src/main/java/emu/grasscutter/data/def/AvatarTalentData.java +++ b/src/main/java/emu/grasscutter/data/excels/AvatarTalentData.java @@ -1,4 +1,4 @@ -package emu.grasscutter.data.def; +package emu.grasscutter.data.excels; import java.util.ArrayList; import emu.grasscutter.data.GameResource; @@ -8,51 +8,51 @@ import emu.grasscutter.data.common.FightPropData; @ResourceType(name = "AvatarTalentExcelConfigData.json", loadPriority = LoadPriority.HIGHEST) public class AvatarTalentData extends GameResource { - private int TalentId; - private int PrevTalent; - private long NameTextMapHash; - private String Icon; - private int MainCostItemId; - private int MainCostItemCount; - private String OpenConfig; - private FightPropData[] AddProps; - private float[] ParamList; + private int talentId; + private int prevTalent; + private long nameTextMapHash; + private String icon; + private int mainCostItemId; + private int mainCostItemCount; + private String openConfig; + private FightPropData[] addProps; + private float[] paramList; @Override public int getId(){ - return this.TalentId; + return this.talentId; } public int PrevTalent() { - return PrevTalent; + return prevTalent; } public long getNameTextMapHash() { - return NameTextMapHash; + return nameTextMapHash; } public String getIcon() { - return Icon; + return icon; } public int getMainCostItemId() { - return MainCostItemId; + return mainCostItemId; } public int getMainCostItemCount() { - return MainCostItemCount; + return mainCostItemCount; } public String getOpenConfig() { - return OpenConfig; + return openConfig; } public FightPropData[] getAddProps() { - return AddProps; + return addProps; } public float[] getParamList() { - return ParamList; + return paramList; } @Override @@ -64,6 +64,6 @@ public class AvatarTalentData extends GameResource { parsed.add(prop); } } - this.AddProps = parsed.toArray(new FightPropData[parsed.size()]); + this.addProps = parsed.toArray(new FightPropData[parsed.size()]); } } diff --git a/src/main/java/emu/grasscutter/data/excels/CodexAnimalData.java b/src/main/java/emu/grasscutter/data/excels/CodexAnimalData.java new file mode 100644 index 000000000..0bc9c0521 --- /dev/null +++ b/src/main/java/emu/grasscutter/data/excels/CodexAnimalData.java @@ -0,0 +1,39 @@ +package emu.grasscutter.data.excels; + +import emu.grasscutter.data.GameResource; +import emu.grasscutter.data.ResourceType; + +@ResourceType(name = {"AnimalCodexExcelConfigData.json"}) +public class CodexAnimalData extends GameResource { + private int Id; + private String type; + private int describeId; + private int sortOrder; + private CodexAnimalUnlockCondition OCCLHPBCDGL; + + @Override + public int getId() { + return Id; + } + + public String getType() { + return type; + } + + public int getDescribeId() { + return describeId; + } + + public int getSortOrder() { + return sortOrder; + } + + public CodexAnimalUnlockCondition getUnlockCondition() { + return OCCLHPBCDGL; + } + + public enum CodexAnimalUnlockCondition { + CODEX_COUNT_TYPE_KILL, + CODEX_COUNT_TYPE_CAPTURE + } +} diff --git a/src/main/java/emu/grasscutter/data/excels/CodexMaterialData.java b/src/main/java/emu/grasscutter/data/excels/CodexMaterialData.java new file mode 100644 index 000000000..3ab32be5c --- /dev/null +++ b/src/main/java/emu/grasscutter/data/excels/CodexMaterialData.java @@ -0,0 +1,29 @@ +package emu.grasscutter.data.excels; + +import emu.grasscutter.data.GameData; +import emu.grasscutter.data.GameResource; +import emu.grasscutter.data.ResourceType; + +@ResourceType(name = {"MaterialCodexExcelConfigData.json"}) +public class CodexMaterialData extends GameResource { + private int Id; + private int materialId; + private int sortOrder; + + public int getSortOrder() { + return sortOrder; + } + + public int getMaterialId() { + return materialId; + } + + public int getId() { + return Id; + } + + @Override + public void onLoad() { + GameData.getCodexMaterialDataIdMap().put(this.getMaterialId(), this); + } +} diff --git a/src/main/java/emu/grasscutter/data/excels/CodexQuestData.java b/src/main/java/emu/grasscutter/data/excels/CodexQuestData.java new file mode 100644 index 000000000..09ccf0b6b --- /dev/null +++ b/src/main/java/emu/grasscutter/data/excels/CodexQuestData.java @@ -0,0 +1,41 @@ +package emu.grasscutter.data.excels; + +import emu.grasscutter.data.GameData; +import emu.grasscutter.data.GameResource; +import emu.grasscutter.data.ResourceType; + +@ResourceType(name = {"QuestCodexExcelConfigData.json"}) +public class CodexQuestData extends GameResource { + private int Id; + private int parentQuestId; + private int chapterId; + private int sortOrder; + private boolean isDisuse; + + public int getParentQuestId() { + return parentQuestId; + } + + public int getId() { + return Id; + } + + public int getChapterId() { + return chapterId; + } + + public int getSortOrder() { + return sortOrder; + } + + public boolean getIsDisuse() { + return isDisuse; + } + + @Override + public void onLoad() { + if(!this.getIsDisuse()) { + GameData.getCodexQuestDataIdMap().put(this.getParentQuestId(), this); + } + } +} diff --git a/src/main/java/emu/grasscutter/data/excels/CodexReliquaryData.java b/src/main/java/emu/grasscutter/data/excels/CodexReliquaryData.java new file mode 100644 index 000000000..8ed747b53 --- /dev/null +++ b/src/main/java/emu/grasscutter/data/excels/CodexReliquaryData.java @@ -0,0 +1,60 @@ +package emu.grasscutter.data.excels; + +import emu.grasscutter.data.GameData; +import emu.grasscutter.data.GameResource; +import emu.grasscutter.data.ResourceType; + +@ResourceType(name = {"ReliquaryCodexExcelConfigData.json"}) +public class CodexReliquaryData extends GameResource { + private int Id; + private int suitId; + private int level; + private int cupId; + private int leatherId; + private int capId; + private int flowerId; + private int sandId; + private int sortOrder; + + public int getSortOrder() { + return sortOrder; + } + + public int getId() { + return Id; + } + + public int getSuitId() { + return suitId; + } + + public int getLevel() { + return level; + } + + public int getCupId() { + return cupId; + } + + public int getLeatherId() { + return leatherId; + } + + public int getCapId() { + return capId; + } + + public int getFlowerId() { + return flowerId; + } + + public int getSandId() { + return sandId; + } + + @Override + public void onLoad() { + GameData.getcodexReliquaryArrayList().add(this); + GameData.getcodexReliquaryIdMap().put(getSuitId(), this); + } +} diff --git a/src/main/java/emu/grasscutter/data/excels/CodexWeaponData.java b/src/main/java/emu/grasscutter/data/excels/CodexWeaponData.java new file mode 100644 index 000000000..7b08290ec --- /dev/null +++ b/src/main/java/emu/grasscutter/data/excels/CodexWeaponData.java @@ -0,0 +1,29 @@ +package emu.grasscutter.data.excels; + +import emu.grasscutter.data.GameData; +import emu.grasscutter.data.GameResource; +import emu.grasscutter.data.ResourceType; + +@ResourceType(name = {"WeaponCodexExcelConfigData.json"}) +public class CodexWeaponData extends GameResource { + private int Id; + private int weaponId; + private int sortOrder; + + public int getSortOrder() { + return sortOrder; + } + + public int getWeaponId() { + return weaponId; + } + + public int getId() { + return Id; + } + + @Override + public void onLoad() { + GameData.getCodexWeaponDataIdMap().put(this.getWeaponId(), this); + } +} diff --git a/src/main/java/emu/grasscutter/data/excels/CombineData.java b/src/main/java/emu/grasscutter/data/excels/CombineData.java new file mode 100644 index 000000000..38ac7f641 --- /dev/null +++ b/src/main/java/emu/grasscutter/data/excels/CombineData.java @@ -0,0 +1,83 @@ +package emu.grasscutter.data.excels; + +import emu.grasscutter.data.GameResource; +import emu.grasscutter.data.ResourceType; +import emu.grasscutter.data.common.ItemParamData; + +import java.util.List; +import java.util.stream.Collectors; + +@ResourceType(name = "CombineExcelConfigData.json") +public class CombineData extends GameResource { + + private int combineId; + private int playerLevel; + private boolean isDefaultShow; + private int combineType; + private int subCombineType; + private int resultItemId; + private int resultItemCount; + private int scoinCost; + private List randomItems; + private List materialItems; + private String recipeType; + + @Override + public int getId() { + return this.combineId; + } + + @Override + public void onLoad() { + super.onLoad(); + // clean data + randomItems = randomItems.stream().filter(item -> item.getId() > 0).collect(Collectors.toList()); + materialItems = materialItems.stream().filter(item -> item.getId() > 0).collect(Collectors.toList()); + } + + public int getCombineId() { + return combineId; + } + + public int getPlayerLevel() { + return playerLevel; + } + + public boolean isDefaultShow() { + return isDefaultShow; + } + + public int getCombineType() { + return combineType; + } + + public int getSubCombineType() { + return subCombineType; + } + + public int getResultItemId() { + return resultItemId; + } + + public int getResultItemCount() { + return resultItemCount; + } + + public int getScoinCost() { + return scoinCost; + } + + public List getRandomItems() { + return randomItems; + } + + public List getMaterialItems() { + return materialItems; + } + + public String getRecipeType() { + return recipeType; + } + +} + diff --git a/src/main/java/emu/grasscutter/data/def/DailyDungeonData.java b/src/main/java/emu/grasscutter/data/excels/DailyDungeonData.java similarity index 59% rename from src/main/java/emu/grasscutter/data/def/DailyDungeonData.java rename to src/main/java/emu/grasscutter/data/excels/DailyDungeonData.java index 8cd878125..39f1da4f3 100644 --- a/src/main/java/emu/grasscutter/data/def/DailyDungeonData.java +++ b/src/main/java/emu/grasscutter/data/excels/DailyDungeonData.java @@ -1,4 +1,4 @@ -package emu.grasscutter.data.def; +package emu.grasscutter.data.excels; import java.util.Calendar; @@ -12,14 +12,14 @@ import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap; @ResourceType(name = "DailyDungeonConfigData.json") public class DailyDungeonData extends GameResource { - private int Id; - private int[] Monday; - private int[] Tuesday; - private int[] Wednesday; - private int[] Thursday; - private int[] Friday; - private int[] Saturday; - private int[] Sunday; + private int id; + private int[] monday; + private int[] tuesday; + private int[] wednesday; + private int[] thursday; + private int[] friday; + private int[] saturday; + private int[] sunday; private static final int[] empty = new int[0]; private final Int2ObjectMap map; @@ -30,7 +30,7 @@ public class DailyDungeonData extends GameResource { @Override public int getId() { - return this.Id; + return this.id; } public int[] getDungeonsByDay(int day) { @@ -39,12 +39,12 @@ public class DailyDungeonData extends GameResource { @Override public void onLoad() { - map.put(Calendar.MONDAY, Monday); - map.put(Calendar.TUESDAY, Tuesday); - map.put(Calendar.WEDNESDAY, Wednesday); - map.put(Calendar.THURSDAY, Thursday); - map.put(Calendar.FRIDAY, Friday); - map.put(Calendar.SATURDAY, Saturday); - map.put(Calendar.SUNDAY, Sunday); + map.put(Calendar.MONDAY, monday); + map.put(Calendar.TUESDAY, tuesday); + map.put(Calendar.WEDNESDAY, wednesday); + map.put(Calendar.THURSDAY, thursday); + map.put(Calendar.FRIDAY, friday); + map.put(Calendar.SATURDAY, saturday); + map.put(Calendar.SUNDAY, sunday); } } diff --git a/src/main/java/emu/grasscutter/data/def/DungeonData.java b/src/main/java/emu/grasscutter/data/excels/DungeonData.java similarity index 67% rename from src/main/java/emu/grasscutter/data/def/DungeonData.java rename to src/main/java/emu/grasscutter/data/excels/DungeonData.java index 086cbb6bf..3b18c01ef 100644 --- a/src/main/java/emu/grasscutter/data/def/DungeonData.java +++ b/src/main/java/emu/grasscutter/data/excels/DungeonData.java @@ -1,4 +1,4 @@ -package emu.grasscutter.data.def; +package emu.grasscutter.data.excels; import emu.grasscutter.data.GameData; import emu.grasscutter.data.GameResource; @@ -8,25 +8,25 @@ import emu.grasscutter.game.props.SceneType; @ResourceType(name = "DungeonExcelConfigData.json") public class DungeonData extends GameResource { - private int Id; - private int SceneId; - private int ShowLevel; - private int PassRewardPreviewID; - private String InvolveType; // TODO enum + private int id; + private int sceneId; + private int showLevel; + private int passRewardPreviewID; + private String involveType; // TODO enum private RewardPreviewData previewData; @Override public int getId() { - return this.Id; + return this.id; } public int getSceneId() { - return SceneId; + return sceneId; } public int getShowLevel() { - return ShowLevel; + return showLevel; } public RewardPreviewData getRewardPreview() { @@ -35,8 +35,8 @@ public class DungeonData extends GameResource { @Override public void onLoad() { - if (this.PassRewardPreviewID > 0) { - this.previewData = GameData.getRewardPreviewDataMap().get(this.PassRewardPreviewID); + if (this.passRewardPreviewID > 0) { + this.previewData = GameData.getRewardPreviewDataMap().get(this.passRewardPreviewID); } } } diff --git a/src/main/java/emu/grasscutter/data/excels/EnvAnimalGatherConfigData.java b/src/main/java/emu/grasscutter/data/excels/EnvAnimalGatherConfigData.java new file mode 100644 index 000000000..e99d681e0 --- /dev/null +++ b/src/main/java/emu/grasscutter/data/excels/EnvAnimalGatherConfigData.java @@ -0,0 +1,40 @@ +package emu.grasscutter.data.excels; + +import java.util.List; + +import emu.grasscutter.data.GameResource; +import emu.grasscutter.data.ResourceType; + +@ResourceType(name = "EnvAnimalGatherExcelConfigData.json", loadPriority = ResourceType.LoadPriority.LOW) +public class EnvAnimalGatherConfigData extends GameResource { + private int animalId; + private String entityType; + private List gatherItemId; + private String excludeWeathers; + private int aliveTime; + private int escapeTime; + private int escapeRadius; + @Override + public int getId() { + return animalId; + } + public int getAnimalId(){ + return animalId; + } + public String getEntityType(){ + return entityType; + } + public GatherItem gatherItem(){ + return gatherItemId.get(0); + } + public static class GatherItem{ + private int id; + private int count; + public int getId(){ + return id; + } + public int getCount(){ + return count; + } + } +} diff --git a/src/main/java/emu/grasscutter/data/def/EquipAffixData.java b/src/main/java/emu/grasscutter/data/excels/EquipAffixData.java similarity index 65% rename from src/main/java/emu/grasscutter/data/def/EquipAffixData.java rename to src/main/java/emu/grasscutter/data/excels/EquipAffixData.java index 9020c7b46..e7356d939 100644 --- a/src/main/java/emu/grasscutter/data/def/EquipAffixData.java +++ b/src/main/java/emu/grasscutter/data/excels/EquipAffixData.java @@ -1,4 +1,4 @@ -package emu.grasscutter.data.def; +package emu.grasscutter.data.excels; import java.util.ArrayList; import emu.grasscutter.data.GameResource; @@ -8,41 +8,41 @@ import emu.grasscutter.data.common.FightPropData; @ResourceType(name = "EquipAffixExcelConfigData.json") public class EquipAffixData extends GameResource { - private int AffixId; - private int Id; - private int Level; - private long NameTextMapHash; - private String OpenConfig; - private FightPropData[] AddProps; - private float[] ParamList; + private int affixId; + private int id; + private int level; + private long nameTextMapHash; + private String openConfig; + private FightPropData[] addProps; + private float[] paramList; @Override public int getId() { - return AffixId; + return affixId; } public int getMainId() { - return Id; + return id; } public int getLevel() { - return Level; + return level; } public long getNameTextMapHash() { - return NameTextMapHash; + return nameTextMapHash; } public String getOpenConfig() { - return OpenConfig; + return openConfig; } public FightPropData[] getAddProps() { - return AddProps; + return addProps; } public float[] getParamList() { - return ParamList; + return paramList; } @Override @@ -54,6 +54,6 @@ public class EquipAffixData extends GameResource { parsed.add(prop); } } - this.AddProps = parsed.toArray(new FightPropData[parsed.size()]); + this.addProps = parsed.toArray(new FightPropData[parsed.size()]); } } diff --git a/src/main/java/emu/grasscutter/data/def/FetterCharacterCardData.java b/src/main/java/emu/grasscutter/data/excels/FetterCharacterCardData.java similarity index 73% rename from src/main/java/emu/grasscutter/data/def/FetterCharacterCardData.java rename to src/main/java/emu/grasscutter/data/excels/FetterCharacterCardData.java index 62e323509..771bd9cb2 100644 --- a/src/main/java/emu/grasscutter/data/def/FetterCharacterCardData.java +++ b/src/main/java/emu/grasscutter/data/excels/FetterCharacterCardData.java @@ -1,24 +1,24 @@ -package emu.grasscutter.data.def; - -import emu.grasscutter.data.GameResource; -import emu.grasscutter.data.ResourceType; -import emu.grasscutter.data.ResourceType.LoadPriority; - -@ResourceType(name = "FetterCharacterCardExcelConfigData.json", loadPriority = LoadPriority.HIGHEST) -public class FetterCharacterCardData extends GameResource { - private int AvatarId; - private int RewardId; - - @Override - public int getId() { - return AvatarId; - } - - public int getRewardId() { - return RewardId; - } - - @Override - public void onLoad() { - } -} +package emu.grasscutter.data.excels; + +import emu.grasscutter.data.GameResource; +import emu.grasscutter.data.ResourceType; +import emu.grasscutter.data.ResourceType.LoadPriority; + +@ResourceType(name = "FetterCharacterCardExcelConfigData.json", loadPriority = LoadPriority.HIGHEST) +public class FetterCharacterCardData extends GameResource { + private int avatarId; + private int rewardId; + + @Override + public int getId() { + return avatarId; + } + + public int getRewardId() { + return rewardId; + } + + @Override + public void onLoad() { + } +} diff --git a/src/main/java/emu/grasscutter/data/def/FetterData.java b/src/main/java/emu/grasscutter/data/excels/FetterData.java similarity index 75% rename from src/main/java/emu/grasscutter/data/def/FetterData.java rename to src/main/java/emu/grasscutter/data/excels/FetterData.java index 697843680..101f31915 100644 --- a/src/main/java/emu/grasscutter/data/def/FetterData.java +++ b/src/main/java/emu/grasscutter/data/excels/FetterData.java @@ -1,32 +1,32 @@ -package emu.grasscutter.data.def; - -import java.util.List; - -import emu.grasscutter.data.GameResource; -import emu.grasscutter.data.ResourceType; -import emu.grasscutter.data.ResourceType.LoadPriority; -import emu.grasscutter.data.common.OpenCondData; - -@ResourceType(name = {"FetterInfoExcelConfigData.json", "FettersExcelConfigData.json", "FetterStoryExcelConfigData.json", "PhotographExpressionExcelConfigData.json", "PhotographPosenameExcelConfigData.json"}, loadPriority = LoadPriority.HIGHEST) -public class FetterData extends GameResource { - private int AvatarId; - private int FetterId; - private List OpenCond; - - @Override - public int getId() { - return FetterId; - } - - public int getAvatarId() { - return AvatarId; - } - - public List getOpenConds() { - return OpenCond; - } - - @Override - public void onLoad() { - } -} +package emu.grasscutter.data.excels; + +import java.util.List; + +import emu.grasscutter.data.GameResource; +import emu.grasscutter.data.ResourceType; +import emu.grasscutter.data.ResourceType.LoadPriority; +import emu.grasscutter.data.common.OpenCondData; + +@ResourceType(name = {"FetterInfoExcelConfigData.json", "FettersExcelConfigData.json", "FetterStoryExcelConfigData.json", "PhotographExpressionExcelConfigData.json", "PhotographPosenameExcelConfigData.json"}, loadPriority = LoadPriority.HIGHEST) +public class FetterData extends GameResource { + private int avatarId; + private int fetterId; + private List openCond; + + @Override + public int getId() { + return fetterId; + } + + public int getAvatarId() { + return avatarId; + } + + public List getOpenConds() { + return openCond; + } + + @Override + public void onLoad() { + } +} diff --git a/src/main/java/emu/grasscutter/data/def/GadgetData.java b/src/main/java/emu/grasscutter/data/excels/GadgetData.java similarity index 60% rename from src/main/java/emu/grasscutter/data/def/GadgetData.java rename to src/main/java/emu/grasscutter/data/excels/GadgetData.java index 6f1a8aa46..2cd79f6c2 100644 --- a/src/main/java/emu/grasscutter/data/def/GadgetData.java +++ b/src/main/java/emu/grasscutter/data/excels/GadgetData.java @@ -1,4 +1,4 @@ -package emu.grasscutter.data.def; +package emu.grasscutter.data.excels; import emu.grasscutter.data.GameResource; import emu.grasscutter.data.ResourceType; @@ -6,53 +6,53 @@ import emu.grasscutter.game.props.EntityType; @ResourceType(name = "GadgetExcelConfigData.json") public class GadgetData extends GameResource { - private int Id; + private int id; - private EntityType Type; - private String JsonName; - private boolean IsInteractive; - private String[] Tags; - private String ItemJsonName; - private String InteeIconName; - private long NameTextMapHash; - private int CampID; + private EntityType type; + private String jsonName; + private boolean isInteractive; + private String[] tags; + private String itemJsonName; + private String inteeIconName; + private long nameTextMapHash; + private int campID; private String LODPatternName; @Override public int getId() { - return this.Id; + return this.id; } public EntityType getType() { - return Type; + return type; } public String getJsonName() { - return JsonName; + return jsonName; } public boolean isInteractive() { - return IsInteractive; + return isInteractive; } public String[] getTags() { - return Tags; + return tags; } public String getItemJsonName() { - return ItemJsonName; + return itemJsonName; } public String getInteeIconName() { - return InteeIconName; + return inteeIconName; } public long getNameTextMapHash() { - return NameTextMapHash; + return nameTextMapHash; } public int getCampID() { - return CampID; + return campID; } public String getLODPatternName() { return LODPatternName; } diff --git a/src/main/java/emu/grasscutter/data/excels/ItemData.java b/src/main/java/emu/grasscutter/data/excels/ItemData.java new file mode 100644 index 000000000..fba8d9ec0 --- /dev/null +++ b/src/main/java/emu/grasscutter/data/excels/ItemData.java @@ -0,0 +1,267 @@ +package emu.grasscutter.data.excels; + +import java.util.List; + +import emu.grasscutter.data.GameResource; +import emu.grasscutter.data.ResourceType; +import emu.grasscutter.data.common.ItemUseData; +import emu.grasscutter.game.props.FightProperty; +import it.unimi.dsi.fastutil.ints.IntOpenHashSet; +import it.unimi.dsi.fastutil.ints.IntSet; + +@ResourceType(name = {"MaterialExcelConfigData.json", "WeaponExcelConfigData.json", "ReliquaryExcelConfigData.json"}) +public class ItemData extends GameResource { + + private int id; + private int stackLimit = 1; + private int maxUseCount; + private int rankLevel; + private String effectName; + private int[] satiationParams; + private int rank; + private int weight; + private int gadgetId; + + private int[] destroyReturnMaterial; + private int[] destroyReturnMaterialCount; + + private List itemUse; + + // Food + private String foodQuality; + private String useTarget; + private String[] iseParam; + + // String enums + private String itemType; + private String materialType; + private String equipType; + private String effectType; + private String destroyRule; + + // Relic + private int mainPropDepotId; + private int appendPropDepotId; + private int appendPropNum; + private int setId; + private int[] addPropLevels; + private int baseConvExp; + private int maxLevel; + + // Weapon + private int weaponPromoteId; + private int weaponBaseExp; + private int storyId; + private int avatarPromoteId; + private int awakenMaterial; + private int[] awakenCosts; + private int[] skillAffix; + private WeaponProperty[] weaponProp; + + // Hash + private String icon; + private long nameTextMapHash; + + // Post load + private transient emu.grasscutter.game.inventory.MaterialType materialEnumType; + private transient emu.grasscutter.game.inventory.ItemType itemEnumType; + private transient emu.grasscutter.game.inventory.EquipType equipEnumType; + + private IntSet addPropLevelSet; + + @Override + public int getId(){ + return this.id; + } + + public String getMaterialTypeString(){ + return this.materialType; + } + + public int getStackLimit(){ + return this.stackLimit; + } + + public int getMaxUseCount(){ + return this.maxUseCount; + } + + public String getUseTarget(){ + return this.useTarget; + } + + public String[] getUseParam(){ + return this.iseParam; + } + + public int getRankLevel(){ + return this.rankLevel; + } + + public String getFoodQuality(){ + return this.foodQuality; + } + + public String getEffectName(){ + return this.effectName; + } + + public int[] getSatiationParams(){ + return this.satiationParams; + } + + public int[] getDestroyReturnMaterial(){ + return this.destroyReturnMaterial; + } + + public int[] getDestroyReturnMaterialCount(){ + return this.destroyReturnMaterialCount; + } + + public List getItemUse() { + return itemUse; + } + + public long getNameTextMapHash(){ + return this.nameTextMapHash; + } + + public String getIcon(){ + return this.icon; + } + + public String getItemTypeString(){ + return this.itemType; + } + + public int getRank(){ + return this.rank; + } + + public int getGadgetId() { + return gadgetId; + } + + public int getBaseConvExp() { + return baseConvExp; + } + + public int getMainPropDepotId() { + return mainPropDepotId; + } + + public int getAppendPropDepotId() { + return appendPropDepotId; + } + + public int getAppendPropNum() { + return appendPropNum; + } + + public int getSetId() { + return setId; + } + + public int getWeaponPromoteId() { + return weaponPromoteId; + } + + public int getWeaponBaseExp() { + return weaponBaseExp; + } + + public int getAwakenMaterial() { + return awakenMaterial; + } + + public int[] getAwakenCosts() { + return awakenCosts; + } + + public IntSet getAddPropLevelSet() { + return addPropLevelSet; + } + + public int[] getSkillAffix() { + return skillAffix; + } + + public WeaponProperty[] getWeaponProperties() { + return weaponProp; + } + + public int getMaxLevel() { + return maxLevel; + } + + public emu.grasscutter.game.inventory.ItemType getItemType() { + return this.itemEnumType; + } + + public emu.grasscutter.game.inventory.MaterialType getMaterialType() { + return this.materialEnumType; + } + + public emu.grasscutter.game.inventory.EquipType getEquipType() { + return this.equipEnumType; + } + + public boolean canAddRelicProp(int level) { + return this.addPropLevelSet != null & this.addPropLevelSet.contains(level); + } + + public boolean isEquip() { + return this.itemEnumType == emu.grasscutter.game.inventory.ItemType.ITEM_RELIQUARY || this.itemEnumType == emu.grasscutter.game.inventory.ItemType.ITEM_WEAPON; + } + + @Override + public void onLoad() { + this.itemEnumType = emu.grasscutter.game.inventory.ItemType.getTypeByName(getItemTypeString()); + this.materialEnumType = emu.grasscutter.game.inventory.MaterialType.getTypeByName(getMaterialTypeString()); + + if (this.itemEnumType == emu.grasscutter.game.inventory.ItemType.ITEM_RELIQUARY) { + this.equipEnumType = emu.grasscutter.game.inventory.EquipType.getTypeByName(this.equipType); + if (this.addPropLevels != null && this.addPropLevels.length > 0) { + this.addPropLevelSet = new IntOpenHashSet(this.addPropLevels); + } + } else if (this.itemEnumType == emu.grasscutter.game.inventory.ItemType.ITEM_WEAPON) { + this.equipEnumType = emu.grasscutter.game.inventory.EquipType.EQUIP_WEAPON; + } else { + this.equipEnumType = emu.grasscutter.game.inventory.EquipType.EQUIP_NONE; + } + + if (this.getWeaponProperties() != null) { + for (WeaponProperty weaponProperty : this.getWeaponProperties()) { + weaponProperty.onLoad(); + } + } + } + + public static class WeaponProperty { + private FightProperty fightProp; + private String propType; + private float initValue; + private String type; + + public String getPropType(){ + return this.propType; + } + + public float getInitValue(){ + return this.initValue; + } + + public String getType(){ + return this.type; + } + + public FightProperty getFightProp() { + return fightProp; + } + + public void onLoad() { + this.fightProp = FightProperty.getPropByName(propType); + } + + } +} diff --git a/src/main/java/emu/grasscutter/data/def/MonsterCurveData.java b/src/main/java/emu/grasscutter/data/excels/MonsterCurveData.java similarity index 57% rename from src/main/java/emu/grasscutter/data/def/MonsterCurveData.java rename to src/main/java/emu/grasscutter/data/excels/MonsterCurveData.java index 07c705403..29e7da2fb 100644 --- a/src/main/java/emu/grasscutter/data/def/MonsterCurveData.java +++ b/src/main/java/emu/grasscutter/data/excels/MonsterCurveData.java @@ -1,4 +1,4 @@ -package emu.grasscutter.data.def; +package emu.grasscutter.data.excels; import java.util.HashMap; import java.util.Map; @@ -10,23 +10,23 @@ import emu.grasscutter.data.common.CurveInfo; @ResourceType(name = "MonsterCurveExcelConfigData.json") public class MonsterCurveData extends GameResource { - private int Level; - private CurveInfo[] CurveInfos; + private int level; + private CurveInfo[] curveInfos; - private Map curveInfos; + private Map curveInfoMap; @Override public int getId() { - return Level; + return level; } public float getMultByProp(String fightProp) { - return curveInfos.getOrDefault(fightProp, 1f); + return curveInfoMap.getOrDefault(fightProp, 1f); } @Override public void onLoad() { - this.curveInfos = new HashMap<>(); - Stream.of(this.CurveInfos).forEach(info -> this.curveInfos.put(info.getType(), info.getValue())); + this.curveInfoMap = new HashMap<>(); + Stream.of(this.curveInfos).forEach(info -> this.curveInfoMap.put(info.getType(), info.getValue())); } } diff --git a/src/main/java/emu/grasscutter/data/def/MonsterData.java b/src/main/java/emu/grasscutter/data/excels/MonsterData.java similarity index 56% rename from src/main/java/emu/grasscutter/data/def/MonsterData.java rename to src/main/java/emu/grasscutter/data/excels/MonsterData.java index 341a10ce0..758937fda 100644 --- a/src/main/java/emu/grasscutter/data/def/MonsterData.java +++ b/src/main/java/emu/grasscutter/data/excels/MonsterData.java @@ -1,4 +1,4 @@ -package emu.grasscutter.data.def; +package emu.grasscutter.data.excels; import java.util.List; @@ -7,155 +7,157 @@ import emu.grasscutter.data.GameResource; import emu.grasscutter.data.ResourceType; import emu.grasscutter.data.ResourceType.LoadPriority; import emu.grasscutter.data.common.PropGrowCurve; +import emu.grasscutter.game.props.MonsterType; @ResourceType(name = "MonsterExcelConfigData.json", loadPriority = LoadPriority.LOW) public class MonsterData extends GameResource { - private int Id; + private int id; - private String MonsterName; - private String Type; - private String ServerScript; - private List Affix; - private String Ai; - private int[] Equips; - private List HpDrops; - private int KillDropId; - private String ExcludeWeathers; - private int FeatureTagGroupID; - private int MpPropID; - private String Skin; - private int DescribeId; - private int CombatBGMLevel; - private int EntityBudgetLevel; - private float HpBase; - private float AttackBase; - private float DefenseBase; - private float FireSubHurt; - private float ElecSubHurt; - private float GrassSubHurt; - private float WaterSubHurt; - private float WindSubHurt; - private float RockSubHurt; - private float IceSubHurt; - private float PhysicalSubHurt; - private List PropGrowCurves; - private long NameTextMapHash; - private int CampID; + private String monsterName; + private MonsterType type; + private String serverScript; + private List affix; + private String ai; + private int[] equips; + private List hpDrops; + private int killDropId; + private String excludeWeathers; + private int featureTagGroupID; + private int mpPropID; + private String skin; + private int describeId; + private int combatBGMLevel; + private int entityBudgetLevel; + private float hpBase; + private float attackBase; + private float defenseBase; + private float fireSubHurt; + private float elecSubHurt; + private float grassSubHurt; + private float waterSubHurt; + private float windSubHurt; + private float rockSubHurt; + private float iceSubHurt; + private float physicalSubHurt; + private List propGrowCurves; + private long nameTextMapHash; + private int campID; + // Transient private int weaponId; private MonsterDescribeData describeData; @Override public int getId() { - return this.Id; + return this.id; } public String getMonsterName() { - return MonsterName; + return monsterName; } - public String getType() { - return Type; + public MonsterType getType() { + return type; } public String getServerScript() { - return ServerScript; + return serverScript; } public List getAffix() { - return Affix; + return affix; } public String getAi() { - return Ai; + return ai; } public int[] getEquips() { - return Equips; + return equips; } public List getHpDrops() { - return HpDrops; + return hpDrops; } public int getKillDropId() { - return KillDropId; + return killDropId; } public String getExcludeWeathers() { - return ExcludeWeathers; + return excludeWeathers; } public int getFeatureTagGroupID() { - return FeatureTagGroupID; + return featureTagGroupID; } public int getMpPropID() { - return MpPropID; + return mpPropID; } public String getSkin() { - return Skin; + return skin; } public int getDescribeId() { - return DescribeId; + return describeId; } public int getCombatBGMLevel() { - return CombatBGMLevel; + return combatBGMLevel; } public int getEntityBudgetLevel() { - return EntityBudgetLevel; + return entityBudgetLevel; } public float getBaseHp() { - return HpBase; + return hpBase; } public float getBaseAttack() { - return AttackBase; + return attackBase; } public float getBaseDefense() { - return DefenseBase; + return defenseBase; } public float getElecSubHurt() { - return ElecSubHurt; + return elecSubHurt; } public float getGrassSubHurt() { - return GrassSubHurt; + return grassSubHurt; } public float getWaterSubHurt() { - return WaterSubHurt; + return waterSubHurt; } public float getWindSubHurt() { - return WindSubHurt; + return windSubHurt; } public float getIceSubHurt() { - return IceSubHurt; + return iceSubHurt; } public float getPhysicalSubHurt() { - return PhysicalSubHurt; + return physicalSubHurt; } public List getPropGrowCurves() { - return PropGrowCurves; + return propGrowCurves; } public long getNameTextMapHash() { - return NameTextMapHash; + return nameTextMapHash; } public int getCampID() { - return CampID; + return campID; } public MonsterDescribeData getDescribeData() { @@ -170,7 +172,7 @@ public class MonsterData extends GameResource { public void onLoad() { this.describeData = GameData.getMonsterDescribeDataMap().get(this.getDescribeId()); - for (int id : this.Equips) { + for (int id : this.equips) { if (id == 0) { continue; } diff --git a/src/main/java/emu/grasscutter/data/def/MonsterDescribeData.java b/src/main/java/emu/grasscutter/data/excels/MonsterDescribeData.java similarity index 65% rename from src/main/java/emu/grasscutter/data/def/MonsterDescribeData.java rename to src/main/java/emu/grasscutter/data/excels/MonsterDescribeData.java index 41b826c5b..9f6c81e25 100644 --- a/src/main/java/emu/grasscutter/data/def/MonsterDescribeData.java +++ b/src/main/java/emu/grasscutter/data/excels/MonsterDescribeData.java @@ -1,4 +1,4 @@ -package emu.grasscutter.data.def; +package emu.grasscutter.data.excels; import emu.grasscutter.data.GameResource; import emu.grasscutter.data.ResourceType; @@ -6,31 +6,31 @@ import emu.grasscutter.data.ResourceType.LoadPriority; @ResourceType(name = "MonsterDescribeExcelConfigData.json", loadPriority = LoadPriority.HIGH) public class MonsterDescribeData extends GameResource { - private int Id; - private long NameTextMapHash; - private int TitleID; - private int SpecialNameLabID; - private String Icon; + private int id; + private long nameTextMapHash; + private int titleID; + private int specialNameLabID; + private String icon; @Override public int getId() { - return Id; + return id; } public long getNameTextMapHash() { - return NameTextMapHash; + return nameTextMapHash; } public int getTitleID() { - return TitleID; + return titleID; } public int getSpecialNameLabID() { - return SpecialNameLabID; + return specialNameLabID; } public String getIcon() { - return Icon; + return icon; } @Override diff --git a/src/main/java/emu/grasscutter/data/def/NpcData.java b/src/main/java/emu/grasscutter/data/excels/NpcData.java similarity index 51% rename from src/main/java/emu/grasscutter/data/def/NpcData.java rename to src/main/java/emu/grasscutter/data/excels/NpcData.java index 37fa78776..cd6323525 100644 --- a/src/main/java/emu/grasscutter/data/def/NpcData.java +++ b/src/main/java/emu/grasscutter/data/excels/NpcData.java @@ -1,68 +1,68 @@ -package emu.grasscutter.data.def; +package emu.grasscutter.data.excels; import emu.grasscutter.data.GameResource; import emu.grasscutter.data.ResourceType; @ResourceType(name = "NpcExcelConfigData.json") public class NpcData extends GameResource { - private int Id; + private int id; - private String JsonName; - private String Alias; - private String ScriptDataPath; - private String LuaDataPath; + private String jsonName; + private String alias; + private String scriptDataPath; + private String luaDataPath; - private boolean IsInteractive; - private boolean HasMove; - private String DyePart; - private String BillboardIcon; + private boolean isInteractive; + private boolean hasMove; + private String dyePart; + private String billboardIcon; - private long NameTextMapHash; - private int CampID; + private long nameTextMapHash; + private int campID; @Override public int getId() { - return this.Id; + return this.id; } public String getJsonName() { - return JsonName; + return jsonName; } public String getAlias() { - return Alias; + return alias; } public String getScriptDataPath() { - return ScriptDataPath; + return scriptDataPath; } public String getLuaDataPath() { - return LuaDataPath; + return luaDataPath; } public boolean isIsInteractive() { - return IsInteractive; + return isInteractive; } public boolean isHasMove() { - return HasMove; + return hasMove; } public String getDyePart() { - return DyePart; + return dyePart; } public String getBillboardIcon() { - return BillboardIcon; + return billboardIcon; } public long getNameTextMapHash() { - return NameTextMapHash; + return nameTextMapHash; } public int getCampID() { - return CampID; + return campID; } @Override diff --git a/src/main/java/emu/grasscutter/data/def/PlayerLevelData.java b/src/main/java/emu/grasscutter/data/excels/PlayerLevelData.java similarity index 61% rename from src/main/java/emu/grasscutter/data/def/PlayerLevelData.java rename to src/main/java/emu/grasscutter/data/excels/PlayerLevelData.java index 1e02e3eca..646c06e3b 100644 --- a/src/main/java/emu/grasscutter/data/def/PlayerLevelData.java +++ b/src/main/java/emu/grasscutter/data/excels/PlayerLevelData.java @@ -1,33 +1,33 @@ -package emu.grasscutter.data.def; +package emu.grasscutter.data.excels; import emu.grasscutter.data.GameResource; import emu.grasscutter.data.ResourceType; @ResourceType(name = "PlayerLevelExcelConfigData.json") public class PlayerLevelData extends GameResource { - private int Level; - private int Exp; - private int RewardId; - private int UnlockWorldLevel; + private int level; + private int exp; + private int rewardId; + private int unlockWorldLevel; @Override public int getId() { - return this.Level; + return this.level; } public int getLevel() { - return Level; + return level; } public int getExp() { - return Exp; + return exp; } public int getRewardId() { - return RewardId; + return rewardId; } public int getUnlockWorldLevel() { - return UnlockWorldLevel; + return unlockWorldLevel; } } diff --git a/src/main/java/emu/grasscutter/data/def/ProudSkillData.java b/src/main/java/emu/grasscutter/data/excels/ProudSkillData.java similarity index 57% rename from src/main/java/emu/grasscutter/data/def/ProudSkillData.java rename to src/main/java/emu/grasscutter/data/excels/ProudSkillData.java index 3eff8b94d..34701ffe5 100644 --- a/src/main/java/emu/grasscutter/data/def/ProudSkillData.java +++ b/src/main/java/emu/grasscutter/data/excels/ProudSkillData.java @@ -1,4 +1,4 @@ -package emu.grasscutter.data.def; +package emu.grasscutter.data.excels; import java.util.ArrayList; import java.util.List; @@ -11,82 +11,82 @@ import emu.grasscutter.data.common.ItemParamData; @ResourceType(name = "ProudSkillExcelConfigData.json") public class ProudSkillData extends GameResource { - private int ProudSkillId; - private int ProudSkillGroupId; - private int Level; - private int CoinCost; - private int BreakLevel; - private int ProudSkillType; - private String OpenConfig; - private List CostItems; - private List FilterConds; - private List LifeEffectParams; - private FightPropData[] AddProps; - private float[] ParamList; - private long[] ParamDescList; - private long NameTextMapHash; + private int proudSkillId; + private int proudSkillGroupId; + private int level; + private int coinCost; + private int breakLevel; + private int proudSkillType; + private String openConfig; + private List costItems; + private List filterConds; + private List lifeEffectParams; + private FightPropData[] addProps; + private float[] paramList; + private long[] paramDescList; + private long nameTextMapHash; @Override public int getId() { - return ProudSkillId; + return proudSkillId; } public int getProudSkillGroupId() { - return ProudSkillGroupId; + return proudSkillGroupId; } public int getLevel() { - return Level; + return level; } public int getCoinCost() { - return CoinCost; + return coinCost; } public int getBreakLevel() { - return BreakLevel; + return breakLevel; } public int getProudSkillType() { - return ProudSkillType; + return proudSkillType; } public String getOpenConfig() { - return OpenConfig; + return openConfig; } public List getCostItems() { - return CostItems; + return costItems; } public List getFilterConds() { - return FilterConds; + return filterConds; } public List getLifeEffectParams() { - return LifeEffectParams; + return lifeEffectParams; } public FightPropData[] getAddProps() { - return AddProps; + return addProps; } public float[] getParamList() { - return ParamList; + return paramList; } public long[] getParamDescList() { - return ParamDescList; + return paramDescList; } public long getNameTextMapHash() { - return NameTextMapHash; + return nameTextMapHash; } @Override public void onLoad() { if (this.getOpenConfig() != null & this.getOpenConfig().length() > 0) { - this.OpenConfig = "Avatar_" + this.getOpenConfig(); + this.openConfig = "Avatar_" + this.getOpenConfig(); } // Fight props ArrayList parsed = new ArrayList(getAddProps().length); @@ -96,6 +96,6 @@ public class ProudSkillData extends GameResource { parsed.add(prop); } } - this.AddProps = parsed.toArray(new FightPropData[parsed.size()]); + this.addProps = parsed.toArray(new FightPropData[parsed.size()]); } } diff --git a/src/main/java/emu/grasscutter/data/excels/QuestData.java b/src/main/java/emu/grasscutter/data/excels/QuestData.java new file mode 100644 index 000000000..7866eeb45 --- /dev/null +++ b/src/main/java/emu/grasscutter/data/excels/QuestData.java @@ -0,0 +1,126 @@ +package emu.grasscutter.data.excels; + +import java.util.Arrays; +import java.util.List; + +import emu.grasscutter.data.GameResource; +import emu.grasscutter.data.ResourceType; +import emu.grasscutter.game.quest.enums.LogicType; +import emu.grasscutter.game.quest.enums.QuestTrigger; + +@ResourceType(name = "QuestExcelConfigData.json") +public class QuestData extends GameResource { + private int subId; + private int mainId; + private int order; + private long descTextMapHash; + + private boolean finishParent; + private boolean isRewind; + + private LogicType acceptCondComb; + private QuestCondition[] acceptConditons; + private LogicType finishCondComb; + private QuestCondition[] finishConditons; + private LogicType failCondComb; + private QuestCondition[] failConditons; + + private List acceptCond; + private List finishCond; + private List failCond; + private List beginExec; + private List finishExec; + private List failExec; + + public int getId() { + return subId; + } + + public int getMainId() { + return mainId; + } + + public int getOrder() { + return order; + } + + public long getDescTextMapHash() { + return descTextMapHash; + } + + public boolean finishParent() { + return finishParent; + } + + public boolean isRewind() { + return isRewind; + } + + public LogicType getAcceptCondComb() { + return acceptCondComb; + } + + public QuestCondition[] getAcceptCond() { + return acceptConditons; + } + + public LogicType getFinishCondComb() { + return finishCondComb; + } + + public QuestCondition[] getFinishCond() { + return finishConditons; + } + + public LogicType getFailCondComb() { + return failCondComb; + } + + public QuestCondition[] getFailCond() { + return failConditons; + } + + public void onLoad() { + this.acceptConditons = acceptCond.stream().filter(p -> p._type != null).map(QuestCondition::new).toArray(QuestCondition[]::new); + acceptCond = null; + this.finishConditons = finishCond.stream().filter(p -> p._type != null).map(QuestCondition::new).toArray(QuestCondition[]::new); + finishCond = null; + this.failConditons = failCond.stream().filter(p -> p._type != null).map(QuestCondition::new).toArray(QuestCondition[]::new); + failCond = null; + } + + public class QuestParam { + QuestTrigger _type; + int[] _param; + String _count; + } + + public class QuestExecParam { + QuestTrigger _type; + String[] _param; + String _count; + } + + public static class QuestCondition { + private QuestTrigger type; + private int[] param; + private String count; + + public QuestCondition(QuestParam param) { + this.type = param._type; + this.param = param._param; + } + + public QuestTrigger getType() { + return type; + } + + public int[] getParam() { + return param; + } + + public String getCount() { + return count; + } + } +} diff --git a/src/main/java/emu/grasscutter/data/def/ReliquaryAffixData.java b/src/main/java/emu/grasscutter/data/excels/ReliquaryAffixData.java similarity index 51% rename from src/main/java/emu/grasscutter/data/def/ReliquaryAffixData.java rename to src/main/java/emu/grasscutter/data/excels/ReliquaryAffixData.java index ba9b1d369..cd4a04497 100644 --- a/src/main/java/emu/grasscutter/data/def/ReliquaryAffixData.java +++ b/src/main/java/emu/grasscutter/data/excels/ReliquaryAffixData.java @@ -1,4 +1,4 @@ -package emu.grasscutter.data.def; +package emu.grasscutter.data.excels; import emu.grasscutter.data.GameResource; import emu.grasscutter.data.ResourceType; @@ -6,43 +6,41 @@ import emu.grasscutter.game.props.FightProperty; @ResourceType(name = "ReliquaryAffixExcelConfigData.json") public class ReliquaryAffixData extends GameResource { - private int Id; + private int id; - private int DepotId; - private int GroupId; - private String PropType; - private float PropValue; - private int Weight; - private int UpgradeWeight; - - private FightProperty fightProp; + private int depotId; + private int groupId; + private FightProperty propType; + private float propValue; + private int weight; + private int upgradeWeight; @Override public int getId() { - return Id; + return id; } + public int getDepotId() { - return DepotId; + return depotId; } + public int getGroupId() { - return GroupId; + return groupId; } + public float getPropValue() { - return PropValue; + return propValue; } + public int getWeight() { - return Weight; + return weight; } + public int getUpgradeWeight() { - return UpgradeWeight; + return upgradeWeight; } public FightProperty getFightProp() { - return fightProp; - } - - @Override - public void onLoad() { - this.fightProp = FightProperty.getPropByName(this.PropType); + return propType; } } diff --git a/src/main/java/emu/grasscutter/data/def/ReliquaryLevelData.java b/src/main/java/emu/grasscutter/data/excels/ReliquaryLevelData.java similarity index 74% rename from src/main/java/emu/grasscutter/data/def/ReliquaryLevelData.java rename to src/main/java/emu/grasscutter/data/excels/ReliquaryLevelData.java index b4e37dd90..488cca1e6 100644 --- a/src/main/java/emu/grasscutter/data/def/ReliquaryLevelData.java +++ b/src/main/java/emu/grasscutter/data/excels/ReliquaryLevelData.java @@ -1,4 +1,4 @@ -package emu.grasscutter.data.def; +package emu.grasscutter.data.excels; import java.util.List; @@ -13,10 +13,10 @@ public class ReliquaryLevelData extends GameResource { private int id; private Int2ObjectMap propMap; - private int Rank; - private int Level; - private int Exp; - private List AddProps; + private int rank; + private int level; + private int exp; + private List addProps; @Override public int getId() { @@ -24,15 +24,15 @@ public class ReliquaryLevelData extends GameResource { } public int getRank() { - return Rank; + return rank; } public int getLevel() { - return Level; + return level; } public int getExp() { - return Exp; + return exp; } public float getPropValue(FightProperty prop) { @@ -45,23 +45,23 @@ public class ReliquaryLevelData extends GameResource { @Override public void onLoad() { - this.id = (Rank << 8) + this.getLevel(); + this.id = (rank << 8) + this.getLevel(); this.propMap = new Int2ObjectOpenHashMap<>(); - for (RelicLevelProperty p : AddProps) { + for (RelicLevelProperty p : addProps) { this.propMap.put(FightProperty.getPropByName(p.getPropType()).getId(), (Float) p.getValue()); } } public class RelicLevelProperty { - private String PropType; - private float Value; + private String propType; + private float value; public String getPropType() { - return PropType; + return propType; } public float getValue() { - return Value; + return value; } } } diff --git a/src/main/java/emu/grasscutter/data/def/ReliquaryMainPropData.java b/src/main/java/emu/grasscutter/data/excels/ReliquaryMainPropData.java similarity index 53% rename from src/main/java/emu/grasscutter/data/def/ReliquaryMainPropData.java rename to src/main/java/emu/grasscutter/data/excels/ReliquaryMainPropData.java index 6b2c920e4..5039849ff 100644 --- a/src/main/java/emu/grasscutter/data/def/ReliquaryMainPropData.java +++ b/src/main/java/emu/grasscutter/data/excels/ReliquaryMainPropData.java @@ -1,4 +1,4 @@ -package emu.grasscutter.data.def; +package emu.grasscutter.data.excels; import emu.grasscutter.data.GameResource; import emu.grasscutter.data.ResourceType; @@ -6,32 +6,26 @@ import emu.grasscutter.game.props.FightProperty; @ResourceType(name = "ReliquaryMainPropExcelConfigData.json") public class ReliquaryMainPropData extends GameResource { - private int Id; + private int id; - private int PropDepotId; - private String PropType; - private String AffixName; - private int Weight; - - private FightProperty fightProp; + private int propDepotId; + private FightProperty propType; + private int weight; @Override public int getId() { - return Id; + return id; } + public int getPropDepotId() { - return PropDepotId; + return propDepotId; } + public int getWeight() { - return Weight; + return weight; } public FightProperty getFightProp() { - return fightProp; - } - - @Override - public void onLoad() { - this.fightProp = FightProperty.getPropByName(this.PropType); + return propType; } } diff --git a/src/main/java/emu/grasscutter/data/def/ReliquarySetData.java b/src/main/java/emu/grasscutter/data/excels/ReliquarySetData.java similarity index 67% rename from src/main/java/emu/grasscutter/data/def/ReliquarySetData.java rename to src/main/java/emu/grasscutter/data/excels/ReliquarySetData.java index 9eac2c050..910b8d724 100644 --- a/src/main/java/emu/grasscutter/data/def/ReliquarySetData.java +++ b/src/main/java/emu/grasscutter/data/excels/ReliquarySetData.java @@ -1,23 +1,23 @@ -package emu.grasscutter.data.def; +package emu.grasscutter.data.excels; import emu.grasscutter.data.GameResource; import emu.grasscutter.data.ResourceType; @ResourceType(name = "ReliquarySetExcelConfigData.json") public class ReliquarySetData extends GameResource { - private int SetId; - private int[] SetNeedNum; + private int setId; + private int[] setNeedNum; private int EquipAffixId; - private int DisableFilter; - private int[] ContainsList; + private int disableFilter; + private int[] containsList; @Override public int getId() { - return SetId; + return setId; } public int[] getSetNeedNum() { - return SetNeedNum; + return setNeedNum; } public int getEquipAffixId() { @@ -25,11 +25,11 @@ public class ReliquarySetData extends GameResource { } public int getDisableFilter() { - return DisableFilter; + return disableFilter; } public int[] getContainsList() { - return ContainsList; + return containsList; } @Override diff --git a/src/main/java/emu/grasscutter/data/excels/RewardData.java b/src/main/java/emu/grasscutter/data/excels/RewardData.java new file mode 100644 index 000000000..7df00b641 --- /dev/null +++ b/src/main/java/emu/grasscutter/data/excels/RewardData.java @@ -0,0 +1,27 @@ +package emu.grasscutter.data.excels; + +import java.util.List; + +import emu.grasscutter.data.GameResource; +import emu.grasscutter.data.ResourceType; +import emu.grasscutter.data.common.ItemParamData; + +@ResourceType(name = "RewardExcelConfigData.json") +public class RewardData extends GameResource { + public int rewardId; + public List rewardItemList; + + @Override + public int getId() { + return rewardId; + } + + public List getRewardItemList() { + return rewardItemList; + } + + @Override + public void onLoad() { + rewardItemList = rewardItemList.stream().filter(i -> i.getId() > 0).toList(); + } +} diff --git a/src/main/java/emu/grasscutter/data/def/RewardPreviewData.java b/src/main/java/emu/grasscutter/data/excels/RewardPreviewData.java similarity index 70% rename from src/main/java/emu/grasscutter/data/def/RewardPreviewData.java rename to src/main/java/emu/grasscutter/data/excels/RewardPreviewData.java index f971ee1cf..4a783f2b5 100644 --- a/src/main/java/emu/grasscutter/data/def/RewardPreviewData.java +++ b/src/main/java/emu/grasscutter/data/excels/RewardPreviewData.java @@ -1,4 +1,4 @@ -package emu.grasscutter.data.def; +package emu.grasscutter.data.excels; import java.util.Arrays; import java.util.List; @@ -16,28 +16,28 @@ import emu.grasscutter.data.common.ItemParamStringData; @ResourceType(name = "RewardPreviewExcelConfigData.json", loadPriority = LoadPriority.HIGH) public class RewardPreviewData extends GameResource { - private int Id; - private ItemParamStringData[] PreviewItems; - private ItemParamData[] PreviewItemsArray; + private int id; + private ItemParamStringData[] previewItems; + private ItemParamData[] previewItemsArray; @Override public int getId() { - return this.Id; + return this.id; } public ItemParamData[] getPreviewItems() { - return PreviewItemsArray; + return previewItemsArray; } @Override public void onLoad() { - if (this.PreviewItems != null && this.PreviewItems.length > 0) { - this.PreviewItemsArray = Arrays.stream(this.PreviewItems) + if (this.previewItems != null && this.previewItems.length > 0) { + this.previewItemsArray = Arrays.stream(this.previewItems) .filter(d -> d.getId() > 0 && d.getCount() != null && !d.getCount().isEmpty()) .map(ItemParamStringData::toItemParamData) .toArray(size -> new ItemParamData[size]); } else { - this.PreviewItemsArray = new ItemParamData[0]; + this.previewItemsArray = new ItemParamData[0]; } } } diff --git a/src/main/java/emu/grasscutter/data/def/SceneData.java b/src/main/java/emu/grasscutter/data/excels/SceneData.java similarity index 71% rename from src/main/java/emu/grasscutter/data/def/SceneData.java rename to src/main/java/emu/grasscutter/data/excels/SceneData.java index cd1510e1d..a0b9583c8 100644 --- a/src/main/java/emu/grasscutter/data/def/SceneData.java +++ b/src/main/java/emu/grasscutter/data/excels/SceneData.java @@ -1,4 +1,4 @@ -package emu.grasscutter.data.def; +package emu.grasscutter.data.excels; import emu.grasscutter.data.GameData; import emu.grasscutter.data.GameResource; @@ -8,22 +8,21 @@ import emu.grasscutter.game.props.SceneType; @ResourceType(name = "SceneExcelConfigData.json") public class SceneData extends GameResource { - private int Id; - private SceneType Type; - private String ScriptData; + private int id; + private SceneType type; + private String scriptData; - @Override public int getId() { - return this.Id; + return this.id; } public SceneType getSceneType() { - return Type; + return type; } public String getScriptData() { - return ScriptData; + return scriptData; } @Override diff --git a/src/main/java/emu/grasscutter/data/def/ShopGoodsData.java b/src/main/java/emu/grasscutter/data/excels/ShopGoodsData.java similarity index 57% rename from src/main/java/emu/grasscutter/data/def/ShopGoodsData.java rename to src/main/java/emu/grasscutter/data/excels/ShopGoodsData.java index 1a4168637..4c0a14b58 100644 --- a/src/main/java/emu/grasscutter/data/def/ShopGoodsData.java +++ b/src/main/java/emu/grasscutter/data/excels/ShopGoodsData.java @@ -1,4 +1,4 @@ -package emu.grasscutter.data.def; +package emu.grasscutter.data.excels; import emu.grasscutter.data.GameResource; import emu.grasscutter.data.ResourceType; @@ -9,34 +9,34 @@ import java.util.List; @ResourceType(name = "ShopGoodsExcelConfigData.json") public class ShopGoodsData extends GameResource { - private int GoodsId; - private int ShopType; - private int ItemId; + private int goodsId; + private int shopType; + private int itemId; - private int ItemCount; + private int itemCount; - private int CostScoin; - private int CostHcoin; - private int CostMcoin; + private int costScoin; + private int costHcoin; + private int costMcoin; - private List CostItems; - private int MinPlayerLevel; - private int MaxPlayerLevel; + private List costItems; + private int minPlayerLevel; + private int maxPlayerLevel; - private int BuyLimit; - private int SubTabId; + private int buyLimit; + private int subTabId; - private String RefreshType; - private transient ShopInfo.ShopRefreshType RefreshTypeEnum; + private String refreshType; + private transient ShopInfo.ShopRefreshType refreshTypeEnum; - private int RefreshParam; + private int refreshParam; @Override public void onLoad() { - if (this.RefreshType == null) - this.RefreshTypeEnum = ShopInfo.ShopRefreshType.NONE; + if (this.refreshType == null) + this.refreshTypeEnum = ShopInfo.ShopRefreshType.NONE; else { - this.RefreshTypeEnum = switch (this.RefreshType) { + this.refreshTypeEnum = switch (this.refreshType) { case "SHOP_REFRESH_DAILY" -> ShopInfo.ShopRefreshType.SHOP_REFRESH_DAILY; case "SHOP_REFRESH_WEEKLY" -> ShopInfo.ShopRefreshType.SHOP_REFRESH_WEEKLY; case "SHOP_REFRESH_MONTHLY" -> ShopInfo.ShopRefreshType.SHOP_REFRESH_MONTHLY; @@ -51,58 +51,58 @@ public class ShopGoodsData extends GameResource { } public int getGoodsId() { - return GoodsId; + return goodsId; } public int getShopType() { - return ShopType; + return shopType; } public int getItemId() { - return ItemId; + return itemId; } public int getItemCount() { - return ItemCount; + return itemCount; } public int getCostScoin() { - return CostScoin; + return costScoin; } public int getCostHcoin() { - return CostHcoin; + return costHcoin; } public int getCostMcoin() { - return CostMcoin; + return costMcoin; } public List getCostItems() { - return CostItems; + return costItems; } public int getMinPlayerLevel() { - return MinPlayerLevel; + return minPlayerLevel; } public int getMaxPlayerLevel() { - return MaxPlayerLevel; + return maxPlayerLevel; } public int getBuyLimit() { - return BuyLimit; + return buyLimit; } public int getSubTabId() { - return SubTabId; + return subTabId; } public ShopInfo.ShopRefreshType getRefreshType() { - return RefreshTypeEnum; + return refreshTypeEnum; } public int getRefreshParam() { - return RefreshParam; + return refreshParam; } } diff --git a/src/main/java/emu/grasscutter/data/excels/TowerFloorData.java b/src/main/java/emu/grasscutter/data/excels/TowerFloorData.java new file mode 100644 index 000000000..6cb3008f0 --- /dev/null +++ b/src/main/java/emu/grasscutter/data/excels/TowerFloorData.java @@ -0,0 +1,49 @@ +package emu.grasscutter.data.excels; + +import emu.grasscutter.data.GameResource; +import emu.grasscutter.data.ResourceType; + +@ResourceType(name = "TowerFloorExcelConfigData.json") +public class TowerFloorData extends GameResource { + + private int floorId; + private int floorIndex; + private int levelGroupId; + private int overrideMonsterLevel; + private int teamNum; + private int floorLevelConfigId; + + @Override + public int getId() { + 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; + } +} diff --git a/src/main/java/emu/grasscutter/data/excels/TowerLevelData.java b/src/main/java/emu/grasscutter/data/excels/TowerLevelData.java new file mode 100644 index 000000000..5bfcc61fb --- /dev/null +++ b/src/main/java/emu/grasscutter/data/excels/TowerLevelData.java @@ -0,0 +1,34 @@ +package emu.grasscutter.data.excels; + +import emu.grasscutter.data.GameResource; +import emu.grasscutter.data.ResourceType; + +@ResourceType(name = "TowerLevelExcelConfigData.json") +public class TowerLevelData extends GameResource { + + private int levelId; + private int levelIndex; + private int levelGroupId; + private int dungeonId; + + @Override + public int getId() { + return this.getLevelId(); + } + + public int getLevelId() { + return levelId; + } + + public int getLevelGroupId() { + return levelGroupId; + } + + public int getLevelIndex() { + return levelIndex; + } + + public int getDungeonId() { + return dungeonId; + } +} diff --git a/src/main/java/emu/grasscutter/data/excels/TowerScheduleData.java b/src/main/java/emu/grasscutter/data/excels/TowerScheduleData.java new file mode 100644 index 000000000..fc8a0cfc2 --- /dev/null +++ b/src/main/java/emu/grasscutter/data/excels/TowerScheduleData.java @@ -0,0 +1,51 @@ +package emu.grasscutter.data.excels; + +import emu.grasscutter.data.GameResource; +import emu.grasscutter.data.ResourceType; + +import java.util.List; + +@ResourceType(name = "TowerScheduleExcelConfigData.json") +public class TowerScheduleData extends GameResource { + private int scheduleId; + private List entranceFloorId; + private List schedules; + private int monthlyLevelConfigId; + + @Override + public int getId() { + return scheduleId; + } + + @Override + public void onLoad() { + super.onLoad(); + this.schedules = this.schedules.stream() + .filter(item -> item.getFloorList().size() > 0) + .toList(); + } + + public int getScheduleId() { + return scheduleId; + } + + public List getEntranceFloorId() { + return entranceFloorId; + } + + public List getSchedules() { + return schedules; + } + + public int getMonthlyLevelConfigId() { + return monthlyLevelConfigId; + } + + public static class ScheduleDetail{ + private List floorList; + + public List getFloorList() { + return floorList; + } + } +} diff --git a/src/main/java/emu/grasscutter/data/def/WeaponCurveData.java b/src/main/java/emu/grasscutter/data/excels/WeaponCurveData.java similarity index 56% rename from src/main/java/emu/grasscutter/data/def/WeaponCurveData.java rename to src/main/java/emu/grasscutter/data/excels/WeaponCurveData.java index 287ce8af6..05e64c8ed 100644 --- a/src/main/java/emu/grasscutter/data/def/WeaponCurveData.java +++ b/src/main/java/emu/grasscutter/data/excels/WeaponCurveData.java @@ -1,4 +1,4 @@ -package emu.grasscutter.data.def; +package emu.grasscutter.data.excels; import java.util.HashMap; import java.util.Map; @@ -10,23 +10,23 @@ import emu.grasscutter.data.common.CurveInfo; @ResourceType(name = "WeaponCurveExcelConfigData.json") public class WeaponCurveData extends GameResource { - private int Level; - private CurveInfo[] CurveInfos; + private int level; + private CurveInfo[] curveInfos; - private Map curveInfos; + private Map curveInfosMap; @Override public int getId() { - return Level; + return level; } public float getMultByProp(String fightProp) { - return curveInfos.getOrDefault(fightProp, 1f); + return curveInfosMap.getOrDefault(fightProp, 1f); } @Override public void onLoad() { - this.curveInfos = new HashMap<>(); - Stream.of(this.CurveInfos).forEach(info -> this.curveInfos.put(info.getType(), info.getValue())); + this.curveInfosMap = new HashMap<>(); + Stream.of(this.curveInfos).forEach(info -> this.curveInfosMap.put(info.getType(), info.getValue())); } } diff --git a/src/main/java/emu/grasscutter/data/def/WeaponLevelData.java b/src/main/java/emu/grasscutter/data/excels/WeaponLevelData.java similarity index 67% rename from src/main/java/emu/grasscutter/data/def/WeaponLevelData.java rename to src/main/java/emu/grasscutter/data/excels/WeaponLevelData.java index 98550b24a..3f69e2145 100644 --- a/src/main/java/emu/grasscutter/data/def/WeaponLevelData.java +++ b/src/main/java/emu/grasscutter/data/excels/WeaponLevelData.java @@ -1,23 +1,23 @@ -package emu.grasscutter.data.def; +package emu.grasscutter.data.excels; import emu.grasscutter.data.GameResource; import emu.grasscutter.data.ResourceType; @ResourceType(name = "WeaponLevelExcelConfigData.json") public class WeaponLevelData extends GameResource { - private int Level; - private int[] RequiredExps; + private int level; + private int[] requiredExps; @Override public int getId() { - return this.Level; + return this.level; } public int getLevel() { - return Level; + return level; } public int[] getRequiredExps() { - return RequiredExps; + return requiredExps; } } diff --git a/src/main/java/emu/grasscutter/data/def/WeaponPromoteData.java b/src/main/java/emu/grasscutter/data/excels/WeaponPromoteData.java similarity index 65% rename from src/main/java/emu/grasscutter/data/def/WeaponPromoteData.java rename to src/main/java/emu/grasscutter/data/excels/WeaponPromoteData.java index 8e75b2b7c..ee0306115 100644 --- a/src/main/java/emu/grasscutter/data/def/WeaponPromoteData.java +++ b/src/main/java/emu/grasscutter/data/excels/WeaponPromoteData.java @@ -1,4 +1,4 @@ -package emu.grasscutter.data.def; +package emu.grasscutter.data.excels; import java.util.ArrayList; import emu.grasscutter.data.GameResource; @@ -9,45 +9,45 @@ import emu.grasscutter.data.common.ItemParamData; @ResourceType(name = "WeaponPromoteExcelConfigData.json") public class WeaponPromoteData extends GameResource { - private int WeaponPromoteId; - private int PromoteLevel; - private ItemParamData[] CostItems; - private int CoinCost; - private FightPropData[] AddProps; - private int UnlockMaxLevel; - private int RequiredPlayerLevel; + private int weaponPromoteId; + private int promoteLevel; + private ItemParamData[] costItems; + private int coinCost; + private FightPropData[] addProps; + private int unlockMaxLevel; + private int requiredPlayerLevel; @Override public int getId() { - return (WeaponPromoteId << 8) + PromoteLevel; + return (weaponPromoteId << 8) + promoteLevel; } public int getWeaponPromoteId() { - return WeaponPromoteId; + return weaponPromoteId; } public int getPromoteLevel() { - return PromoteLevel; + return promoteLevel; } public ItemParamData[] getCostItems() { - return CostItems; + return costItems; } public int getCoinCost() { - return CoinCost; + return coinCost; } public FightPropData[] getAddProps() { - return AddProps; + return addProps; } public int getUnlockMaxLevel() { - return UnlockMaxLevel; + return unlockMaxLevel; } public int getRequiredPlayerLevel() { - return RequiredPlayerLevel; + return requiredPlayerLevel; } @Override @@ -60,7 +60,7 @@ public class WeaponPromoteData extends GameResource { } trim.add(itemParam); } - this.CostItems = trim.toArray(new ItemParamData[trim.size()]); + this.costItems = trim.toArray(new ItemParamData[trim.size()]); // Trim fight prop data ArrayList parsed = new ArrayList<>(getAddProps().length); for (FightPropData prop : getAddProps()) { @@ -69,6 +69,6 @@ public class WeaponPromoteData extends GameResource { parsed.add(prop); } } - this.AddProps = parsed.toArray(new FightPropData[parsed.size()]); + this.addProps = parsed.toArray(new FightPropData[parsed.size()]); } } diff --git a/src/main/java/emu/grasscutter/data/def/WorldLevelData.java b/src/main/java/emu/grasscutter/data/excels/WorldLevelData.java similarity index 71% rename from src/main/java/emu/grasscutter/data/def/WorldLevelData.java rename to src/main/java/emu/grasscutter/data/excels/WorldLevelData.java index 3b101f9b8..e31713b67 100644 --- a/src/main/java/emu/grasscutter/data/def/WorldLevelData.java +++ b/src/main/java/emu/grasscutter/data/excels/WorldLevelData.java @@ -1,20 +1,20 @@ -package emu.grasscutter.data.def; +package emu.grasscutter.data.excels; import emu.grasscutter.data.GameResource; import emu.grasscutter.data.ResourceType; @ResourceType(name = "WorldLevelExcelConfigData.json") public class WorldLevelData extends GameResource { - private int Level; - private int MonsterLevel; + private int level; + private int monsterLevel; @Override public int getId() { - return this.Level; + return this.level; } public int getMonsterLevel() { - return MonsterLevel; + return monsterLevel; } @Override diff --git a/src/main/java/emu/grasscutter/database/DatabaseHelper.java b/src/main/java/emu/grasscutter/database/DatabaseHelper.java index f63798988..5defbca6b 100644 --- a/src/main/java/emu/grasscutter/database/DatabaseHelper.java +++ b/src/main/java/emu/grasscutter/database/DatabaseHelper.java @@ -3,10 +3,12 @@ package emu.grasscutter.database; import java.util.List; import com.mongodb.client.result.DeleteResult; + import dev.morphia.query.FindOptions; import dev.morphia.query.Sort; import dev.morphia.query.experimental.filters.Filters; import emu.grasscutter.GameConstants; +import emu.grasscutter.Grasscutter; import emu.grasscutter.game.Account; import emu.grasscutter.game.avatar.Avatar; import emu.grasscutter.game.friends.Friendship; @@ -14,27 +16,34 @@ import emu.grasscutter.game.gacha.GachaRecord; import emu.grasscutter.game.inventory.GameItem; import emu.grasscutter.game.mail.Mail; import emu.grasscutter.game.player.Player; +import emu.grasscutter.game.quest.GameMainQuest; + +import static com.mongodb.client.model.Filters.eq; public final class DatabaseHelper { public static Account createAccount(String username) { - return createAccountWithId(username, 0); + return createAccountWithUid(username, 0); } - public static Account createAccountWithId(String username, int reservedId) { + public static Account createAccountWithUid(String username, int reservedUid) { // Unique names only - Account exists = DatabaseHelper.getAccountByName(username); - if (exists != null) { + if (DatabaseHelper.checkIfAccountExists(username)) { return null; } // Make sure there are no id collisions - if (reservedId > 0) { + if (reservedUid > 0) { // Cannot make account with the same uid as the server console - if (reservedId == GameConstants.SERVER_CONSOLE_UID) { + if (reservedUid == GameConstants.SERVER_CONSOLE_UID) { return null; } - exists = DatabaseHelper.getAccountByPlayerId(reservedId); - if (exists != null) { + + if (DatabaseHelper.checkIfAccountExists(reservedUid)) { + return null; + } + + // Make sure no existing player already has this id. + if (DatabaseHelper.checkIfPlayerExists(reservedUid)) { return null; } } @@ -44,8 +53,8 @@ public final class DatabaseHelper { account.setUsername(username); account.setId(Integer.toString(DatabaseManager.getNextId(account))); - if (reservedId > 0) { - account.setPlayerId(reservedId); + if (reservedUid > 0) { + account.setReservedPlayerUid(reservedUid); } DatabaseHelper.saveAccount(account); @@ -74,118 +83,158 @@ public final class DatabaseHelper { } public static Account getAccountByName(String username) { - return DatabaseManager.getDatastore().find(Account.class).filter(Filters.eq("username", username)).first(); + return DatabaseManager.getGameDatastore().find(Account.class).filter(Filters.eq("username", username)).first(); } public static Account getAccountByToken(String token) { if(token == null) return null; - return DatabaseManager.getDatastore().find(Account.class).filter(Filters.eq("token", token)).first(); + return DatabaseManager.getGameDatastore().find(Account.class).filter(Filters.eq("token", token)).first(); } public static Account getAccountBySessionKey(String sessionKey) { if(sessionKey == null) return null; - return DatabaseManager.getDatastore().find(Account.class).filter(Filters.eq("sessionKey", sessionKey)).first(); + return DatabaseManager.getGameDatastore().find(Account.class).filter(Filters.eq("sessionKey", sessionKey)).first(); } public static Account getAccountById(String uid) { - return DatabaseManager.getDatastore().find(Account.class).filter(Filters.eq("_id", uid)).first(); + return DatabaseManager.getGameDatastore().find(Account.class).filter(Filters.eq("_id", uid)).first(); } public static Account getAccountByPlayerId(int playerId) { - return DatabaseManager.getDatastore().find(Account.class).filter(Filters.eq("playerId", playerId)).first(); + return DatabaseManager.getGameDatastore().find(Account.class).filter(Filters.eq("reservedPlayerId", playerId)).first(); + } + + public static boolean checkIfAccountExists(String name) { + return DatabaseManager.getGameDatastore().find(Account.class).filter(Filters.eq("username", name)).count() > 0; + } + + public static boolean checkIfAccountExists(int reservedUid) { + return DatabaseManager.getGameDatastore().find(Account.class).filter(Filters.eq("reservedPlayerId", reservedUid)).count() > 0; } - public static boolean deleteAccount(String username) { - return DatabaseManager.getDatastore().find(Account.class).filter(Filters.eq("username", username)).delete().getDeletedCount() > 0; + public static void deleteAccount(Account target) { + // To delete an account, we need to also delete all the other documents in the database that reference the account. + // This should optimally be wrapped inside a transaction, to make sure an error thrown mid-way does not leave the + // database in an inconsistent state, but unfortunately Mongo only supports that when we have a replica set ... + + Player player = Grasscutter.getGameServer().getPlayerByAccountId(target.getId()); + + if (player != null) { + // Close session first + player.getSession().close(); + + // Delete data from collections + DatabaseManager.getGameDatabase().getCollection("mail").deleteMany(eq("ownerUid", player.getUid())); + DatabaseManager.getGameDatabase().getCollection("avatars").deleteMany(eq("ownerId", player.getUid())); + DatabaseManager.getGameDatabase().getCollection("gachas").deleteMany(eq("ownerId", player.getUid())); + DatabaseManager.getGameDatabase().getCollection("items").deleteMany(eq("ownerId", player.getUid())); + DatabaseManager.getGameDatabase().getCollection("quests").deleteMany(eq("ownerUid", player.getUid())); + + // Delete friendships. + // Here, we need to make sure to not only delete the deleted account's friendships, + // but also all friendship entries for that account's friends. + DatabaseManager.getGameDatabase().getCollection("friendships").deleteMany(eq("ownerId", player.getUid())); + DatabaseManager.getGameDatabase().getCollection("friendships").deleteMany(eq("friendId", player.getUid())); + + // Delete the player last. + DatabaseManager.getGameDatastore().find(Player.class).filter(Filters.eq("id", player.getUid())).delete(); + } + + // Finally, delete the account itself. + DatabaseManager.getGameDatastore().find(Account.class).filter(Filters.eq("id", target.getId())).delete(); } public static List getAllPlayers() { - return DatabaseManager.getDatastore().find(Player.class).stream().toList(); + return DatabaseManager.getGameDatastore().find(Player.class).stream().toList(); } - public static Player getPlayerById(int id) { - return DatabaseManager.getDatastore().find(Player.class).filter(Filters.eq("_id", id)).first(); + public static Player getPlayerByUid(int id) { + return DatabaseManager.getGameDatastore().find(Player.class).filter(Filters.eq("_id", id)).first(); + } + + public static Player getPlayerByAccount(Account account) { + return DatabaseManager.getGameDatastore().find(Player.class).filter(Filters.eq("accountId", account.getId())).first(); + } + + public static boolean checkIfPlayerExists(int uid) { + return DatabaseManager.getGameDatastore().find(Player.class).filter(Filters.eq("_id", uid)).count() > 0; } - public static boolean checkPlayerExists(int id) { - return DatabaseManager.getDatastore().find(Player.class).filter(Filters.eq("_id", id)).first() != null; - } - - public static synchronized Player createPlayer(Player character, int reservedId) { + public static synchronized Player generatePlayerUid(Player character, int reservedId) { // Check if reserved id int id; - if (reservedId > 0 && !checkPlayerExists(reservedId)) { + if (reservedId > 0 && !checkIfPlayerExists(reservedId)) { id = reservedId; character.setUid(id); } else { do { id = DatabaseManager.getNextId(character); } - while (checkPlayerExists(id)); + while (checkIfPlayerExists(id)); character.setUid(id); } // Save to database - DatabaseManager.getDatastore().save(character); + DatabaseManager.getGameDatastore().save(character); return character; } public static synchronized int getNextPlayerId(int reservedId) { // Check if reserved id int id; - if (reservedId > 0 && !checkPlayerExists(reservedId)) { + if (reservedId > 0 && !checkIfPlayerExists(reservedId)) { id = reservedId; } else { do { id = DatabaseManager.getNextId(Player.class); } - while (checkPlayerExists(id)); + while (checkIfPlayerExists(id)); } return id; } public static void savePlayer(Player character) { - DatabaseManager.getDatastore().save(character); + DatabaseManager.getGameDatastore().save(character); } public static void saveAvatar(Avatar avatar) { - DatabaseManager.getDatastore().save(avatar); + DatabaseManager.getGameDatastore().save(avatar); } public static List getAvatars(Player player) { - return DatabaseManager.getDatastore().find(Avatar.class).filter(Filters.eq("ownerId", player.getUid())).stream().toList(); + return DatabaseManager.getGameDatastore().find(Avatar.class).filter(Filters.eq("ownerId", player.getUid())).stream().toList(); } public static void saveItem(GameItem item) { - DatabaseManager.getDatastore().save(item); + DatabaseManager.getGameDatastore().save(item); } public static boolean deleteItem(GameItem item) { - DeleteResult result = DatabaseManager.getDatastore().delete(item); + DeleteResult result = DatabaseManager.getGameDatastore().delete(item); return result.wasAcknowledged(); } public static List getInventoryItems(Player player) { - return DatabaseManager.getDatastore().find(GameItem.class).filter(Filters.eq("ownerId", player.getUid())).stream().toList(); + return DatabaseManager.getGameDatastore().find(GameItem.class).filter(Filters.eq("ownerId", player.getUid())).stream().toList(); } public static List getFriends(Player player) { - return DatabaseManager.getDatastore().find(Friendship.class).filter(Filters.eq("ownerId", player.getUid())).stream().toList(); + return DatabaseManager.getGameDatastore().find(Friendship.class).filter(Filters.eq("ownerId", player.getUid())).stream().toList(); } public static List getReverseFriends(Player player) { - return DatabaseManager.getDatastore().find(Friendship.class).filter(Filters.eq("friendId", player.getUid())).stream().toList(); + return DatabaseManager.getGameDatastore().find(Friendship.class).filter(Filters.eq("friendId", player.getUid())).stream().toList(); } public static void saveFriendship(Friendship friendship) { - DatabaseManager.getDatastore().save(friendship); + DatabaseManager.getGameDatastore().save(friendship); } public static void deleteFriendship(Friendship friendship) { - DatabaseManager.getDatastore().delete(friendship); + DatabaseManager.getGameDatastore().delete(friendship); } public static Friendship getReverseFriendship(Friendship friendship) { - return DatabaseManager.getDatastore().find(Friendship.class).filter(Filters.and( + return DatabaseManager.getGameDatastore().find(Friendship.class).filter(Filters.and( Filters.eq("ownerId", friendship.getFriendId()), Filters.eq("friendId", friendship.getOwnerId()) )).first(); @@ -196,7 +245,7 @@ public final class DatabaseHelper { } public static List getGachaRecords(int ownerId, int page, int gachaType, int pageSize){ - return DatabaseManager.getDatastore().find(GachaRecord.class).filter( + return DatabaseManager.getGameDatastore().find(GachaRecord.class).filter( Filters.eq("ownerId", ownerId), Filters.eq("gachaType", gachaType) ).iterator(new FindOptions() @@ -211,7 +260,7 @@ public final class DatabaseHelper { } public static long getGachaRecordsMaxPage(int ownerId, int page, int gachaType, int pageSize){ - long count = DatabaseManager.getDatastore().find(GachaRecord.class).filter( + long count = DatabaseManager.getGameDatastore().find(GachaRecord.class).filter( Filters.eq("ownerId", ownerId), Filters.eq("gachaType", gachaType) ).count(); @@ -219,19 +268,31 @@ public final class DatabaseHelper { } public static void saveGachaRecord(GachaRecord gachaRecord){ - DatabaseManager.getDatastore().save(gachaRecord); + DatabaseManager.getGameDatastore().save(gachaRecord); } public static List getAllMail(Player player) { - return DatabaseManager.getDatastore().find(Mail.class).filter(Filters.eq("ownerUid", player.getUid())).stream().toList(); + return DatabaseManager.getGameDatastore().find(Mail.class).filter(Filters.eq("ownerUid", player.getUid())).stream().toList(); } public static void saveMail(Mail mail) { - DatabaseManager.getDatastore().save(mail); + DatabaseManager.getGameDatastore().save(mail); } public static boolean deleteMail(Mail mail) { - DeleteResult result = DatabaseManager.getDatastore().delete(mail); + DeleteResult result = DatabaseManager.getGameDatastore().delete(mail); return result.wasAcknowledged(); } + + public static List getAllQuests(Player player) { + return DatabaseManager.getGameDatastore().find(GameMainQuest.class).filter(Filters.eq("ownerUid", player.getUid())).stream().toList(); + } + + public static void saveQuest(GameMainQuest quest) { + DatabaseManager.getGameDatastore().save(quest); + } + + public static boolean deleteQuest(GameMainQuest quest) { + return DatabaseManager.getGameDatastore().delete(quest).wasAcknowledged(); + } } diff --git a/src/main/java/emu/grasscutter/database/DatabaseManager.java b/src/main/java/emu/grasscutter/database/DatabaseManager.java index 90ff17238..7c1360f7c 100644 --- a/src/main/java/emu/grasscutter/database/DatabaseManager.java +++ b/src/main/java/emu/grasscutter/database/DatabaseManager.java @@ -1,6 +1,5 @@ package emu.grasscutter.database; -import com.mongodb.MongoClientURI; import com.mongodb.MongoCommandException; import com.mongodb.client.MongoClient; import com.mongodb.client.MongoClients; @@ -20,69 +19,70 @@ import emu.grasscutter.game.gacha.GachaRecord; import emu.grasscutter.game.inventory.GameItem; import emu.grasscutter.game.mail.Mail; import emu.grasscutter.game.player.Player; +import emu.grasscutter.game.quest.GameMainQuest; +import emu.grasscutter.game.quest.GameQuest; + +import static emu.grasscutter.Configuration.*; public final class DatabaseManager { - - private static MongoClient mongoClient; - private static MongoClient dispatchMongoClient; - - private static Datastore datastore; + private static Datastore gameDatastore; private static Datastore dispatchDatastore; private static final Class[] mappedClasses = new Class[] { - DatabaseCounter.class, Account.class, Player.class, Avatar.class, GameItem.class, Friendship.class, GachaRecord.class, Mail.class + DatabaseCounter.class, Account.class, Player.class, Avatar.class, GameItem.class, Friendship.class, + GachaRecord.class, Mail.class, GameMainQuest.class }; - public static Datastore getDatastore() { - return datastore; + public static Datastore getGameDatastore() { + return gameDatastore; } - public static MongoDatabase getDatabase() { - return getDatastore().getDatabase(); + public static MongoDatabase getGameDatabase() { + return getGameDatastore().getDatabase(); } // Yes. I very dislike this method. However, this will be good for now. // TODO: Add dispatch routes for player account management public static Datastore getAccountDatastore() { - if(Grasscutter.getConfig().RunMode == ServerRunMode.GAME_ONLY) { + if(SERVER.runMode == ServerRunMode.GAME_ONLY) { return dispatchDatastore; } else { - return datastore; + return gameDatastore; } } public static void initialize() { // Initialize - MongoClient mongoClient = MongoClients.create(Grasscutter.getConfig().DatabaseUrl); + MongoClient gameMongoClient = MongoClients.create(DATABASE.game.connectionUri); // Set mapper options. MapperOptions mapperOptions = MapperOptions.builder() .storeEmpties(true).storeNulls(false).build(); // Create data store. - datastore = Morphia.createDatastore(mongoClient, Grasscutter.getConfig().DatabaseCollection, mapperOptions); + gameDatastore = Morphia.createDatastore(gameMongoClient, DATABASE.game.collection, mapperOptions); // Map classes. - datastore.getMapper().map(mappedClasses); + gameDatastore.getMapper().map(mappedClasses); // Ensure indexes try { - datastore.ensureIndexes(); + gameDatastore.ensureIndexes(); } catch (MongoCommandException exception) { Grasscutter.getLogger().info("Mongo index error: ", exception); // Duplicate index error if (exception.getCode() == 85) { // Drop all indexes and re add them - MongoIterable collections = datastore.getDatabase().listCollectionNames(); + MongoIterable collections = gameDatastore.getDatabase().listCollectionNames(); for (String name : collections) { - datastore.getDatabase().getCollection(name).dropIndexes(); + gameDatastore.getDatabase().getCollection(name).dropIndexes(); } // Add back indexes - datastore.ensureIndexes(); + gameDatastore.ensureIndexes(); } } - if(Grasscutter.getConfig().RunMode == ServerRunMode.GAME_ONLY) { - dispatchMongoClient = MongoClients.create(Grasscutter.getConfig().getGameServerOptions().DispatchServerDatabaseUrl); - dispatchDatastore = Morphia.createDatastore(dispatchMongoClient, Grasscutter.getConfig().getGameServerOptions().DispatchServerDatabaseCollection); + if(SERVER.runMode == ServerRunMode.GAME_ONLY) { + MongoClient dispatchMongoClient = MongoClients.create(DATABASE.server.connectionUri); + dispatchDatastore = Morphia.createDatastore(dispatchMongoClient, DATABASE.server.collection); // Ensure indexes for dispatch server try { @@ -104,14 +104,14 @@ public final class DatabaseManager { } public static synchronized int getNextId(Class c) { - DatabaseCounter counter = getDatastore().find(DatabaseCounter.class).filter(Filters.eq("_id", c.getSimpleName())).first(); + DatabaseCounter counter = getGameDatastore().find(DatabaseCounter.class).filter(Filters.eq("_id", c.getSimpleName())).first(); if (counter == null) { counter = new DatabaseCounter(c.getSimpleName()); } try { return counter.getNextId(); } finally { - getDatastore().save(counter); + getGameDatastore().save(counter); } } diff --git a/src/main/java/emu/grasscutter/game/Account.java b/src/main/java/emu/grasscutter/game/Account.java index 5b8523ec3..3697dade5 100644 --- a/src/main/java/emu/grasscutter/game/Account.java +++ b/src/main/java/emu/grasscutter/game/Account.java @@ -5,12 +5,12 @@ import emu.grasscutter.database.DatabaseHelper; import emu.grasscutter.utils.Crypto; import emu.grasscutter.utils.Utils; -import java.util.ArrayList; -import java.util.List; +import java.util.*; +import java.util.stream.Stream; import org.bson.Document; -import com.mongodb.DBObject; +import static emu.grasscutter.Configuration.*; @Entity(value = "accounts", useDiscriminator = false) public class Account { @@ -21,16 +21,18 @@ public class Account { private String username; private String password; // Unused for now - @AlsoLoad("playerUid") private int playerId; + private int reservedPlayerId; private String email; private String token; private String sessionKey; // Session token for dispatch server private List permissions; + private Locale locale; @Deprecated public Account() { this.permissions = new ArrayList<>(); + this.locale = LANGUAGE; } public String getId() { @@ -65,12 +67,12 @@ public class Account { this.token = token; } - public int getPlayerUid() { - return this.playerId; + public int getReservedPlayerUid() { + return this.reservedPlayerId; } - public void setPlayerId(int playerId) { - this.playerId = playerId; + public void setReservedPlayerUid(int playerId) { + this.reservedPlayerId = playerId; } public String getEmail() { @@ -95,6 +97,14 @@ public class Account { return this.sessionKey; } + public Locale getLocale() { + return locale; + } + + public void setLocale(Locale locale) { + this.locale = locale; + } + /** * The collection of a player's permissions. */ @@ -107,13 +117,49 @@ public class Account { this.permissions.add(permission); return true; } - public boolean hasPermission(String permission) { - return this.permissions.contains(permission) || - this.permissions.contains("*") || - (this.permissions.contains("player") || this.permissions.contains("player.*")) && permission.startsWith("player.") || - (this.permissions.contains("server") || this.permissions.contains("server.*")) && permission.startsWith("server."); + public static boolean permissionMatchesWildcard(String wildcard, String[] permissionParts) { + String[] wildcardParts = wildcard.split("\\."); + if (permissionParts.length < wildcardParts.length) { // A longer wildcard can never match a shorter permission + return false; + } + for (int i=0; i= (permissionParts.length-1)) { + return true; + } + break; + default: // This layer isn't a wildcard, it needs to match exactly + if (!wildcardParts[i].equals(permissionParts[i])) { + return false; + } + } + } + // At this point the wildcard will have matched every layer, but if it is shorter then the permission then this is not a match at this point (no **). + return (wildcardParts.length == permissionParts.length); } - + + public boolean hasPermission(String permission) { + if(this.permissions.contains("*") && this.permissions.size() == 1) return true; + + // Add default permissions if it doesn't exist + List permissions = Stream.of(this.permissions, Arrays.asList(ACCOUNT.defaultPermissions)) + .flatMap(Collection::stream) + .distinct().toList(); + + if (permissions.contains(permission)) return true; + + String[] permissionParts = permission.split("\\."); + for (String p : permissions) { + if (p.startsWith("-") && permissionMatchesWildcard(p.substring(1), permissionParts)) return false; + if (permissionMatchesWildcard(p, permissionParts)) return true; + } + + return permissions.contains("*"); + } + public boolean removePermission(String permission) { return this.permissions.remove(permission); } @@ -135,5 +181,10 @@ public class Account { if (!document.containsKey("permissions")) { this.addPermission("*"); } + + // Set account default language as server default language + if (!document.containsKey("locale")) { + this.locale = LANGUAGE; + } } } diff --git a/src/main/java/emu/grasscutter/game/ability/AbilityManager.java b/src/main/java/emu/grasscutter/game/ability/AbilityManager.java new file mode 100644 index 000000000..078ef1b71 --- /dev/null +++ b/src/main/java/emu/grasscutter/game/ability/AbilityManager.java @@ -0,0 +1,190 @@ +package emu.grasscutter.game.ability; + +import java.util.Optional; + +import com.google.protobuf.InvalidProtocolBufferException; + +import emu.grasscutter.Grasscutter; + +import emu.grasscutter.data.GameData; +import emu.grasscutter.data.binout.AbilityModifierEntry; +import emu.grasscutter.data.binout.AbilityModifier.AbilityModifierAction; +import emu.grasscutter.data.excels.AvatarSkillDepotData; +import emu.grasscutter.data.excels.ItemData; +import emu.grasscutter.game.avatar.Avatar; +import emu.grasscutter.game.entity.EntityAvatar; +import emu.grasscutter.game.entity.EntityClientGadget; +import emu.grasscutter.game.entity.EntityItem; +import emu.grasscutter.game.entity.GameEntity; +import emu.grasscutter.game.player.Player; +import emu.grasscutter.game.props.ElementType; +import emu.grasscutter.net.proto.AbilityActionGenerateElemBallOuterClass.AbilityActionGenerateElemBall; +import emu.grasscutter.net.proto.AbilityInvokeEntryHeadOuterClass.AbilityInvokeEntryHead; +import emu.grasscutter.net.proto.AbilityInvokeEntryOuterClass.AbilityInvokeEntry; +import emu.grasscutter.net.proto.AbilityMetaModifierChangeOuterClass.AbilityMetaModifierChange; +import emu.grasscutter.net.proto.AbilityMetaReInitOverrideMapOuterClass.AbilityMetaReInitOverrideMap; +import emu.grasscutter.net.proto.AbilityMixinCostStaminaOuterClass.AbilityMixinCostStamina; +import emu.grasscutter.net.proto.AbilityScalarValueEntryOuterClass.AbilityScalarValueEntry; +import emu.grasscutter.net.proto.ModifierActionOuterClass.ModifierAction; +import emu.grasscutter.utils.Position; +import emu.grasscutter.utils.Utils; + +public class AbilityManager { + private Player player; + + public AbilityManager(Player player) { + this.player = player; + } + + public Player getPlayer() { + return this.player; + } + + public void onAbilityInvoke(AbilityInvokeEntry invoke) throws Exception { + // Grasscutter.getLogger().info(invoke.getArgumentType() + " (" + invoke.getArgumentTypeValue() + "): " + Utils.bytesToHex(invoke.toByteArray())); + switch (invoke.getArgumentType()) { + case ABILITY_INVOKE_ARGUMENT_META_OVERRIDE_PARAM: + handleOverrideParam(invoke); + break; + case ABILITY_INVOKE_ARGUMENT_META_REINIT_OVERRIDEMAP: + handleReinitOverrideMap(invoke); + break; + case ABILITY_INVOKE_ARGUMENT_META_MODIFIER_CHANGE: + handleModifierChange(invoke); + break; + case ABILITY_INVOKE_ARGUMENT_MIXIN_COST_STAMINA: + handleMixinCostStamina(invoke); + break; + case ABILITY_INVOKE_ARGUMENT_ACTION_GENERATE_ELEM_BALL: + handleGenerateElemBall(invoke); + break; + default: + break; + } + } + + private void handleOverrideParam(AbilityInvokeEntry invoke) throws Exception { + GameEntity entity = player.getScene().getEntityById(invoke.getEntityId()); + + if (entity == null) { + return; + } + + AbilityScalarValueEntry entry = AbilityScalarValueEntry.parseFrom(invoke.getAbilityData()); + + entity.getMetaOverrideMap().put(entry.getKey().getStr(), entry.getFloatValue()); + } + + private void handleReinitOverrideMap(AbilityInvokeEntry invoke) throws Exception { + GameEntity entity = player.getScene().getEntityById(invoke.getEntityId()); + + if (entity == null) { + return; + } + + AbilityMetaReInitOverrideMap map = AbilityMetaReInitOverrideMap.parseFrom(invoke.getAbilityData()); + + for (AbilityScalarValueEntry entry : map.getOverrideMapList()) { + entity.getMetaOverrideMap().put(entry.getKey().getStr(), entry.getFloatValue()); + } + } + + private void handleModifierChange(AbilityInvokeEntry invoke) throws Exception { + GameEntity target = player.getScene().getEntityById(invoke.getEntityId()); + if (target == null) { + return; + } + + AbilityInvokeEntryHead head = invoke.getHead(); + if (head == null) { + return; + } + + AbilityMetaModifierChange data = AbilityMetaModifierChange.parseFrom(invoke.getAbilityData()); + if (data == null) { + return; + } + + GameEntity sourceEntity = player.getScene().getEntityById(data.getApplyEntityId()); + if (sourceEntity == null) { + return; + } + + // This is not how it works but we will keep it for now since healing abilities dont work properly anyways + if (data.getAction() == ModifierAction.ADDED && data.getParentAbilityName() != null) { + // Handle add modifier here + String modifierString = data.getParentAbilityName().getStr(); + AbilityModifierEntry modifier = GameData.getAbilityModifiers().get(modifierString); + + if (modifier != null && modifier.getOnAdded().size() > 0) { + for (AbilityModifierAction action : modifier.getOnAdded()) { + invokeAction(action, target, sourceEntity); + } + } + + // Add to meta modifier list + target.getMetaModifiers().put(head.getInstancedModifierId(), modifierString); + } else if (data.getAction() == ModifierAction.REMOVED) { + String modifierString = target.getMetaModifiers().get(head.getInstancedModifierId()); + + if (modifierString != null) { + // Get modifier and call on remove event + AbilityModifierEntry modifier = GameData.getAbilityModifiers().get(modifierString); + + if (modifier != null && modifier.getOnRemoved().size() > 0) { + for (AbilityModifierAction action : modifier.getOnRemoved()) { + invokeAction(action, target, sourceEntity); + } + } + + // Remove from meta modifiers + target.getMetaModifiers().remove(head.getInstancedModifierId()); + } + } + } + + private void handleMixinCostStamina(AbilityInvokeEntry invoke) throws InvalidProtocolBufferException { + AbilityMixinCostStamina costStamina = AbilityMixinCostStamina.parseFrom((invoke.getAbilityData())); + getPlayer().getStaminaManager().handleMixinCostStamina(costStamina.getIsSwim()); + } + + private void handleGenerateElemBall(AbilityInvokeEntry invoke) throws InvalidProtocolBufferException { + this.player.getEnergyManager().handleGenerateElemBall(invoke); + } + + private void invokeAction(AbilityModifierAction action, GameEntity target, GameEntity sourceEntity) { + switch (action.type) { + case HealHP -> { + if (action.amount == null) { + return; + } + + float healAmount = 0; + + if (action.amount.isDynamic && action.amount.dynamicKey != null) { + healAmount = sourceEntity.getMetaOverrideMap().getOrDefault(action.amount.dynamicKey, 0f); + } + + if (healAmount > 0) { + target.heal(healAmount); + } + } + case LoseHP -> { + if (action.amountByTargetCurrentHPRatio == null) { + return; + } + + float damageAmount = 0; + + if (action.amount.isDynamic && action.amount.dynamicKey != null) { + damageAmount = sourceEntity.getMetaOverrideMap().getOrDefault(action.amount.dynamicKey, 0f); + } + + if (damageAmount > 0) { + target.damage(damageAmount); + } + } + } + } +} + diff --git a/src/main/java/emu/grasscutter/game/avatar/Avatar.java b/src/main/java/emu/grasscutter/game/avatar/Avatar.java index b0bfb0801..588052c42 100644 --- a/src/main/java/emu/grasscutter/game/avatar/Avatar.java +++ b/src/main/java/emu/grasscutter/game/avatar/Avatar.java @@ -17,24 +17,24 @@ import dev.morphia.annotations.PostLoad; import dev.morphia.annotations.PrePersist; import dev.morphia.annotations.Transient; import emu.grasscutter.data.GameData; +import emu.grasscutter.data.binout.OpenConfigEntry; +import emu.grasscutter.data.binout.OpenConfigEntry.SkillPointModifier; import emu.grasscutter.data.common.FightPropData; -import emu.grasscutter.data.custom.OpenConfigEntry; -import emu.grasscutter.data.custom.OpenConfigEntry.SkillPointModifier; -import emu.grasscutter.data.def.AvatarData; -import emu.grasscutter.data.def.AvatarPromoteData; -import emu.grasscutter.data.def.AvatarSkillData; -import emu.grasscutter.data.def.AvatarSkillDepotData; -import emu.grasscutter.data.def.AvatarSkillDepotData.InherentProudSkillOpens; -import emu.grasscutter.data.def.AvatarTalentData; -import emu.grasscutter.data.def.EquipAffixData; -import emu.grasscutter.data.def.ItemData.WeaponProperty; -import emu.grasscutter.data.def.ProudSkillData; -import emu.grasscutter.data.def.ReliquaryAffixData; -import emu.grasscutter.data.def.ReliquaryLevelData; -import emu.grasscutter.data.def.ReliquaryMainPropData; -import emu.grasscutter.data.def.ReliquarySetData; -import emu.grasscutter.data.def.WeaponCurveData; -import emu.grasscutter.data.def.WeaponPromoteData; +import emu.grasscutter.data.excels.AvatarData; +import emu.grasscutter.data.excels.AvatarPromoteData; +import emu.grasscutter.data.excels.AvatarSkillData; +import emu.grasscutter.data.excels.AvatarSkillDepotData; +import emu.grasscutter.data.excels.AvatarTalentData; +import emu.grasscutter.data.excels.EquipAffixData; +import emu.grasscutter.data.excels.ProudSkillData; +import emu.grasscutter.data.excels.ReliquaryAffixData; +import emu.grasscutter.data.excels.ReliquaryLevelData; +import emu.grasscutter.data.excels.ReliquaryMainPropData; +import emu.grasscutter.data.excels.ReliquarySetData; +import emu.grasscutter.data.excels.WeaponCurveData; +import emu.grasscutter.data.excels.WeaponPromoteData; +import emu.grasscutter.data.excels.AvatarSkillDepotData.InherentProudSkillOpens; +import emu.grasscutter.data.excels.ItemData.WeaponProperty; import emu.grasscutter.database.DatabaseHelper; import emu.grasscutter.game.entity.EntityAvatar; import emu.grasscutter.game.inventory.EquipType; @@ -62,6 +62,8 @@ import it.unimi.dsi.fastutil.ints.Int2IntOpenHashMap; import it.unimi.dsi.fastutil.ints.Int2ObjectMap; import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap; +import static emu.grasscutter.Configuration.GAME_OPTIONS; + @Entity(value = "avatars", useDiscriminator = false) public class Avatar { @Id private ObjectId id; @@ -69,6 +71,7 @@ public class Avatar { @Transient private Player owner; @Transient private AvatarData data; + @Transient private AvatarSkillDepotData skillDepot; @Transient private long guid; // Player unique id private int avatarId; // Id of avatar @@ -78,6 +81,7 @@ public class Avatar { private int satiation; // ? private int satiationPenalty; // ? private float currentHp; + private float currentEnergy; @Transient private final Int2ObjectMap equips; @Transient private final Int2FloatOpenHashMap fightProp; @@ -103,8 +107,8 @@ public class Avatar { private int nameCardRewardId; private int nameCardId; + @Deprecated // Do not use. Morhpia only! public Avatar() { - // Morhpia only! this.equips = new Int2ObjectOpenHashMap<>(); this.fightProp = new Int2FloatOpenHashMap(); this.extraAbilityEmbryos = new HashSet<>(); @@ -140,13 +144,13 @@ public class Avatar { } // Skill depot - this.setSkillDepot(getAvatarData().getSkillDepot()); + this.setSkillDepotData(getAvatarData().getSkillDepot()); // Set stats this.recalcStats(); this.currentHp = getFightProperty(FightProperty.FIGHT_PROP_MAX_HP); setFightProperty(FightProperty.FIGHT_PROP_CUR_HP, this.currentHp); - + this.currentEnergy = 0f; // Load handler this.onLoad(); } @@ -164,7 +168,8 @@ public class Avatar { } protected void setAvatarData(AvatarData data) { - this.data = data; + if (this.data != null) return; + this.data = data; // Used while loading this from the database } public int getOwnerId() { @@ -257,9 +262,19 @@ public class Avatar { return skillDepotId; } - public void setSkillDepot(AvatarSkillDepotData skillDepot) { - // Set id + public AvatarSkillDepotData getSkillDepot() { + return skillDepot; + } + + protected void setSkillDepot(AvatarSkillDepotData skillDepot) { + if (this.skillDepot != null) return; + this.skillDepot = skillDepot; // Used while loading this from the database + } + + public void setSkillDepotData(AvatarSkillDepotData skillDepot) { + // Set id and depot this.skillDepotId = skillDepot.getId(); + this.skillDepot = skillDepot; // Clear, then add skills getSkillLevelMap().clear(); if (skillDepot.getEnergySkill() > 0) { @@ -344,6 +359,34 @@ public class Avatar { this.currentHp = currentHp; } + public void setCurrentEnergy() { + if (GAME_OPTIONS.energyUsage) { + this.setCurrentEnergy(this.currentEnergy); + } + } + + public void setCurrentEnergy(float currentEnergy) { + if (this.getSkillDepot() != null && this.getSkillDepot().getEnergySkillData() != null) { + ElementType element = this.getSkillDepot().getElementType(); + this.setFightProperty(element.getMaxEnergyProp(), this.getSkillDepot().getEnergySkillData().getCostElemVal()); + + if (GAME_OPTIONS.energyUsage) { + this.setFightProperty(element.getCurEnergyProp(), currentEnergy); + } + else { + this.setFightProperty(element.getCurEnergyProp(), this.getSkillDepot().getEnergySkillData().getCostElemVal()); + } + } + } + + public void setCurrentEnergy(FightProperty curEnergyProp, float currentEnergy) { + if (GAME_OPTIONS.energyUsage) { + this.setFightProperty(curEnergyProp, currentEnergy); + this.currentEnergy = currentEnergy; + this.save(); + } + } + public Int2FloatOpenHashMap getFightProperties() { return fightProp; } @@ -481,6 +524,9 @@ public class Avatar { // Get hp percent, set to 100% if none float hpPercent = this.getFightProperty(FightProperty.FIGHT_PROP_MAX_HP) <= 0 ? 1f : this.getFightProperty(FightProperty.FIGHT_PROP_CUR_HP) / this.getFightProperty(FightProperty.FIGHT_PROP_MAX_HP); + // Store current energy value for later + float currentEnergy = (this.getSkillDepot() != null) ? this.getFightProperty(this.getSkillDepot().getElementType().getCurEnergyProp()) : 0f; + // Clear properties this.getFightProperties().clear(); @@ -499,11 +545,7 @@ public class Avatar { } // Set energy usage - if (data.getSkillDepot() != null && data.getSkillDepot().getEnergySkillData() != null) { - ElementType element = data.getSkillDepot().getElementType(); - this.setFightProperty(element.getEnergyProperty(), data.getSkillDepot().getEnergySkillData().getCostElemVal()); - this.setFightProperty((element.getEnergyProperty().getId() % 70) + 1000, data.getSkillDepot().getEnergySkillData().getCostElemVal()); - } + setCurrentEnergy(currentEnergy); // Artifacts for (int slotId = 1; slotId <= 5; slotId++) { diff --git a/src/main/java/emu/grasscutter/game/avatar/AvatarStorage.java b/src/main/java/emu/grasscutter/game/avatar/AvatarStorage.java index 2486e36ab..8feae29c6 100644 --- a/src/main/java/emu/grasscutter/game/avatar/AvatarStorage.java +++ b/src/main/java/emu/grasscutter/game/avatar/AvatarStorage.java @@ -4,7 +4,8 @@ import java.util.Iterator; import java.util.List; import emu.grasscutter.data.GameData; -import emu.grasscutter.data.def.AvatarData; +import emu.grasscutter.data.excels.AvatarData; +import emu.grasscutter.data.excels.AvatarSkillDepotData; import emu.grasscutter.database.DatabaseHelper; import emu.grasscutter.game.entity.EntityAvatar; import emu.grasscutter.game.inventory.GameItem; @@ -139,12 +140,14 @@ public class AvatarStorage implements Iterable { } AvatarData avatarData = GameData.getAvatarDataMap().get(avatar.getAvatarId()); - if (avatarData == null) { + AvatarSkillDepotData skillDepot = GameData.getAvatarSkillDepotDataMap().get(avatar.getSkillDepotId()); + if (avatarData == null || skillDepot == null) { continue; } // Set ownerships avatar.setAvatarData(avatarData); + avatar.setSkillDepot(skillDepot); avatar.setOwner(getPlayer()); // Force recalc of const boosted skills diff --git a/src/main/java/emu/grasscutter/game/combine/CombineManger.java b/src/main/java/emu/grasscutter/game/combine/CombineManger.java index 478edfedb..641a69a0c 100644 --- a/src/main/java/emu/grasscutter/game/combine/CombineManger.java +++ b/src/main/java/emu/grasscutter/game/combine/CombineManger.java @@ -1,7 +1,8 @@ package emu.grasscutter.game.combine; import emu.grasscutter.data.GameData; -import emu.grasscutter.data.def.CombineData; +import emu.grasscutter.data.common.ItemParamData; +import emu.grasscutter.data.excels.CombineData; import emu.grasscutter.game.inventory.ItemType; import emu.grasscutter.game.player.Player; import emu.grasscutter.net.proto.RetcodeOuterClass; @@ -71,7 +72,7 @@ public class CombineManger { CombineResult result = new CombineResult(); result.setMaterial(List.of()); - result.setResult(List.of(new CombineData.CombineItemPair(combineData.getResultItemId(), + result.setResult(List.of(new ItemParamData(combineData.getResultItemId(), combineData.getResultItemCount() * count))); // TODO lucky characters result.setExtra(List.of()); diff --git a/src/main/java/emu/grasscutter/game/combine/CombineResult.java b/src/main/java/emu/grasscutter/game/combine/CombineResult.java index f67a64737..067d55568 100644 --- a/src/main/java/emu/grasscutter/game/combine/CombineResult.java +++ b/src/main/java/emu/grasscutter/game/combine/CombineResult.java @@ -1,44 +1,45 @@ package emu.grasscutter.game.combine; -import emu.grasscutter.data.def.CombineData; - import java.util.List; -public class CombineResult { - private List material; - private List result; - private List extra; - private List back; +import emu.grasscutter.data.common.ItemParamData; +import emu.grasscutter.data.excels.CombineData; - public List getMaterial() { +public class CombineResult { + private List material; + private List result; + private List extra; + private List back; + + public List getMaterial() { return material; } - public void setMaterial(List material) { + public void setMaterial(List material) { this.material = material; } - public List getResult() { + public List getResult() { return result; } - public void setResult(List result) { + public void setResult(List result) { this.result = result; } - public List getExtra() { + public List getExtra() { return extra; } - public void setExtra(List extra) { + public void setExtra(List extra) { this.extra = extra; } - public List getBack() { + public List getBack() { return back; } - public void setBack(List back) { + public void setBack(List back) { this.back = back; } diff --git a/src/main/java/emu/grasscutter/game/drop/DropManager.java b/src/main/java/emu/grasscutter/game/drop/DropManager.java index e304d37b5..8908afbcd 100644 --- a/src/main/java/emu/grasscutter/game/drop/DropManager.java +++ b/src/main/java/emu/grasscutter/game/drop/DropManager.java @@ -2,8 +2,9 @@ package emu.grasscutter.game.drop; import com.google.gson.reflect.TypeToken; import emu.grasscutter.Grasscutter; +import emu.grasscutter.data.DataLoader; import emu.grasscutter.data.GameData; -import emu.grasscutter.data.def.ItemData; +import emu.grasscutter.data.excels.ItemData; import emu.grasscutter.game.entity.EntityItem; import emu.grasscutter.game.entity.EntityMonster; import emu.grasscutter.game.inventory.GameItem; @@ -17,7 +18,8 @@ import emu.grasscutter.utils.Utils; import it.unimi.dsi.fastutil.ints.Int2ObjectMap; import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap; -import java.io.FileReader; +import java.io.InputStreamReader; +import java.io.Reader; import java.util.Collection; import java.util.List; @@ -41,7 +43,7 @@ public class DropManager { } public synchronized void load() { - try (FileReader fileReader = new FileReader(Grasscutter.getConfig().DATA_FOLDER + "Drop.json")) { + try (Reader fileReader = new InputStreamReader(DataLoader.load("Drop.json"))) { getDropData().clear(); List banners = Grasscutter.getGsonFactory().fromJson(fileReader, TypeToken.getParameterized(Collection.class, DropInfo.class).getType()); if(banners.size() > 0) { @@ -69,9 +71,7 @@ public class DropManager { } else { // target is null if items will be added are shared. no one could pick it up because of the combination(give + shared) // so it will be sent to all players' inventories directly. - dropScene.getPlayers().forEach(x -> { - x.getInventory().addItem(new GameItem(itemData, num), ActionReason.SubfieldDrop, true); - }); + dropScene.getPlayers().forEach(x -> x.getInventory().addItem(new GameItem(itemData, num), ActionReason.SubfieldDrop, true)); } } } diff --git a/src/main/java/emu/grasscutter/game/dungeons/DungeonChallenge.java b/src/main/java/emu/grasscutter/game/dungeons/DungeonChallenge.java index 2e07f0058..fbaf26cc5 100644 --- a/src/main/java/emu/grasscutter/game/dungeons/DungeonChallenge.java +++ b/src/main/java/emu/grasscutter/game/dungeons/DungeonChallenge.java @@ -1,7 +1,7 @@ package emu.grasscutter.game.dungeons; import emu.grasscutter.data.common.ItemParamData; -import emu.grasscutter.data.def.DungeonData; +import emu.grasscutter.data.excels.DungeonData; import emu.grasscutter.game.entity.EntityMonster; import emu.grasscutter.game.inventory.GameItem; import emu.grasscutter.game.player.Player; @@ -28,14 +28,20 @@ public class DungeonChallenge { private int challengeId; private boolean success; private boolean progress; - + /** + * has more challenge + */ + private boolean stage; private int score; private int objective = 0; private IntSet rewardedPlayers; - public DungeonChallenge(Scene scene, SceneGroup group) { + public DungeonChallenge(Scene scene, SceneGroup group, int challengeId, int challengeIndex, int objective) { this.scene = scene; this.group = group; + this.challengeId = challengeId; + this.challengeIndex = challengeIndex; + this.objective = objective; this.setRewardedPlayers(new IntOpenHashSet()); } @@ -86,7 +92,15 @@ public class DungeonChallenge { public int getScore() { return score; } - + + public boolean isStage() { + return stage; + } + + public void setStage(boolean stage) { + this.stage = stage; + } + public int getTimeLimit() { return 600; } @@ -112,7 +126,7 @@ public class DungeonChallenge { if (this.isSuccess()) { // Call success script event this.getScene().getScriptManager().callEvent(EventType.EVENT_CHALLENGE_SUCCESS, null); - + // Settle settle(); } else { @@ -122,8 +136,10 @@ public class DungeonChallenge { private void settle() { getScene().getDungeonSettleObservers().forEach(o -> o.onDungeonSettle(getScene())); - - getScene().getScriptManager().callEvent(EventType.EVENT_DUNGEON_SETTLE, new ScriptArgs(this.isSuccess() ? 1 : 0)); + + if(!stage){ + getScene().getScriptManager().callEvent(EventType.EVENT_DUNGEON_SETTLE, new ScriptArgs(this.isSuccess() ? 1 : 0)); + } } public void onMonsterDie(EntityMonster entity) { diff --git a/src/main/java/emu/grasscutter/game/dungeons/DungeonManager.java b/src/main/java/emu/grasscutter/game/dungeons/DungeonManager.java index 0a68e6ab0..367120bb8 100644 --- a/src/main/java/emu/grasscutter/game/dungeons/DungeonManager.java +++ b/src/main/java/emu/grasscutter/game/dungeons/DungeonManager.java @@ -3,10 +3,12 @@ package emu.grasscutter.game.dungeons; import emu.grasscutter.GameConstants; import emu.grasscutter.Grasscutter; import emu.grasscutter.data.GameData; -import emu.grasscutter.data.custom.ScenePointEntry; -import emu.grasscutter.data.def.DungeonData; +import emu.grasscutter.data.binout.ScenePointEntry; +import emu.grasscutter.data.excels.DungeonData; import emu.grasscutter.game.player.Player; import emu.grasscutter.game.props.SceneType; +import emu.grasscutter.game.quest.enums.QuestTrigger; +import emu.grasscutter.game.world.Scene; import emu.grasscutter.net.packet.BasePacket; import emu.grasscutter.net.packet.PacketOpcodes; import emu.grasscutter.server.game.GameServer; @@ -51,8 +53,9 @@ public class DungeonManager { int sceneId = data.getSceneId(); player.getScene().setPrevScene(sceneId); - if(player.getWorld().transferPlayerToScene(player, sceneId, data)){ + if (player.getWorld().transferPlayerToScene(player, sceneId, data)) { player.getScene().addDungeonSettleObserver(basicDungeonSettleObserver); + player.getQuestManager().triggerEvent(QuestTrigger.QUEST_CONTENT_ENTER_DUNGEON, data.getId()); } player.getScene().setPrevScenePoint(pointId); @@ -78,19 +81,21 @@ public class DungeonManager { } public void exitDungeon(Player player) { - if (player.getScene().getSceneType() != SceneType.SCENE_DUNGEON) { + Scene scene = player.getScene(); + + if (scene==null || scene.getSceneType() != SceneType.SCENE_DUNGEON) { return; } // Get previous scene - int prevScene = player.getScene().getPrevScene() > 0 ? player.getScene().getPrevScene() : 3; + int prevScene = scene.getPrevScene() > 0 ? scene.getPrevScene() : 3; // Get previous position - DungeonData dungeonData = player.getScene().getDungeonData(); + DungeonData dungeonData = scene.getDungeonData(); Position prevPos = new Position(GameConstants.START_POSITION); if (dungeonData != null) { - ScenePointEntry entry = GameData.getScenePointEntryById(prevScene, player.getScene().getPrevScenePoint()); + ScenePointEntry entry = GameData.getScenePointEntryById(prevScene, scene.getPrevScenePoint()); if (entry != null) { prevPos.set(entry.getPointData().getTranPos()); diff --git a/src/main/java/emu/grasscutter/game/dungeons/TowerDungeonSettleListener.java b/src/main/java/emu/grasscutter/game/dungeons/TowerDungeonSettleListener.java index 5b1ff7a30..3f212ce4a 100644 --- a/src/main/java/emu/grasscutter/game/dungeons/TowerDungeonSettleListener.java +++ b/src/main/java/emu/grasscutter/game/dungeons/TowerDungeonSettleListener.java @@ -9,15 +9,25 @@ public class TowerDungeonSettleListener implements DungeonSettleListener { @Override public void onDungeonSettle(Scene scene) { + if(scene.getScriptManager().getVariables().containsKey("stage") + && scene.getScriptManager().getVariables().get("stage") == 1){ + return; + } scene.setAutoCloseTime(Utils.getCurrentSeconds() + 1000); var towerManager = scene.getPlayers().get(0).getTowerManager(); - towerManager.notifyCurLevelRecordChangeWhenDone(); - scene.broadcastPacket(new PacketTowerFloorRecordChangeNotify(towerManager.getCurrentFloorId())); - scene.broadcastPacket(new PacketDungeonSettleNotify(scene.getChallenge(), - true, + towerManager.notifyCurLevelRecordChangeWhenDone(3); + scene.broadcastPacket(new PacketTowerFloorRecordChangeNotify( + towerManager.getCurrentFloorId(), + 3, + towerManager.canEnterScheduleFloor() + )); + + scene.broadcastPacket(new PacketDungeonSettleNotify( + scene.getChallenge(), + towerManager.hasNextFloor(), towerManager.hasNextLevel(), - towerManager.getNextFloorId() + towerManager.hasNextLevel() ? 0 : towerManager.getNextFloorId() )); } diff --git a/src/main/java/emu/grasscutter/game/entity/EntityAvatar.java b/src/main/java/emu/grasscutter/game/entity/EntityAvatar.java index 82efb795f..478f7e268 100644 --- a/src/main/java/emu/grasscutter/game/entity/EntityAvatar.java +++ b/src/main/java/emu/grasscutter/game/entity/EntityAvatar.java @@ -2,8 +2,8 @@ package emu.grasscutter.game.entity; import emu.grasscutter.GameConstants; import emu.grasscutter.data.GameData; -import emu.grasscutter.data.def.AvatarData; -import emu.grasscutter.data.def.AvatarSkillDepotData; +import emu.grasscutter.data.excels.AvatarData; +import emu.grasscutter.data.excels.AvatarSkillDepotData; import emu.grasscutter.game.avatar.Avatar; import emu.grasscutter.game.inventory.EquipType; import emu.grasscutter.game.inventory.GameItem; @@ -12,22 +12,25 @@ import emu.grasscutter.game.props.EntityIdType; import emu.grasscutter.game.props.FightProperty; import emu.grasscutter.game.props.PlayerProperty; import emu.grasscutter.game.world.Scene; -import emu.grasscutter.game.world.World; import emu.grasscutter.net.proto.AbilityControlBlockOuterClass.AbilityControlBlock; import emu.grasscutter.net.proto.AbilityEmbryoOuterClass.AbilityEmbryo; import emu.grasscutter.net.proto.AbilitySyncStateInfoOuterClass.AbilitySyncStateInfo; import emu.grasscutter.net.proto.AnimatorParameterValueInfoPairOuterClass.AnimatorParameterValueInfoPair; +import emu.grasscutter.net.proto.ChangeHpReasonOuterClass.ChangeHpReason; import emu.grasscutter.net.proto.EntityAuthorityInfoOuterClass.EntityAuthorityInfo; import emu.grasscutter.net.proto.EntityClientDataOuterClass.EntityClientData; 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.PropChangeReasonOuterClass.PropChangeReason; import emu.grasscutter.net.proto.PropPairOuterClass.PropPair; import emu.grasscutter.net.proto.ProtEntityTypeOuterClass.ProtEntityType; import emu.grasscutter.net.proto.SceneAvatarInfoOuterClass.SceneAvatarInfo; import emu.grasscutter.net.proto.SceneEntityAiInfoOuterClass.SceneEntityAiInfo; import emu.grasscutter.net.proto.SceneEntityInfoOuterClass.SceneEntityInfo; import emu.grasscutter.net.proto.VectorOuterClass.Vector; +import emu.grasscutter.server.packet.send.PacketAvatarFightPropUpdateNotify; +import emu.grasscutter.server.packet.send.PacketEntityFightPropChangeReasonNotify; import emu.grasscutter.utils.Position; import emu.grasscutter.utils.ProtoHelper; import emu.grasscutter.utils.Utils; @@ -43,6 +46,7 @@ public class EntityAvatar extends GameEntity { public EntityAvatar(Scene scene, Avatar avatar) { super(scene); this.avatar = avatar; + this.avatar.setCurrentEnergy(); this.id = getScene().getWorld().getNextEntityId(EntityIdType.AVATAR); GameItem weapon = this.getAvatar().getWeapon(); @@ -54,6 +58,7 @@ public class EntityAvatar extends GameEntity { public EntityAvatar(Avatar avatar) { super(null); this.avatar = avatar; + this.avatar.setCurrentEnergy(); } public Player getPlayer() { @@ -101,15 +106,69 @@ public class EntityAvatar extends GameEntity { @Override public void onDeath(int killerId) { - this.killedType = PlayerDieType.PLAYER_DIE_KILL_BY_MONSTER; + this.killedType = PlayerDieType.PLAYER_DIE_TYPE_KILL_BY_MONSTER; this.killedBy = killerId; + clearEnergy(PropChangeReason.PROP_CHANGE_REASON_STATUE_RECOVER); } public void onDeath(PlayerDieType dieType, int killerId) { this.killedType = dieType; this.killedBy = killerId; + clearEnergy(PropChangeReason.PROP_CHANGE_REASON_STATUE_RECOVER); } + @Override + public float heal(float amount) { + float healed = super.heal(amount); + + if (healed > 0f) { + getScene().broadcastPacket( + new PacketEntityFightPropChangeReasonNotify(this, FightProperty.FIGHT_PROP_CUR_HP, healed, PropChangeReason.PROP_CHANGE_REASON_ABILITY, ChangeHpReason.CHANGE_HP_REASON_CHANGE_HP_ADD_ABILITY) + ); + } + + return healed; + } + + public void clearEnergy(PropChangeReason reason) { + FightProperty curEnergyProp = this.getAvatar().getSkillDepot().getElementType().getCurEnergyProp(); + this.avatar.setCurrentEnergy(curEnergyProp, 0); + + this.getScene().broadcastPacket(new PacketAvatarFightPropUpdateNotify(this.getAvatar(), curEnergyProp)); + this.getScene().broadcastPacket(new PacketEntityFightPropChangeReasonNotify(this, curEnergyProp, 0f, reason)); + } + + public void addEnergy(float amount, PropChangeReason reason) { + this.addEnergy(amount, reason, false); + } + public void addEnergy(float amount, PropChangeReason reason, boolean isFlat) { + // Get current and maximum energy for this avatar. + FightProperty curEnergyProp = this.getAvatar().getSkillDepot().getElementType().getCurEnergyProp(); + FightProperty maxEnergyProp = this.getAvatar().getSkillDepot().getElementType().getMaxEnergyProp(); + + float curEnergy = this.getFightProperty(curEnergyProp); + 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. + if (!isFlat) { + amount *= energyRecharge; + } + + // Determine the new energy value. + float newEnergy = Math.min(curEnergy + amount, maxEnergy); + + // Set energy and notify. + if (newEnergy != curEnergy) { + this.avatar.setCurrentEnergy(curEnergyProp, newEnergy); + + this.getScene().broadcastPacket(new PacketAvatarFightPropUpdateNotify(this.getAvatar(), curEnergyProp)); + this.getScene().broadcastPacket(new PacketEntityFightPropChangeReasonNotify(this, curEnergyProp, newEnergy, reason)); + } + } + public SceneAvatarInfo getSceneAvatarInfo() { SceneAvatarInfo.Builder avatarInfo = SceneAvatarInfo.newBuilder() .setUid(this.getPlayer().getUid()) @@ -150,7 +209,7 @@ public class EntityAvatar extends GameEntity { SceneEntityInfo.Builder entityInfo = SceneEntityInfo.newBuilder() .setEntityId(getId()) - .setEntityType(ProtEntityType.PROT_ENTITY_AVATAR) + .setEntityType(ProtEntityType.PROT_ENTITY_TYPE_AVATAR) .addAnimatorParaList(AnimatorParameterValueInfoPair.newBuilder()) .setEntityClientData(EntityClientData.newBuilder()) .setEntityAuthorityInfo(authority) @@ -241,5 +300,5 @@ public class EntityAvatar extends GameEntity { // return abilityControlBlock.build(); - } + } } diff --git a/src/main/java/emu/grasscutter/game/entity/EntityClientGadget.java b/src/main/java/emu/grasscutter/game/entity/EntityClientGadget.java index 77b76566a..5e0c5eb00 100644 --- a/src/main/java/emu/grasscutter/game/entity/EntityClientGadget.java +++ b/src/main/java/emu/grasscutter/game/entity/EntityClientGadget.java @@ -35,6 +35,8 @@ public class EntityClientGadget extends EntityBaseGadget { private int ownerEntityId; private int targetEntityId; private boolean asyncLoad; + + private int originalOwnerEntityId; public EntityClientGadget(Scene scene, Player player, EvtCreateGadgetNotify notify) { super(scene); @@ -48,6 +50,14 @@ public class EntityClientGadget extends EntityBaseGadget { this.ownerEntityId = notify.getPropOwnerEntityId(); this.targetEntityId = notify.getTargetEntityId(); this.asyncLoad = notify.getIsAsyncLoad(); + + GameEntity owner = scene.getEntityById(this.ownerEntityId); + if (owner instanceof EntityClientGadget ownerGadget) { + this.originalOwnerEntityId = ownerGadget.getOriginalOwnerEntityId(); + } + else { + this.originalOwnerEntityId = this.ownerEntityId; + } } @Override @@ -79,6 +89,10 @@ public class EntityClientGadget extends EntityBaseGadget { return this.asyncLoad; } + public int getOriginalOwnerEntityId() { + return this.originalOwnerEntityId; + } + @Override public void onDeath(int killerId) { @@ -113,7 +127,7 @@ public class EntityClientGadget extends EntityBaseGadget { SceneEntityInfo.Builder entityInfo = SceneEntityInfo.newBuilder() .setEntityId(getId()) - .setEntityType(ProtEntityType.PROT_ENTITY_GADGET) + .setEntityType(ProtEntityType.PROT_ENTITY_TYPE_GADGET) .setMotionInfo(MotionInfo.newBuilder().setPos(getPosition().toProto()).setRot(getRotation().toProto()).setSpeed(Vector.newBuilder())) .addAnimatorParaList(AnimatorParameterValueInfoPair.newBuilder()) .setEntityClientData(EntityClientData.newBuilder()) diff --git a/src/main/java/emu/grasscutter/game/entity/EntityGadget.java b/src/main/java/emu/grasscutter/game/entity/EntityGadget.java index 640f93f22..5eb9c8fab 100644 --- a/src/main/java/emu/grasscutter/game/entity/EntityGadget.java +++ b/src/main/java/emu/grasscutter/game/entity/EntityGadget.java @@ -4,7 +4,7 @@ import java.util.Arrays; import java.util.List; import emu.grasscutter.data.GameData; -import emu.grasscutter.data.def.GadgetData; +import emu.grasscutter.data.excels.GadgetData; import emu.grasscutter.game.props.EntityIdType; import emu.grasscutter.game.props.EntityType; import emu.grasscutter.game.props.PlayerProperty; @@ -122,7 +122,7 @@ public class EntityGadget extends EntityBaseGadget { SceneEntityInfo.Builder entityInfo = SceneEntityInfo.newBuilder() .setEntityId(getId()) - .setEntityType(ProtEntityType.PROT_ENTITY_GADGET) + .setEntityType(ProtEntityType.PROT_ENTITY_TYPE_GADGET) .setMotionInfo(MotionInfo.newBuilder().setPos(getPosition().toProto()).setRot(getRotation().toProto()).setSpeed(Vector.newBuilder())) .addAnimatorParaList(AnimatorParameterValueInfoPair.newBuilder()) .setEntityClientData(EntityClientData.newBuilder()) diff --git a/src/main/java/emu/grasscutter/game/entity/EntityItem.java b/src/main/java/emu/grasscutter/game/entity/EntityItem.java index f2b7386e1..791b230a1 100644 --- a/src/main/java/emu/grasscutter/game/entity/EntityItem.java +++ b/src/main/java/emu/grasscutter/game/entity/EntityItem.java @@ -1,6 +1,6 @@ package emu.grasscutter.game.entity; -import emu.grasscutter.data.def.ItemData; +import emu.grasscutter.data.excels.ItemData; import emu.grasscutter.game.inventory.GameItem; import emu.grasscutter.game.player.Player; import emu.grasscutter.game.props.EntityIdType; @@ -111,7 +111,7 @@ public class EntityItem extends EntityBaseGadget { SceneEntityInfo.Builder entityInfo = SceneEntityInfo.newBuilder() .setEntityId(getId()) - .setEntityType(ProtEntityType.PROT_ENTITY_GADGET) + .setEntityType(ProtEntityType.PROT_ENTITY_TYPE_GADGET) .setMotionInfo(MotionInfo.newBuilder().setPos(getPosition().toProto()).setRot(getRotation().toProto()).setSpeed(Vector.newBuilder())) .addAnimatorParaList(AnimatorParameterValueInfoPair.newBuilder()) .setEntityClientData(EntityClientData.newBuilder()) @@ -127,7 +127,7 @@ public class EntityItem extends EntityBaseGadget { SceneGadgetInfo.Builder gadgetInfo = SceneGadgetInfo.newBuilder() .setGadgetId(this.getItemData().getGadgetId()) .setTrifleItem(this.getItem().toProto()) - .setBornType(GadgetBornType.GADGET_BORN_IN_AIR) + .setBornType(GadgetBornType.GADGET_BORN_TYPE_IN_AIR) .setAuthorityPeerId(this.getWorld().getHostPeerId()) .setIsEnableInteract(true); diff --git a/src/main/java/emu/grasscutter/game/entity/EntityMonster.java b/src/main/java/emu/grasscutter/game/entity/EntityMonster.java index c9d0c0982..bd9785497 100644 --- a/src/main/java/emu/grasscutter/game/entity/EntityMonster.java +++ b/src/main/java/emu/grasscutter/game/entity/EntityMonster.java @@ -2,9 +2,10 @@ package emu.grasscutter.game.entity; import emu.grasscutter.data.GameData; import emu.grasscutter.data.common.PropGrowCurve; -import emu.grasscutter.data.def.MonsterCurveData; -import emu.grasscutter.data.def.MonsterData; +import emu.grasscutter.data.excels.MonsterCurveData; +import emu.grasscutter.data.excels.MonsterData; import emu.grasscutter.game.dungeons.DungeonChallenge; +import emu.grasscutter.game.player.Player; import emu.grasscutter.game.props.EntityIdType; import emu.grasscutter.game.props.FightProperty; import emu.grasscutter.game.props.PlayerProperty; @@ -111,17 +112,41 @@ public class EntityMonster extends GameEntity { this.poseId = poseId; } + @Override + public void damage(float amount, int killerId) { + // Get HP before damage. + float hpBeforeDamage = this.getFightProperty(FightProperty.FIGHT_PROP_CUR_HP); + + // Apply damage. + super.damage(amount, killerId); + + // Get HP after damage. + float hpAfterDamage = this.getFightProperty(FightProperty.FIGHT_PROP_CUR_HP); + + // Invoke energy drop logic. + for (Player player : this.getScene().getPlayers()) { + player.getEnergyManager().handleMonsterEnergyDrop(this, hpBeforeDamage, hpAfterDamage); + } + } + @Override public void onDeath(int killerId) { if (this.getSpawnEntry() != null) { this.getScene().getDeadSpawnedEntities().add(getSpawnEntry()); } - if (getScene().getScriptManager().isInit() && this.getGroupId() > 0) { - getScene().getScriptManager().callEvent(EventType.EVENT_ANY_MONSTER_DIE, null); - } + // first set the challenge data if (getScene().getChallenge() != null && getScene().getChallenge().getGroup().id == this.getGroupId()) { getScene().getChallenge().onMonsterDie(this); } + if (getScene().getScriptManager().isInit() && this.getGroupId() > 0) { + if(getScene().getScriptManager().getScriptMonsterSpawnService() != null){ + getScene().getScriptManager().getScriptMonsterSpawnService().onMonsterDead(this); + } + // prevent spawn monster after success + if(getScene().getChallenge() != null && getScene().getChallenge().inProgress()){ + getScene().getScriptManager().callEvent(EventType.EVENT_ANY_MONSTER_DIE, null); + } + } } public void recalcStats() { @@ -186,7 +211,7 @@ public class EntityMonster extends GameEntity { SceneEntityInfo.Builder entityInfo = SceneEntityInfo.newBuilder() .setEntityId(getId()) - .setEntityType(ProtEntityType.PROT_ENTITY_MONSTER) + .setEntityType(ProtEntityType.PROT_ENTITY_TYPE_MONSTER) .setMotionInfo(this.getMotionInfo()) .addAnimatorParaList(AnimatorParameterValueInfoPair.newBuilder()) .setEntityClientData(EntityClientData.newBuilder()) @@ -215,7 +240,7 @@ public class EntityMonster extends GameEntity { .setAuthorityPeerId(getWorld().getHostPeerId()) .setPoseId(this.getPoseId()) .setBlockId(3001) - .setBornType(MonsterBornType.MONSTER_BORN_DEFAULT) + .setBornType(MonsterBornType.MONSTER_BORN_TYPE_DEFAULT) .setSpecialNameId(40); if (getMonsterData().getDescribeData() != null) { diff --git a/src/main/java/emu/grasscutter/game/entity/EntityVehicle.java b/src/main/java/emu/grasscutter/game/entity/EntityVehicle.java index 09f80e15b..ceab84bc3 100644 --- a/src/main/java/emu/grasscutter/game/entity/EntityVehicle.java +++ b/src/main/java/emu/grasscutter/game/entity/EntityVehicle.java @@ -106,7 +106,7 @@ public class EntityVehicle extends EntityBaseGadget { SceneEntityInfo.Builder entityInfo = SceneEntityInfo.newBuilder() .setEntityId(getId()) - .setEntityType(ProtEntityType.PROT_ENTITY_GADGET) + .setEntityType(ProtEntityType.PROT_ENTITY_TYPE_GADGET) .setMotionInfo(MotionInfo.newBuilder().setPos(getPosition().toProto()).setRot(getRotation().toProto()).setSpeed(Vector.newBuilder())) .addAnimatorParaList(AnimatorParameterValueInfoPair.newBuilder()) .setGadget(gadgetInfo) diff --git a/src/main/java/emu/grasscutter/game/entity/GameEntity.java b/src/main/java/emu/grasscutter/game/entity/GameEntity.java index 627b41103..b9ca01aea 100644 --- a/src/main/java/emu/grasscutter/game/entity/GameEntity.java +++ b/src/main/java/emu/grasscutter/game/entity/GameEntity.java @@ -1,5 +1,8 @@ package emu.grasscutter.game.entity; +import java.util.HashMap; +import java.util.Map; + import emu.grasscutter.game.props.FightProperty; import emu.grasscutter.game.props.LifeState; import emu.grasscutter.game.world.Scene; @@ -9,8 +12,11 @@ import emu.grasscutter.net.proto.MotionInfoOuterClass.MotionInfo; import emu.grasscutter.net.proto.MotionStateOuterClass.MotionState; import emu.grasscutter.net.proto.SceneEntityInfoOuterClass.SceneEntityInfo; import emu.grasscutter.net.proto.VectorOuterClass.Vector; +import emu.grasscutter.server.packet.send.PacketEntityFightPropUpdateNotify; import emu.grasscutter.utils.Position; import it.unimi.dsi.fastutil.ints.Int2FloatOpenHashMap; +import it.unimi.dsi.fastutil.ints.Int2ObjectMap; +import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap; public abstract class GameEntity { protected int id; @@ -25,9 +31,13 @@ public abstract class GameEntity { private int lastMoveSceneTimeMs; private int lastMoveReliableSeq; + // Abilities + private Map metaOverrideMap; + private Int2ObjectMap metaModifiers; + public GameEntity(Scene scene) { this.scene = scene; - this.moveState = MotionState.MOTION_NONE; + this.moveState = MotionState.MOTION_STATE_NONE; } public int getId() { @@ -54,6 +64,20 @@ public abstract class GameEntity { return isAlive() ? LifeState.LIFE_ALIVE : LifeState.LIFE_DEAD; } + public Map getMetaOverrideMap() { + if (this.metaOverrideMap == null) { + this.metaOverrideMap = new HashMap<>(); + } + return this.metaOverrideMap; + } + + public Int2ObjectMap getMetaModifiers() { + if (this.metaModifiers == null) { + this.metaModifiers = new Int2ObjectOpenHashMap<>(); + } + return this.metaModifiers; + } + public abstract Int2FloatOpenHashMap getFightProperties(); public abstract Position getPosition(); @@ -146,4 +170,53 @@ public abstract class GameEntity { public void setSpawnEntry(SpawnDataEntry spawnEntry) { this.spawnEntry = spawnEntry; } + + public float heal(float amount) { + if (this.getFightProperties() == null) { + return 0f; + } + + float curHp = getFightProperty(FightProperty.FIGHT_PROP_CUR_HP); + float maxHp = getFightProperty(FightProperty.FIGHT_PROP_MAX_HP); + + if (curHp >= maxHp) { + return 0f; + } + + float healed = Math.min(maxHp - curHp, amount); + this.addFightProperty(FightProperty.FIGHT_PROP_CUR_HP, healed); + + getScene().broadcastPacket(new PacketEntityFightPropUpdateNotify(this, FightProperty.FIGHT_PROP_CUR_HP)); + + return healed; + } + + public void damage(float amount) { + damage(amount, 0); + } + + public void damage(float amount, int killerId) { + // Sanity check + if (getFightProperties() == null) { + return; + } + + // Lose hp + addFightProperty(FightProperty.FIGHT_PROP_CUR_HP, -amount); + + // Check if dead + boolean isDead = false; + if (getFightProperty(FightProperty.FIGHT_PROP_CUR_HP) <= 0f) { + setFightProperty(FightProperty.FIGHT_PROP_CUR_HP, 0f); + isDead = true; + } + + // Packets + this.getScene().broadcastPacket(new PacketEntityFightPropUpdateNotify(this, FightProperty.FIGHT_PROP_CUR_HP)); + + // Check if dead + if (isDead) { + getScene().killEntity(this, killerId); + } + } } diff --git a/src/main/java/emu/grasscutter/game/expedition/ExpeditionManager.java b/src/main/java/emu/grasscutter/game/expedition/ExpeditionManager.java index 5d1b652e1..9aab70992 100644 --- a/src/main/java/emu/grasscutter/game/expedition/ExpeditionManager.java +++ b/src/main/java/emu/grasscutter/game/expedition/ExpeditionManager.java @@ -2,14 +2,19 @@ package emu.grasscutter.game.expedition; import com.google.gson.reflect.TypeToken; import emu.grasscutter.Grasscutter; +import emu.grasscutter.data.DataLoader; import emu.grasscutter.server.game.GameServer; import it.unimi.dsi.fastutil.ints.Int2ObjectMap; import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap; import java.io.FileReader; +import java.io.InputStreamReader; +import java.io.Reader; import java.util.Collection; import java.util.List; +import static emu.grasscutter.Configuration.*; + public class ExpeditionManager { public GameServer getGameServer() { return gameServer; @@ -28,7 +33,7 @@ public class ExpeditionManager { } public synchronized void load() { - try (FileReader fileReader = new FileReader(Grasscutter.getConfig().DATA_FOLDER + "ExpeditionReward.json")) { + try (Reader fileReader = new InputStreamReader(DataLoader.load("ExpeditionReward.json"))) { getExpeditionRewardDataList().clear(); List banners = Grasscutter.getGsonFactory().fromJson(fileReader, TypeToken.getParameterized(Collection.class, ExpeditionRewardInfo.class).getType()); if(banners.size() > 0) { diff --git a/src/main/java/emu/grasscutter/game/friends/FriendsList.java b/src/main/java/emu/grasscutter/game/friends/FriendsList.java index de73ecb30..70eace5ca 100644 --- a/src/main/java/emu/grasscutter/game/friends/FriendsList.java +++ b/src/main/java/emu/grasscutter/game/friends/FriendsList.java @@ -104,7 +104,7 @@ public class FriendsList { } // Handle - if (result == DealAddFriendResultType.DEAL_ADD_FRIEND_ACCEPT) { // Request accepted + if (result == DealAddFriendResultType.DEAL_ADD_FRIEND_RESULT_TYPE_ACCEPT) { // Request accepted myFriendship.setIsFriend(true); theirFriendship.setIsFriend(true); diff --git a/src/main/java/emu/grasscutter/game/friends/Friendship.java b/src/main/java/emu/grasscutter/game/friends/Friendship.java index 79439bb3a..91afc85aa 100644 --- a/src/main/java/emu/grasscutter/game/friends/Friendship.java +++ b/src/main/java/emu/grasscutter/game/friends/Friendship.java @@ -95,13 +95,13 @@ public class Friendship { .setProfilePicture(ProfilePicture.newBuilder().setAvatarId(getFriendProfile().getAvatarId())) .setWorldLevel(getFriendProfile().getWorldLevel()) .setSignature(getFriendProfile().getSignature()) - .setOnlineState(getFriendProfile().isOnline() ? FriendOnlineState.FRIEND_ONLINE : FriendOnlineState.FREIEND_DISCONNECT) + .setOnlineState(getFriendProfile().isOnline() ? FriendOnlineState.FRIEND_ONLINE_STATE_ONLINE : FriendOnlineState.FRIEND_ONLINE_STATE_FREIEND_DISCONNECT) .setIsMpModeAvailable(true) .setLastActiveTime(getFriendProfile().getLastActiveTime()) .setNameCardId(getFriendProfile().getNameCard()) .setParam(getFriendProfile().getDaysSinceLogin()) .setIsGameSource(true) - .setPlatformType(PlatformTypeOuterClass.PlatformType.PC) + .setPlatformType(PlatformTypeOuterClass.PlatformType.PLATFORM_TYPE_PC) .build(); return proto; diff --git a/src/main/java/emu/grasscutter/game/gacha/GachaBanner.java b/src/main/java/emu/grasscutter/game/gacha/GachaBanner.java index b48cb0898..f0dfca88c 100644 --- a/src/main/java/emu/grasscutter/game/gacha/GachaBanner.java +++ b/src/main/java/emu/grasscutter/game/gacha/GachaBanner.java @@ -1,8 +1,13 @@ package emu.grasscutter.game.gacha; -import emu.grasscutter.Grasscutter; import emu.grasscutter.net.proto.GachaInfoOuterClass.GachaInfo; import emu.grasscutter.net.proto.GachaUpInfoOuterClass.GachaUpInfo; +import emu.grasscutter.utils.Utils; + +import static emu.grasscutter.Configuration.*; + +import emu.grasscutter.Grasscutter; +import emu.grasscutter.data.common.ItemParamData; public class GachaBanner { private int gachaType; @@ -10,18 +15,35 @@ public class GachaBanner { private String prefabPath; private String previewPrefabPath; private String titlePath; - private int costItem; + private int costItemId = 0; + private int costItemAmount = 1; + private int costItemId10 = 0; + private int costItemAmount10 = 10; private int beginTime; private int endTime; private int sortId; - private int[] rateUpItems1; - private int[] rateUpItems2; - private int baseYellowWeight = 60; // Max 10000 - private int basePurpleWeight = 510; // Max 10000 - private int eventChance = 50; // Chance to win a featured event item - private int softPity = 75; - private int hardPity = 90; + private int[] rateUpItems4 = {}; + private int[] rateUpItems5 = {}; + private int[] fallbackItems3 = {11301, 11302, 11306, 12301, 12302, 12305, 13303, 14301, 14302, 14304, 15301, 15302, 15304}; + private int[] fallbackItems4Pool1 = {1014, 1020, 1023, 1024, 1025, 1027, 1031, 1032, 1034, 1036, 1039, 1043, 1044, 1045, 1048, 1053, 1055, 1056, 1064}; + private int[] fallbackItems4Pool2 = {11401, 11402, 11403, 11405, 12401, 12402, 12403, 12405, 13401, 13407, 14401, 14402, 14403, 14409, 15401, 15402, 15403, 15405}; + private int[] fallbackItems5Pool1 = {1003, 1016, 1042, 1035, 1041}; + private int[] fallbackItems5Pool2 = {11501, 11502, 12501, 12502, 13502, 13505, 14501, 14502, 15501, 15502}; + private boolean removeC6FromPool = false; + private boolean autoStripRateUpFromFallback = true; + private int[][] weights4 = {{1,510}, {8,510}, {10,10000}}; + private int[][] weights5 = {{1,75}, {73,150}, {90,10000}}; + private int[][] poolBalanceWeights4 = {{1,255}, {17,255}, {21,10455}}; + private int[][] poolBalanceWeights5 = {{1,30}, {147,150}, {181,10230}}; + private int eventChance4 = 50; // Chance to win a featured event item + private int eventChance5 = 50; // Chance to win a featured event item 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; public int getGachaType() { return gachaType; @@ -47,8 +69,15 @@ public class GachaBanner { return titlePath; } + public ItemParamData getCost(int numRolls) { + return switch (numRolls) { + case 10 -> new ItemParamData((costItemId10 > 0) ? costItemId10 : getCostItem(), costItemAmount10); + default -> new ItemParamData(getCostItem(), costItemAmount * numRolls); + }; + } + public int getCostItem() { - return costItem; + return (costItem > 0) ? costItem : costItemId; } public int getBeginTime() { @@ -63,90 +92,102 @@ public class GachaBanner { return sortId; } - public int getBaseYellowWeight() { - return baseYellowWeight; + public int[] getRateUpItems4() { + return (rateUpItems2.length > 0) ? rateUpItems2 : rateUpItems4; + } + public int[] getRateUpItems5() { + return (rateUpItems1.length > 0) ? rateUpItems1 : rateUpItems5; } - public int getBasePurpleWeight() { - return basePurpleWeight; + public int[] getFallbackItems3() {return fallbackItems3;} + public int[] getFallbackItems4Pool1() {return fallbackItems4Pool1;} + public int[] getFallbackItems4Pool2() {return fallbackItems4Pool2;} + public int[] getFallbackItems5Pool1() {return fallbackItems5Pool1;} + public int[] getFallbackItems5Pool2() {return fallbackItems5Pool2;} + + public boolean getRemoveC6FromPool() {return removeC6FromPool;} + public boolean getAutoStripRateUpFromFallback() {return autoStripRateUpFromFallback;} + + + public int getWeight(int rarity, int pity) { + return switch(rarity) { + case 4 -> Utils.lerp(pity, weights4); + default -> Utils.lerp(pity, weights5); + }; } - public int[] getRateUpItems1() { - return rateUpItems1; + public int getPoolBalanceWeight(int rarity, int pity) { + return switch(rarity) { + case 4 -> Utils.lerp(pity, poolBalanceWeights4); + default -> Utils.lerp(pity, poolBalanceWeights5); + }; } - public int[] getRateUpItems2() { - return rateUpItems2; - } - - public int getSoftPity() { - return softPity - 1; - } - - public int getHardPity() { - return hardPity - 1; - } - - public int getEventChance() { - return eventChance; + public int getEventChance(int rarity) { + return switch(rarity) { + case 4 -> eventChance4; + default -> (eventChance > -1) ? eventChance : eventChance5; + }; } @Deprecated public GachaInfo toProto() { return toProto(""); } + public GachaInfo toProto(String sessionKey) { - String record = "http" + (Grasscutter.getConfig().getDispatchOptions().FrontHTTPS ? "s" : "") + "://" - + (Grasscutter.getConfig().getDispatchOptions().PublicIp.isEmpty() ? - Grasscutter.getConfig().getDispatchOptions().Ip : - Grasscutter.getConfig().getDispatchOptions().PublicIp) - + ":" - + Integer.toString(Grasscutter.getConfig().getDispatchOptions().PublicPort == 0 ? - Grasscutter.getConfig().getDispatchOptions().Port : - Grasscutter.getConfig().getDispatchOptions().PublicPort) + String record = "http" + (HTTP_ENCRYPTION.useInRouting ? "s" : "") + "://" + + lr(HTTP_INFO.accessAddress, HTTP_INFO.bindAddress) + ":" + + lr(HTTP_INFO.accessPort, HTTP_INFO.bindPort) + "/gacha?s=" + sessionKey + "&gachaType=" + gachaType; + String details = "http" + (HTTP_ENCRYPTION.useInRouting ? "s" : "") + "://" + + lr(HTTP_INFO.accessAddress, HTTP_INFO.bindAddress) + ":" + + lr(HTTP_INFO.accessPort, HTTP_INFO.bindPort) + + "/gacha/details?s=" + sessionKey + "&scheduleId=" + scheduleId; + // Grasscutter.getLogger().info("record = " + record); + ItemParamData costItem1 = this.getCost(1); + ItemParamData costItem10 = this.getCost(10); GachaInfo.Builder info = GachaInfo.newBuilder() .setGachaType(this.getGachaType()) .setScheduleId(this.getScheduleId()) .setBeginTime(this.getBeginTime()) .setEndTime(this.getEndTime()) - .setCostItemId(this.getCostItem()) - .setCostItemNum(1) + .setCostItemId(costItem1.getId()) + .setCostItemNum(costItem1.getCount()) + .setTenCostItemId(costItem10.getId()) + .setTenCostItemNum(costItem10.getCount()) .setGachaPrefabPath(this.getPrefabPath()) .setGachaPreviewPrefabPath(this.getPreviewPrefabPath()) - .setGachaProbUrl(record) - .setGachaProbUrlOversea(record) + .setGachaProbUrl(details) + .setGachaProbUrlOversea(details) .setGachaRecordUrl(record) .setGachaRecordUrlOversea(record) - .setTenCostItemId(this.getCostItem()) - .setTenCostItemNum(10) .setLeftGachaTimes(Integer.MAX_VALUE) .setGachaTimesLimit(Integer.MAX_VALUE) .setGachaSortId(this.getSortId()); - if (this.getTitlePath() != null) { - info.setGachaTitlePath(this.getTitlePath()); + info.setTitleTextmap(this.getTitlePath()); } - if (this.getRateUpItems1().length > 0) { + if (this.getRateUpItems5().length > 0) { GachaUpInfo.Builder upInfo = GachaUpInfo.newBuilder().setItemParentType(1); - for (int id : getRateUpItems1()) { + for (int id : getRateUpItems5()) { upInfo.addItemIdList(id); - info.addMainNameId(id); + info.addDisplayUp5ItemList(id); } info.addGachaUpInfoList(upInfo); } - if (this.getRateUpItems2().length > 0) { + if (this.getRateUpItems4().length > 0) { GachaUpInfo.Builder upInfo = GachaUpInfo.newBuilder().setItemParentType(2); - for (int id : getRateUpItems2()) { + for (int id : getRateUpItems4()) { upInfo.addItemIdList(id); - if (info.getSubNameIdCount() == 0) { - info.addSubNameId(id); + if (info.getDisplayUp4ItemListCount() == 0) { + info.addDisplayUp4ItemList(id); } } diff --git a/src/main/java/emu/grasscutter/game/gacha/GachaManager.java b/src/main/java/emu/grasscutter/game/gacha/GachaManager.java index ca7640e17..4ebce55e8 100644 --- a/src/main/java/emu/grasscutter/game/gacha/GachaManager.java +++ b/src/main/java/emu/grasscutter/game/gacha/GachaManager.java @@ -2,8 +2,11 @@ package emu.grasscutter.game.gacha; import java.io.File; import java.io.FileReader; +import java.io.InputStreamReader; +import java.io.Reader; import java.nio.file.*; import java.util.ArrayList; +import java.util.Arrays; import java.util.Collection; import java.util.List; import java.util.concurrent.ThreadLocalRandom; @@ -12,12 +15,15 @@ import com.google.gson.reflect.TypeToken; import com.sun.nio.file.SensitivityWatchEventModifier; import emu.grasscutter.Grasscutter; +import emu.grasscutter.data.DataLoader; import emu.grasscutter.data.GameData; -import emu.grasscutter.data.def.ItemData; +import emu.grasscutter.data.common.ItemParamData; +import emu.grasscutter.data.excels.ItemData; import emu.grasscutter.database.DatabaseHelper; import emu.grasscutter.game.avatar.Avatar; import emu.grasscutter.game.gacha.GachaBanner.BannerType; import emu.grasscutter.game.inventory.GameItem; +import emu.grasscutter.game.inventory.Inventory; import emu.grasscutter.game.inventory.ItemType; import emu.grasscutter.game.inventory.MaterialType; import emu.grasscutter.game.player.Player; @@ -28,26 +34,25 @@ import emu.grasscutter.net.proto.ItemParamOuterClass.ItemParam; import emu.grasscutter.server.game.GameServer; import emu.grasscutter.server.game.GameServerTickEvent; import emu.grasscutter.server.packet.send.PacketDoGachaRsp; +import emu.grasscutter.utils.Utils; import it.unimi.dsi.fastutil.ints.Int2ObjectMap; import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap; import it.unimi.dsi.fastutil.ints.IntArrayList; import it.unimi.dsi.fastutil.ints.IntList; import org.greenrobot.eventbus.Subscribe; +import static emu.grasscutter.Configuration.*; + public class GachaManager { private final GameServer server; private final Int2ObjectMap gachaBanners; private GetGachaInfoRsp cachedProto; WatchService watchService; - - private int[] yellowAvatars = new int[] {1003, 1016, 1042, 1035, 1041}; - private int[] yellowWeapons = new int[] {11501, 11502, 12501, 12502, 13502, 13505, 14501, 14502, 15501, 15502}; - private int[] purpleAvatars = new int[] {1006, 1014, 1015, 1020, 1021, 1023, 1024, 1025, 1027, 1031, 1032, 1034, 1036, 1039, 1043, 1044, 1045, 1048, 1053, 1055, 1056, 1064}; - private int[] purpleWeapons = new int[] {11401, 11402, 11403, 11405, 12401, 12402, 12403, 12405, 13401, 13407, 14401, 14402, 14403, 14409, 15401, 15402, 15403, 15405}; - private int[] blueWeapons = new int[] {11301, 11302, 11306, 12301, 12302, 12305, 13303, 14301, 14302, 14304, 15301, 15302, 15304}; - private static int starglitterId = 221; - private static int stardustId = 222; + private static final int starglitterId = 221; + private static final int stardustId = 222; + private int[] fallbackItems4Pool2Default = {11401, 11402, 11403, 11405, 12401, 12402, 12403, 12405, 13401, 13407, 14401, 14402, 14403, 14409, 15401, 15402, 15403, 15405}; + private int[] fallbackItems5Pool2Default = {11501, 11502, 12501, 12502, 13502, 13505, 14501, 14502, 15501, 15502}; public GachaManager(GameServer server) { this.server = server; @@ -64,7 +69,7 @@ public class GachaManager { return gachaBanners; } - public int randomRange(int min, int max) { + public int randomRange(int min, int max) { // Both are inclusive return ThreadLocalRandom.current().nextInt(max - min + 1) + min; } @@ -73,14 +78,16 @@ public class GachaManager { } public synchronized void load() { - try (FileReader fileReader = new FileReader(Grasscutter.getConfig().DATA_FOLDER + "Banners.json")) { + try (Reader fileReader = new InputStreamReader(DataLoader.load("Banners.json"))) { getGachaBanners().clear(); List banners = Grasscutter.getGsonFactory().fromJson(fileReader, TypeToken.getParameterized(Collection.class, GachaBanner.class).getType()); if(banners.size() > 0) { for (GachaBanner banner : banners) { - getGachaBanners().put(banner.getGachaType(), banner); + getGachaBanners().put(banner.getScheduleId(), banner); } Grasscutter.getLogger().info("Banners successfully loaded."); + + this.cachedProto = createProto(); } else { Grasscutter.getLogger().error("Unable to load banners. Banners size is 0."); @@ -90,116 +97,196 @@ public class GachaManager { e.printStackTrace(); } } + + private class BannerPools { + public int[] rateUpItems4; + public int[] rateUpItems5; + public int[] fallbackItems4Pool1; + public int[] fallbackItems4Pool2; + public int[] fallbackItems5Pool1; + public int[] fallbackItems5Pool2; + + public BannerPools(GachaBanner banner) { + rateUpItems4 = banner.getRateUpItems4(); + rateUpItems5 = banner.getRateUpItems5(); + fallbackItems4Pool1 = banner.getFallbackItems4Pool1(); + fallbackItems4Pool2 = banner.getFallbackItems4Pool2(); + fallbackItems5Pool1 = banner.getFallbackItems5Pool1(); + fallbackItems5Pool2 = banner.getFallbackItems5Pool2(); + + if (banner.getAutoStripRateUpFromFallback()) { + fallbackItems4Pool1 = Utils.setSubtract(fallbackItems4Pool1, rateUpItems4); + fallbackItems4Pool2 = Utils.setSubtract(fallbackItems4Pool2, rateUpItems4); + fallbackItems5Pool1 = Utils.setSubtract(fallbackItems5Pool1, rateUpItems5); + fallbackItems5Pool2 = Utils.setSubtract(fallbackItems5Pool2, rateUpItems5); + } + } + + public void removeFromAllPools(int[] itemIds) { + rateUpItems4 = Utils.setSubtract(rateUpItems4, itemIds); + rateUpItems5 = Utils.setSubtract(rateUpItems5, itemIds); + fallbackItems4Pool1 = Utils.setSubtract(fallbackItems4Pool1, itemIds); + fallbackItems4Pool2 = Utils.setSubtract(fallbackItems4Pool2, itemIds); + fallbackItems5Pool1 = Utils.setSubtract(fallbackItems5Pool1, itemIds); + fallbackItems5Pool2 = Utils.setSubtract(fallbackItems5Pool2, itemIds); + } + } + + private synchronized int checkPlayerAvatarConstellationLevel(Player player, int itemId) { // Maybe this would be useful in the Player class? + ItemData itemData = GameData.getItemDataMap().get(itemId); + if ((itemData == null) || (itemData.getMaterialType() != MaterialType.MATERIAL_AVATAR)){ + return -2; // Not an Avatar + } + Avatar avatar = player.getAvatars().getAvatarById((itemId % 1000) + 10000000); + if (avatar == null) { + return -1; // Doesn't have + } + // Constellation + int constLevel = avatar.getCoreProudSkillLevel(); + GameItem constItem = player.getInventory().getInventoryTab(ItemType.ITEM_MATERIAL).getItemById(itemId + 100); + constLevel += (constItem == null)? 0 : constItem.getCount(); + return constLevel; + } + + private synchronized int[] removeC6FromPool(int[] itemPool, Player player) { + IntList temp = new IntArrayList(); + for (int itemId : itemPool) { + if (checkPlayerAvatarConstellationLevel(player, itemId) < 6) { + temp.add(itemId); + } + } + return temp.toIntArray(); + } + + private synchronized int drawRoulette(int[] weights, int cutoff) { + // This follows the logic laid out in issue #183 + // Simple weighted selection with an upper bound for the roll that cuts off trailing entries + // All weights must be >= 0 + int total = 0; + for (int weight : weights) { + if (weight < 0) { + throw new IllegalArgumentException("Weights must be non-negative!"); + } + total += weight; + } + int roll = ThreadLocalRandom.current().nextInt((total < cutoff)? total : cutoff); + int subTotal = 0; + for (int i=0; i= 1) // Lost previous coinflip + || (this.randomRange(1, 100) <= banner.getEventChance(rarity)); // Won this coinflip + if (pullFeatured && (featured.length > 0)) { + itemId = getRandom(featured); + gachaInfo.setFailedFeaturedItemPulls(rarity, 0); + } else { + gachaInfo.addFailedFeaturedItemPulls(rarity, 1); + if (fallback1.length < 1) { + if (fallback2.length < 1) { + itemId = getRandom((rarity==5)? fallbackItems5Pool2Default : fallbackItems4Pool2Default); + } else { + itemId = getRandom(fallback2); + } + } else if (fallback2.length < 1) { + itemId = getRandom(fallback1); + } else { // Both pools are possible, use the pool balancer + int pityPool1 = banner.getPoolBalanceWeight(rarity, gachaInfo.getPityPool(rarity, 1)); + int pityPool2 = banner.getPoolBalanceWeight(rarity, gachaInfo.getPityPool(rarity, 2)); + int chosenPool = switch ((pityPool1 >= pityPool2)? 1 : 0) { // Larger weight must come first for the hard cutoff to function correctly + case 1 -> 1 + drawRoulette(new int[] {pityPool1, pityPool2}, 10000); + default -> 2 - drawRoulette(new int[] {pityPool2, pityPool1}, 10000); + }; + itemId = switch (chosenPool) { + case 1: + gachaInfo.setPityPool(rarity, 1, 0); + yield getRandom(fallback1); + default: + gachaInfo.setPityPool(rarity, 2, 0); + yield getRandom(fallback2); + }; + } + } + return itemId; + } + + private synchronized int doPull(GachaBanner banner, PlayerGachaBannerInfo gachaInfo, BannerPools pools) { + // Pre-increment all pity pools (yes this makes all calculations assume 1-indexed pity) + gachaInfo.incPityAll(); + + int[] weights = {banner.getWeight(5, gachaInfo.getPity5()), banner.getWeight(4, gachaInfo.getPity4()), 10000}; + int levelWon = 5 - drawRoulette(weights, 10000); + + return switch (levelWon) { + case 5: + gachaInfo.setPity5(0); + yield doRarePull(pools.rateUpItems5, pools.fallbackItems5Pool1, pools.fallbackItems5Pool2, 5, banner, gachaInfo); + case 4: + gachaInfo.setPity4(0); + yield doRarePull(pools.rateUpItems4, pools.fallbackItems4Pool1, pools.fallbackItems4Pool2, 4, banner, gachaInfo); + default: + yield getRandom(banner.getFallbackItems3()); + }; + } - public synchronized void doPulls(Player player, int gachaType, int times) { + public synchronized void doPulls(Player player, int scheduleId, int times) { // Sanity check if (times != 10 && times != 1) { return; } - if (player.getInventory().getInventoryTab(ItemType.ITEM_WEAPON).getSize() + times > player.getInventory().getInventoryTab(ItemType.ITEM_WEAPON).getMaxCapacity()) { + Inventory inventory = player.getInventory(); + if (inventory.getInventoryTab(ItemType.ITEM_WEAPON).getSize() + times > inventory.getInventoryTab(ItemType.ITEM_WEAPON).getMaxCapacity()) { player.sendPacket(new PacketDoGachaRsp()); return; } // Get banner - GachaBanner banner = this.getGachaBanners().get(gachaType); + GachaBanner banner = this.getGachaBanners().get(scheduleId); if (banner == null) { player.sendPacket(new PacketDoGachaRsp()); return; } // Spend currency - if (banner.getCostItem() > 0) { - GameItem costItem = player.getInventory().getInventoryTab(ItemType.ITEM_MATERIAL).getItemById(banner.getCostItem()); - if (costItem == null || costItem.getCount() < times) { - return; - } - - player.getInventory().removeItem(costItem, times); - } - - // Roll - PlayerGachaBannerInfo gachaInfo = player.getGachaInfo().getBannerInfo(banner); - IntList wonItems = new IntArrayList(times); - - for (int i = 0; i < times; i++) { - int random = this.randomRange(1, 10000); - int itemId = 0; - - int bonusYellowChance = gachaInfo.getPity5() >= banner.getSoftPity() ? 100 * (gachaInfo.getPity5() - banner.getSoftPity() - 1): 0; - int yellowChance = banner.getBaseYellowWeight() + (int) Math.floor(100f * (gachaInfo.getPity5() / (banner.getSoftPity() - 1D))) + bonusYellowChance; - int purpleChance = 10000 - (banner.getBasePurpleWeight() + (int) Math.floor(790f * (gachaInfo.getPity4() / 8f))); - - if (random <= yellowChance || gachaInfo.getPity5() >= banner.getHardPity()) { - if (banner.getRateUpItems1().length > 0) { - int eventChance = this.randomRange(1, 100); - - if (eventChance <= banner.getEventChance() || gachaInfo.getFailedFeaturedItemPulls() >= 1) { - itemId = getRandom(banner.getRateUpItems1()); - gachaInfo.setFailedFeaturedItemPulls(0); - } else { - // Lost the 50/50... rip - gachaInfo.addFailedFeaturedItemPulls(1); - } - } - - if (itemId == 0) { - int typeChance = this.randomRange(banner.getBannerType() == BannerType.WEAPON ? 2 : 1, banner.getBannerType() == BannerType.EVENT ? 1 : 2); - if (typeChance == 1) { - itemId = getRandom(this.yellowAvatars); - } else { - itemId = getRandom(this.yellowWeapons); - } - } - - // Pity - gachaInfo.addPity4(1); - gachaInfo.setPity5(0); - } else if (random >= purpleChance || gachaInfo.getPity4() >= 9) { - if (banner.getRateUpItems2().length > 0) { - int eventChance = this.randomRange(1, 100); - - if (eventChance >= 50) { - itemId = getRandom(banner.getRateUpItems2()); - } - } - - if (itemId == 0) { - int typeChance = this.randomRange(banner.getBannerType() == BannerType.WEAPON ? 2 : 1, banner.getBannerType() == BannerType.EVENT ? 1 : 2); - if (typeChance == 1) { - itemId = getRandom(this.purpleAvatars); - } else { - itemId = getRandom(this.purpleWeapons); - } - } - - // Pity - gachaInfo.addPity5(1); - gachaInfo.setPity4(0); - } else { - itemId = getRandom(this.blueWeapons); - - // Pity - gachaInfo.addPity4(1); - gachaInfo.addPity5(1); - } - - // Add winning item - wonItems.add(itemId); + ItemParamData cost = banner.getCost(times); + if (cost.getCount() > 0 && !inventory.payItem(cost)) { + player.sendPacket(new PacketDoGachaRsp()); + return; } // Add to character + PlayerGachaBannerInfo gachaInfo = player.getGachaInfo().getBannerInfo(banner); + BannerPools pools = new BannerPools(banner); List list = new ArrayList<>(); int stardust = 0, starglitter = 0; + + if (banner.getRemoveC6FromPool()) { // The ultimate form of pity (non-vanilla) + pools.rateUpItems4 = removeC6FromPool(pools.rateUpItems4, player); + pools.rateUpItems5 = removeC6FromPool(pools.rateUpItems5, player); + pools.fallbackItems4Pool1 = removeC6FromPool(pools.fallbackItems4Pool1, player); + pools.fallbackItems4Pool2 = removeC6FromPool(pools.fallbackItems4Pool2, player); + pools.fallbackItems5Pool1 = removeC6FromPool(pools.fallbackItems5Pool1, player); + pools.fallbackItems5Pool2 = removeC6FromPool(pools.fallbackItems5Pool2, player); + } - for (int itemId : wonItems) { + for (int i = 0; i < times; i++) { + // Roll + int itemId = doPull(banner, gachaInfo, pools); ItemData itemData = GameData.getItemDataMap().get(itemId); if (itemData == null) { - continue; + continue; // Maybe we should bail out if an item fails instead of rolling the rest? } // Write gacha record - GachaRecord gachaRecord = new GachaRecord(itemId, player.getUid(), gachaType); + GachaRecord gachaRecord = new GachaRecord(itemId, player.getUid(), banner.getGachaType()); DatabaseHelper.saveGachaRecord(gachaRecord); // Create gacha item @@ -208,63 +295,47 @@ public class GachaManager { boolean isTransferItem = false; // Const check - if (itemData.getMaterialType() == MaterialType.MATERIAL_AVATAR) { - int avatarId = (itemData.getId() % 1000) + 10000000; - Avatar avatar = player.getAvatars().getAvatarById(avatarId); - if (avatar != null) { - int constLevel = avatar.getCoreProudSkillLevel(); - int constItemId = itemData.getId() + 100; - GameItem constItem = player.getInventory().getInventoryTab(ItemType.ITEM_MATERIAL).getItemById(constItemId); - if (constItem != null) { - constLevel += constItem.getCount(); + int constellation = checkPlayerAvatarConstellationLevel(player, itemId); + switch (constellation) { + case -2: // Is weapon + switch (itemData.getRankLevel()) { + case 5 -> addStarglitter = 10; + case 4 -> addStarglitter = 2; + default -> addStardust = 15; } - - if (constLevel < 6) { - // Not max const - addStarglitter = 2; - // Add 1 const - gachaItem.addTransferItems(GachaTransferItem.newBuilder().setItem(ItemParam.newBuilder().setItemId(constItemId).setCount(1)).setIsTransferItemNew(constItem == null)); - player.getInventory().addItem(constItemId, 1); - } else { - // Is max const - addStarglitter = 5; - } - - if (itemData.getRankLevel() == 5) { - addStarglitter *= 5; - } - - isTransferItem = true; - } else { - // New + break; + case -1: // New character gachaItem.setIsGachaItemNew(true); - } - } else { - // Is weapon - switch (itemData.getRankLevel()) { - case 5: - addStarglitter = 10; - break; - case 4: - addStarglitter = 2; - break; - case 3: - addStardust = 15; - break; - } + break; + default: + if (constellation >= 6) { // C6, give consolation starglitter + addStarglitter = (itemData.getRankLevel()==5)? 25 : 5; + } else { // C0-C5, give constellation item + if (banner.getRemoveC6FromPool() && constellation == 5) { // New C6, remove it from the pools so we don't get C7 in a 10pull + pools.removeFromAllPools(new int[] {itemId}); + } + addStarglitter = (itemData.getRankLevel()==5)? 10 : 2; + int constItemId = itemId + 100; + GameItem constItem = inventory.getInventoryTab(ItemType.ITEM_MATERIAL).getItemById(constItemId); + gachaItem.addTransferItems(GachaTransferItem.newBuilder().setItem(ItemParam.newBuilder().setItemId(constItemId).setCount(1)).setIsTransferItemNew(constItem == null)); + inventory.addItem(constItemId, 1); + } + isTransferItem = true; + break; } // Create item GameItem item = new GameItem(itemData); gachaItem.setGachaItem(item.toItemParam()); - player.getInventory().addItem(item); + inventory.addItem(item); stardust += addStardust; starglitter += addStarglitter; if (addStardust > 0) { gachaItem.addTokenItemList(ItemParam.newBuilder().setItemId(stardustId).setCount(addStardust)); - } if (addStarglitter > 0) { + } + if (addStarglitter > 0) { ItemParam starglitterParam = ItemParam.newBuilder().setItemId(starglitterId).setCount(addStarglitter).build(); if (isTransferItem) { gachaItem.addTransferItems(GachaTransferItem.newBuilder().setItem(starglitterParam)); @@ -277,9 +348,10 @@ public class GachaManager { // Add stardust/starglitter if (stardust > 0) { - player.getInventory().addItem(stardustId, stardust); - } if (starglitter > 0) { - player.getInventory().addItem(starglitterId, starglitter); + inventory.addItem(stardustId, stardust); + } + if (starglitter > 0) { + inventory.addItem(starglitterId, starglitter); } // Packets @@ -290,7 +362,7 @@ public class GachaManager { if(this.watchService == null) { try { this.watchService = FileSystems.getDefault().newWatchService(); - Path path = new File(Grasscutter.getConfig().DATA_FOLDER).toPath(); + Path path = new File(DATA()).toPath(); path.register(watchService, new WatchEvent.Kind[]{StandardWatchEventKinds.ENTRY_MODIFY}, SensitivityWatchEventModifier.HIGH); } catch (Exception e) { Grasscutter.getLogger().error("Unable to load the Gacha Manager Watch Service. If ServerOptions.watchGacha is true it will not auto-reload"); @@ -303,7 +375,7 @@ public class GachaManager { @Subscribe public synchronized void watchBannerJson(GameServerTickEvent tickEvent) { - if(Grasscutter.getConfig().getGameServerOptions().WatchGacha) { + if(GAME_OPTIONS.watchGachaConfig) { try { WatchKey watchKey = watchService.take(); @@ -340,8 +412,13 @@ public class GachaManager { private synchronized GetGachaInfoRsp createProto(String sessionKey) { GetGachaInfoRsp.Builder proto = GetGachaInfoRsp.newBuilder().setGachaRandom(12345); + long currentTime = System.currentTimeMillis() / 1000L; + for (GachaBanner banner : getGachaBanners().values()) { - proto.addGachaInfoList(banner.toProto(sessionKey)); + if ((banner.getEndTime() >= currentTime && banner.getBeginTime() <= currentTime) || (banner.getBannerType() == BannerType.STANDARD)) + { + proto.addGachaInfoList(banner.toProto(sessionKey)); + } } return proto.build(); diff --git a/src/main/java/emu/grasscutter/game/gacha/PlayerGachaBannerInfo.java b/src/main/java/emu/grasscutter/game/gacha/PlayerGachaBannerInfo.java index b0c85d355..f07d2eff0 100644 --- a/src/main/java/emu/grasscutter/game/gacha/PlayerGachaBannerInfo.java +++ b/src/main/java/emu/grasscutter/game/gacha/PlayerGachaBannerInfo.java @@ -7,6 +7,11 @@ public class PlayerGachaBannerInfo { private int pity5 = 0; private int pity4 = 0; private int failedFeaturedItemPulls = 0; + private int failedFeatured4ItemPulls = 0; + private int pity5Pool1 = 0; + private int pity5Pool2 = 0; + private int pity4Pool1 = 0; + private int pity4Pool2 = 0; public int getPity5() { return pity5; @@ -32,15 +37,82 @@ public class PlayerGachaBannerInfo { this.pity4 += amount; } - public int getFailedFeaturedItemPulls() { - return failedFeaturedItemPulls; + public int getFailedFeaturedItemPulls(int rarity) { + return switch (rarity) { + case 4 -> failedFeatured4ItemPulls; + default -> failedFeaturedItemPulls; // 5 + }; } - public void setFailedFeaturedItemPulls(int failedEventCharacterPulls) { - this.failedFeaturedItemPulls = failedEventCharacterPulls; + public void setFailedFeaturedItemPulls(int rarity, int amount) { + switch (rarity) { + case 4 -> failedFeatured4ItemPulls = amount; + default -> failedFeaturedItemPulls = amount; // 5 + }; } - public void addFailedFeaturedItemPulls(int amount) { - failedFeaturedItemPulls += amount; + public void addFailedFeaturedItemPulls(int rarity, int amount) { + switch (rarity) { + case 4 -> failedFeatured4ItemPulls += amount; + default -> failedFeaturedItemPulls += amount; // 5 + }; + } + + public int getPityPool(int rarity, int pool) { + return switch (rarity) { + case 4 -> switch (pool) { + case 1 -> pity4Pool1; + default -> pity4Pool2; + }; + default -> switch (pool) { + case 1 -> pity5Pool1; + default -> pity5Pool2; + }; + }; + } + + public void setPityPool(int rarity, int pool, int amount) { + switch (rarity) { + case 4: + switch (pool) { + case 1 -> pity4Pool1 = amount; + default -> pity4Pool2 = amount; + }; + break; + case 5: + default: + switch (pool) { + case 1 -> pity5Pool1 = amount; + default -> pity5Pool2 = amount; + }; + break; + }; + } + + public void addPityPool(int rarity, int pool, int amount) { + switch (rarity) { + case 4: + switch (pool) { + case 1 -> pity4Pool1 += amount; + default -> pity4Pool2 += amount; + }; + break; + case 5: + default: + switch (pool) { + case 1 -> pity5Pool1 += amount; + default -> pity5Pool2 += amount; + }; + break; + }; + } + + public void incPityAll() { + pity4++; + pity5++; + pity4Pool1++; + pity4Pool2++; + pity5Pool1++; + pity5Pool2++; } } diff --git a/src/main/java/emu/grasscutter/game/inventory/GameItem.java b/src/main/java/emu/grasscutter/game/inventory/GameItem.java index 9fbc1e8c1..9aae598ff 100644 --- a/src/main/java/emu/grasscutter/game/inventory/GameItem.java +++ b/src/main/java/emu/grasscutter/game/inventory/GameItem.java @@ -15,9 +15,9 @@ import dev.morphia.annotations.Transient; import emu.grasscutter.data.GameData; import emu.grasscutter.data.GameDepot; -import emu.grasscutter.data.def.ItemData; -import emu.grasscutter.data.def.ReliquaryAffixData; -import emu.grasscutter.data.def.ReliquaryMainPropData; +import emu.grasscutter.data.excels.ItemData; +import emu.grasscutter.data.excels.ReliquaryAffixData; +import emu.grasscutter.data.excels.ReliquaryMainPropData; import emu.grasscutter.database.DatabaseHelper; import emu.grasscutter.game.player.Player; import emu.grasscutter.game.props.FightProperty; diff --git a/src/main/java/emu/grasscutter/game/inventory/Inventory.java b/src/main/java/emu/grasscutter/game/inventory/Inventory.java index 14d1ae203..e175aef01 100644 --- a/src/main/java/emu/grasscutter/game/inventory/Inventory.java +++ b/src/main/java/emu/grasscutter/game/inventory/Inventory.java @@ -6,16 +6,15 @@ import java.util.LinkedList; import java.util.List; import emu.grasscutter.GameConstants; -import emu.grasscutter.Grasscutter; import emu.grasscutter.data.GameData; -import emu.grasscutter.data.def.AvatarCostumeData; -import emu.grasscutter.data.def.AvatarData; -import emu.grasscutter.data.def.AvatarFlycloakData; -import emu.grasscutter.data.def.ItemData; +import emu.grasscutter.data.common.ItemParamData; +import emu.grasscutter.data.excels.AvatarCostumeData; +import emu.grasscutter.data.excels.AvatarData; +import emu.grasscutter.data.excels.AvatarFlycloakData; +import emu.grasscutter.data.excels.ItemData; import emu.grasscutter.database.DatabaseHelper; import emu.grasscutter.game.avatar.AvatarStorage; import emu.grasscutter.game.avatar.Avatar; -import emu.grasscutter.game.entity.EntityAvatar; import emu.grasscutter.game.player.Player; import emu.grasscutter.game.props.ActionReason; import emu.grasscutter.net.proto.ItemParamOuterClass.ItemParam; @@ -28,6 +27,8 @@ import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap; import it.unimi.dsi.fastutil.longs.Long2ObjectMap; import it.unimi.dsi.fastutil.longs.Long2ObjectOpenHashMap; +import static emu.grasscutter.Configuration.*; + public class Inventory implements Iterable { private final Player player; @@ -39,10 +40,10 @@ public class Inventory implements Iterable { this.store = new Long2ObjectOpenHashMap<>(); this.inventoryTypes = new Int2ObjectOpenHashMap<>(); - this.createInventoryTab(ItemType.ITEM_WEAPON, new EquipInventoryTab(Grasscutter.getConfig().getGameServerOptions().InventoryLimitWeapon)); - this.createInventoryTab(ItemType.ITEM_RELIQUARY, new EquipInventoryTab(Grasscutter.getConfig().getGameServerOptions().InventoryLimitRelic)); - this.createInventoryTab(ItemType.ITEM_MATERIAL, new MaterialInventoryTab(Grasscutter.getConfig().getGameServerOptions().InventoryLimitMaterial)); - this.createInventoryTab(ItemType.ITEM_FURNITURE, new MaterialInventoryTab(Grasscutter.getConfig().getGameServerOptions().InventoryLimitFurniture)); + this.createInventoryTab(ItemType.ITEM_WEAPON, new EquipInventoryTab(INVENTORY_LIMITS.weapons)); + this.createInventoryTab(ItemType.ITEM_RELIQUARY, new EquipInventoryTab(INVENTORY_LIMITS.relics)); + this.createInventoryTab(ItemType.ITEM_MATERIAL, new MaterialInventoryTab(INVENTORY_LIMITS.materials)); + this.createInventoryTab(ItemType.ITEM_FURNITURE, new MaterialInventoryTab(INVENTORY_LIMITS.furniture)); } public Player getPlayer() { @@ -149,6 +150,14 @@ public class Inventory implements Iterable { addItems(items.stream().map(param -> new GameItem(param.getItemId(), param.getCount())).toList(), null); } + public void addItemParamDatas(Collection items) { + addItemParamDatas(items, null); + } + + public void addItemParamDatas(Collection items, ActionReason reason) { + addItems(items.stream().map(param -> new GameItem(param.getItemId(), param.getCount())).toList(), reason); + } + private synchronized GameItem putItem(GameItem item) { // Dont add items that dont have a valid item definition. if (item.getItemData() == null) { @@ -172,6 +181,9 @@ public class Inventory implements Iterable { // Handle this.addVirtualItem(item.getItemId(), item.getCount()); return item; + } else if (item.getItemData().getMaterialType() == MaterialType.MATERIAL_ADSORBATE) { + this.player.getEnergyManager().handlePickupElemBall(item); + return null; } else if (item.getItemData().getMaterialType() == MaterialType.MATERIAL_AVATAR) { // Get avatar id int avatarId = (item.getItemId() % 1000) + 10000000; @@ -228,6 +240,7 @@ public class Inventory implements Iterable { } private synchronized void putItem(GameItem item, InventoryTab tab) { + getPlayer().getCodex().checkAddedItem(item); // Set owner and guid FIRST! item.setOwner(getPlayer()); // Put in item store @@ -239,26 +252,78 @@ public class Inventory implements Iterable { private void addVirtualItem(int itemId, int count) { switch (itemId) { - case 101: // Character exp - getPlayer().getServer().getInventoryManager().upgradeAvatar(player, getPlayer().getTeamManager().getCurrentAvatarEntity().getAvatar(), count); - break; - case 102: // Adventure exp - getPlayer().addExpDirectly(count); - break; - case 105: // Companionship exp - getPlayer().getServer().getInventoryManager().upgradeAvatarFetterLevel(player, getPlayer().getTeamManager().getCurrentAvatarEntity().getAvatar(), count); - break; - case 201: // Primogem - getPlayer().setPrimogems(player.getPrimogems() + count); - break; - case 202: // Mora - getPlayer().setMora(player.getMora() + count); - break; - case 203: // Genesis Crystals - getPlayer().setCrystals(player.getCrystals() + count); - break; + case 101 -> // Character exp + getPlayer().getServer().getInventoryManager().upgradeAvatar(player, getPlayer().getTeamManager().getCurrentAvatarEntity().getAvatar(), count); + case 102 -> // Adventure exp + getPlayer().addExpDirectly(count); + case 105 -> // Companionship exp + getPlayer().getServer().getInventoryManager().upgradeAvatarFetterLevel(player, getPlayer().getTeamManager().getCurrentAvatarEntity().getAvatar(), count); + case 201 -> // Primogem + getPlayer().setPrimogems(player.getPrimogems() + count); + case 202 -> // Mora + getPlayer().setMora(player.getMora() + count); + case 203 -> // Genesis Crystals + getPlayer().setCrystals(player.getCrystals() + count); } } + + private int getVirtualItemCount(int itemId) { + switch (itemId) { + case 201: // Primogem + return player.getPrimogems(); + case 202: // Mora + return player.getMora(); + case 203: // Genesis Crystals + return player.getCrystals(); + default: + GameItem item = getInventoryTab(ItemType.ITEM_MATERIAL).getItemById(itemId); // What if we ever want to operate on weapons/relics/furniture? :S + return (item == null) ? 0 : item.getCount(); + } + } + + public boolean payItem(int id, int count) { + return payItem(new ItemParamData(id, count)); + } + + public boolean payItem(ItemParamData costItem) { + return payItems(new ItemParamData[] {costItem}, 1, null); + } + + public boolean payItems(ItemParamData[] costItems) { + return payItems(costItems, 1, null); + } + + public boolean payItems(ItemParamData[] costItems, int quantity) { + return payItems(costItems, quantity, null); + } + + public synchronized boolean payItems(ItemParamData[] costItems, int quantity, ActionReason reason) { + // Make sure player has requisite items + for (ItemParamData cost : costItems) { + if (getVirtualItemCount(cost.getId()) < (cost.getCount() * quantity)) { + return false; + } + } + // All costs are satisfied, now remove them all + for (ItemParamData cost : costItems) { + switch (cost.getId()) { + case 201 -> // Primogem + player.setPrimogems(player.getPrimogems() - (cost.getCount() * quantity)); + case 202 -> // Mora + player.setMora(player.getMora() - (cost.getCount() * quantity)); + case 203 -> // Genesis Crystals + player.setCrystals(player.getCrystals() - (cost.getCount() * quantity)); + default -> + removeItem(getInventoryTab(ItemType.ITEM_MATERIAL).getItemById(cost.getId()), cost.getCount() * quantity); + } + } + + if (reason != null) { // Do we need these? + // getPlayer().sendPacket(new PacketItemAddHintNotify(changedItems, reason)); + } + // getPlayer().sendPacket(new PacketStoreItemChangeNotify(changedItems)); + return true; + } public void removeItems(List items) { // TODO Bulk delete diff --git a/src/main/java/emu/grasscutter/game/managers/ChatManager.java b/src/main/java/emu/grasscutter/game/managers/ChatManager/ChatManager.java similarity index 95% rename from src/main/java/emu/grasscutter/game/managers/ChatManager.java rename to src/main/java/emu/grasscutter/game/managers/ChatManager/ChatManager.java index 7377717a5..42ff2df41 100644 --- a/src/main/java/emu/grasscutter/game/managers/ChatManager.java +++ b/src/main/java/emu/grasscutter/game/managers/ChatManager/ChatManager.java @@ -1,4 +1,4 @@ -package emu.grasscutter.game.managers; +package emu.grasscutter.game.managers.ChatManager; import emu.grasscutter.command.CommandMap; import emu.grasscutter.game.player.Player; @@ -10,7 +10,7 @@ import emu.grasscutter.server.packet.send.PacketPrivateChatNotify; import java.util.Arrays; import java.util.List; -public class ChatManager { +public class ChatManager implements ChatManagerHandler { static final List PREFIXES = Arrays.asList('/', '!'); private final GameServer server; diff --git a/src/main/java/emu/grasscutter/game/managers/ChatManager/ChatManagerHandler.java b/src/main/java/emu/grasscutter/game/managers/ChatManager/ChatManagerHandler.java new file mode 100644 index 000000000..3239265e2 --- /dev/null +++ b/src/main/java/emu/grasscutter/game/managers/ChatManager/ChatManagerHandler.java @@ -0,0 +1,12 @@ +package emu.grasscutter.game.managers.ChatManager; + +import emu.grasscutter.game.player.Player; +import emu.grasscutter.server.game.GameServer; + +public interface ChatManagerHandler { + GameServer getServer(); + void sendPrivateMessage(Player player, int targetUid, String message); + void sendPrivateMessage(Player player, int targetUid, int emote); + void sendTeamMessage(Player player, int channel, String message); + void sendTeamMessage(Player player, int channel, int icon); +} diff --git a/src/main/java/emu/grasscutter/game/managers/DeforestationManager/DeforestationManager.java b/src/main/java/emu/grasscutter/game/managers/DeforestationManager/DeforestationManager.java new file mode 100644 index 000000000..ff7c0b1a9 --- /dev/null +++ b/src/main/java/emu/grasscutter/game/managers/DeforestationManager/DeforestationManager.java @@ -0,0 +1,91 @@ +package emu.grasscutter.game.managers.DeforestationManager; + +import java.util.ArrayList; +import java.util.HashMap; + +import dev.morphia.annotations.Transient; +import emu.grasscutter.Grasscutter; +import emu.grasscutter.data.GameData; +import emu.grasscutter.game.entity.EntityItem; +import emu.grasscutter.game.player.Player; +import emu.grasscutter.game.world.Scene; +import emu.grasscutter.net.proto.HitTreeNotifyOuterClass; +import emu.grasscutter.net.proto.VectorOuterClass; +import emu.grasscutter.utils.Position; + +public class DeforestationManager { + final static int RECORD_EXPIRED_SECONDS = 60*5; // 5 min + final static int RECORD_MAX_TIMES = 3; // max number of wood + final static int RECORD_MAX_TIMES_OTHER_HIT_TREE = 10; // if hit 10 times other trees, reset wood + + @Transient private final Player player; + @Transient private final ArrayList currentRecord; + @Transient private final static HashMap ColliderTypeToWoodItemID = new HashMap<>(); + static { + /* define wood types which reflected to item id*/ + ColliderTypeToWoodItemID.put(1,101301); + ColliderTypeToWoodItemID.put(2,101302); + ColliderTypeToWoodItemID.put(3,101303); + ColliderTypeToWoodItemID.put(4,101304); + ColliderTypeToWoodItemID.put(5,101305); + ColliderTypeToWoodItemID.put(6,101306); + ColliderTypeToWoodItemID.put(7,101307); + ColliderTypeToWoodItemID.put(8,101308); + ColliderTypeToWoodItemID.put(9,101309); + ColliderTypeToWoodItemID.put(10,101310); + ColliderTypeToWoodItemID.put(11,101311); + ColliderTypeToWoodItemID.put(12,101312); + } + public DeforestationManager(Player player){ + this.player = player; + this.currentRecord = new ArrayList<>(); + } + public void resetWood(){ + synchronized (currentRecord) { + currentRecord.clear(); + } + } + public void onDeforestationInvoke(HitTreeNotifyOuterClass.HitTreeNotify hit){ + synchronized (currentRecord) { + //Grasscutter.getLogger().info("onDeforestationInvoke! Wood records {}", currentRecord); + VectorOuterClass.Vector hitPosition = hit.getHitPostion(); + int woodType = hit.getWoodType(); + if (ColliderTypeToWoodItemID.containsKey(woodType)) {// is a available wood type + Scene scene = player.getScene(); + int itemId = ColliderTypeToWoodItemID.get(woodType); + int positionHash = hitPosition.hashCode(); + HitTreeRecord record = searchRecord(positionHash); + if (record == null) { + record = new HitTreeRecord(positionHash); + }else{ + currentRecord.remove(record);// move it to last position + } + currentRecord.add(record); + if(currentRecord.size()>RECORD_MAX_TIMES_OTHER_HIT_TREE){ + currentRecord.remove(0); + } + if(record.record()) { + EntityItem entity = new EntityItem(scene, + null, + GameData.getItemDataMap().get(itemId), + new Position(hitPosition.getX(), hitPosition.getY(), hitPosition.getZ()), + 1, + false); + scene.addEntity(entity); + } + //record.record()=false : too many wood they have deforested, no more wood dropped! + } else { + Grasscutter.getLogger().warn("No wood type {} found.", woodType); + } + } + // unknown wood type + } + private HitTreeRecord searchRecord(int id){ + for (HitTreeRecord record : currentRecord) { + if (record.getUnique() == id) { + return record; + } + } + return null; + } +} diff --git a/src/main/java/emu/grasscutter/game/managers/DeforestationManager/HitTreeRecord.java b/src/main/java/emu/grasscutter/game/managers/DeforestationManager/HitTreeRecord.java new file mode 100644 index 000000000..ee78c7506 --- /dev/null +++ b/src/main/java/emu/grasscutter/game/managers/DeforestationManager/HitTreeRecord.java @@ -0,0 +1,57 @@ +package emu.grasscutter.game.managers.DeforestationManager; + + + +public class HitTreeRecord { + private final int unique; + private short count; // hit this tree times + private long time; // last available hitting time + HitTreeRecord(int unique){ + this.count = 0; + this.time = 0; + this.unique = unique; + } + + /** + * reset hit time + */ + private void resetTime(){ + this.time = System.currentTimeMillis(); + } + + + /** + * commit hit behavior + */ + public boolean record(){ + if (this.count < DeforestationManager.RECORD_MAX_TIMES) { + this.count++; + resetTime(); + return true; + } + // check expired + boolean isWaiting = System.currentTimeMillis() - this.time < DeforestationManager.RECORD_EXPIRED_SECONDS * 1000L; + if(isWaiting){ + return false; + }else{ + this.count = 1; + resetTime(); + return true; + } + } + /** + * get unique id + */ + public int getUnique(){ + return unique; + } + + @Override + public String toString() { + return "HitTreeRecord{" + + "unique=" + unique + + ", count=" + count + + ", time=" + time + + '}'; + } +} diff --git a/src/main/java/emu/grasscutter/game/managers/EnergyManager/EnergyDropEntry.java b/src/main/java/emu/grasscutter/game/managers/EnergyManager/EnergyDropEntry.java new file mode 100644 index 000000000..a26140521 --- /dev/null +++ b/src/main/java/emu/grasscutter/game/managers/EnergyManager/EnergyDropEntry.java @@ -0,0 +1,16 @@ +package emu.grasscutter.game.managers.EnergyManager; + +import java.util.List; + +public class EnergyDropEntry { + private int dropId; + private List dropList; + + public int getDropId() { + return this.dropId; + } + + public List getDropList() { + return this.dropList; + } +} diff --git a/src/main/java/emu/grasscutter/game/managers/EnergyManager/EnergyDropInfo.java b/src/main/java/emu/grasscutter/game/managers/EnergyManager/EnergyDropInfo.java new file mode 100644 index 000000000..40145c545 --- /dev/null +++ b/src/main/java/emu/grasscutter/game/managers/EnergyManager/EnergyDropInfo.java @@ -0,0 +1,14 @@ +package emu.grasscutter.game.managers.EnergyManager; + +public class EnergyDropInfo { + private int ballId; + private int count; + + public int getBallId() { + return this.ballId; + } + + public int getCount() { + return this.count; + } +} diff --git a/src/main/java/emu/grasscutter/game/managers/EnergyManager/EnergyManager.java b/src/main/java/emu/grasscutter/game/managers/EnergyManager/EnergyManager.java new file mode 100644 index 000000000..4e1439e5a --- /dev/null +++ b/src/main/java/emu/grasscutter/game/managers/EnergyManager/EnergyManager.java @@ -0,0 +1,449 @@ +package emu.grasscutter.game.managers.EnergyManager; + +import emu.grasscutter.Grasscutter; +import emu.grasscutter.data.DataLoader; +import emu.grasscutter.data.GameData; +import emu.grasscutter.data.excels.AvatarSkillDepotData; +import emu.grasscutter.data.excels.ItemData; +import emu.grasscutter.data.excels.MonsterData.HpDrops; +import emu.grasscutter.game.avatar.Avatar; +import emu.grasscutter.game.entity.EntityAvatar; +import emu.grasscutter.game.entity.EntityClientGadget; +import emu.grasscutter.game.entity.EntityItem; +import emu.grasscutter.game.entity.EntityMonster; +import emu.grasscutter.game.entity.GameEntity; +import emu.grasscutter.game.inventory.GameItem; +import emu.grasscutter.game.player.Player; +import emu.grasscutter.game.props.ElementType; +import emu.grasscutter.game.props.FightProperty; +import emu.grasscutter.game.props.MonsterType; +import emu.grasscutter.game.props.WeaponType; +import emu.grasscutter.net.proto.AbilityActionGenerateElemBallOuterClass.AbilityActionGenerateElemBall; +import emu.grasscutter.net.proto.AbilityIdentifierOuterClass.AbilityIdentifier; +import emu.grasscutter.net.proto.AbilityInvokeEntryOuterClass.AbilityInvokeEntry; +import emu.grasscutter.net.proto.AttackResultOuterClass.AttackResult; +import emu.grasscutter.net.proto.EvtBeingHitInfoOuterClass.EvtBeingHitInfo; +import emu.grasscutter.net.proto.PropChangeReasonOuterClass.PropChangeReason; +import emu.grasscutter.server.game.GameSession; +import emu.grasscutter.utils.Position; +import it.unimi.dsi.fastutil.ints.Int2IntMap; +import it.unimi.dsi.fastutil.ints.Int2ObjectMap; +import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap; + +import static emu.grasscutter.Configuration.GAME_OPTIONS; + +import java.io.InputStreamReader; +import java.io.Reader; +import java.util.Collection; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.concurrent.ThreadLocalRandom; +import static java.util.Map.entry; + +import com.google.gson.reflect.TypeToken; +import com.google.protobuf.InvalidProtocolBufferException; + +public class EnergyManager { + private final Player player; + private final Map avatarNormalProbabilities; +// energyUsage for each player + private Boolean energyUsage; + private final static Int2ObjectMap> energyDropData = new Int2ObjectOpenHashMap<>(); + private final static Int2ObjectMap> skillParticleGenerationData = new Int2ObjectOpenHashMap<>(); + + public EnergyManager(Player player) { + this.player = player; + this.avatarNormalProbabilities = new HashMap<>(); + this.energyUsage=GAME_OPTIONS.energyUsage; + } + + public Player getPlayer() { + return this.player; + } + + public static void initialize() { + // Read the data we need for monster energy drops. + try (Reader fileReader = new InputStreamReader(DataLoader.load("EnergyDrop.json"))) { + List energyDropList = Grasscutter.getGsonFactory().fromJson(fileReader, TypeToken.getParameterized(Collection.class, EnergyDropEntry.class).getType()); + + for (EnergyDropEntry entry : energyDropList) { + energyDropData.put(entry.getDropId(), entry.getDropList()); + } + + Grasscutter.getLogger().info("Energy drop data successfully loaded."); + } + catch (Exception ex) { + Grasscutter.getLogger().error("Unable to load energy drop data.", ex); + } + + // Read the data for particle generation from skills + try (Reader fileReader = new InputStreamReader(DataLoader.load("SkillParticleGeneration.json"))) { + List skillParticleGenerationList = Grasscutter.getGsonFactory().fromJson(fileReader, TypeToken.getParameterized(Collection.class, SkillParticleGenerationEntry.class).getType()); + + for (SkillParticleGenerationEntry entry : skillParticleGenerationList) { + skillParticleGenerationData.put(entry.getAvatarId(), entry.getAmountList()); + } + + Grasscutter.getLogger().info("Skill particle generation data successfully loaded."); + } + catch (Exception ex) { + Grasscutter.getLogger().error("Unable to load skill particle generation data data.", ex); + } + } + + /********** + Particle creation for elemental skills. + **********/ + private int getBallCountForAvatar(int avatarId) { + // We default to two particles. + int count = 2; + + // If we don't have any data for this avatar, stop. + if (!skillParticleGenerationData.containsKey(avatarId)) { + Grasscutter.getLogger().warn("No particle generation data for avatarId {} found.", avatarId); + } + // If we do have data, roll for how many particles we should generate. + else { + int roll = ThreadLocalRandom.current().nextInt(0, 100); + int percentageStack = 0; + for (SkillParticleGenerationInfo info : skillParticleGenerationData.get(avatarId)) { + int chance = info.getChance(); + percentageStack += chance; + if (roll < percentageStack) { + count = info.getValue(); + break; + } + } + } + + // Done. + return count; + } + + private int getBallIdForElement(ElementType element) { + // If we have no element, we default to an elementless particle. + if (element == null) { + return 2024; + } + + // Otherwise, we determin the particle's ID based on the element. + return switch (element) { + case Fire -> 2017; + case Water -> 2018; + case Grass -> 2019; + case Electric -> 2020; + case Wind -> 2021; + case Ice -> 2022; + case Rock -> 2023; + default -> 2024; + }; + } + + public void handleGenerateElemBall(AbilityInvokeEntry invoke) throws InvalidProtocolBufferException { + // ToDo: + // This is also called when a weapon like Favonius Warbow etc. creates energy through its passive. + // We are not handling this correctly at the moment. + + // Get action info. + AbilityActionGenerateElemBall action = AbilityActionGenerateElemBall.parseFrom(invoke.getAbilityData()); + if (action == null) { + return; + } + + // Default to an elementless particle. + int itemId = 2024; + + // Generate 2 particles by default. + int amount = 2; + + // Try to get the casting avatar from the player's party. + Optional avatarEntity = getCastingAvatarEntityForEnergy(invoke.getEntityId()); + + // Bug: invokes twice sometimes, Ayato, Keqing + // ToDo: deal with press, hold difference. deal with charge(Beidou, Yunjin) + if (avatarEntity.isPresent()) { + Avatar avatar = avatarEntity.get().getAvatar(); + + if (avatar != null) { + int avatarId = avatar.getAvatarId(); + AvatarSkillDepotData skillDepotData = avatar.getSkillDepot(); + + // Determine how many particles we need to create for this avatar. + amount = this.getBallCountForAvatar(avatarId); + + // Determine the avatar's element, and based on that the ID of the + // particles we have to generate. + if (skillDepotData != null) { + ElementType element = skillDepotData.getElementType(); + itemId = getBallIdForElement(element); + } + } + } + + // Generate the particles. + for (int i = 0; i < amount; i++) { + generateElemBall(itemId, new Position(action.getPos()), 1); + } + } + + /********** + Pickup of elemental particles and orbs. + **********/ + public void handlePickupElemBall(GameItem elemBall) { + // Check if the item is indeed an energy particle/orb. + if (elemBall.getItemId() < 2001 ||elemBall.getItemId() > 2024) { + return; + } + + // Determine the base amount of energy given by the particle/orb. + // Particles have a base amount of 1.0, and orbs a base amount of 3.0. + float baseEnergy = (elemBall.getItemId() <= 2008) ? 3.0f : 1.0f; + + // Add energy to every team member. + for (int i = 0; i < this.player.getTeamManager().getActiveTeam().size(); i++) { + EntityAvatar entity = this.player.getTeamManager().getActiveTeam().get(i); + + // On-field vs off-field multiplier. + // The on-field character gets no penalty. + // Off-field characters get a penalty depending on the team size, as follows: + // - 2 character team: 0.8 + // - 3 character team: 0.7 + // - 4 character team: 0.6 + // - etc. + // We set a lower bound of 0.1 here, to avoid gaining no or negative energy. + float offFieldPenalty = + (this.player.getTeamManager().getCurrentCharacterIndex() == i) + ? 1.0f + : 1.0f - this.player.getTeamManager().getActiveTeam().size() * 0.1f; + offFieldPenalty = Math.max(offFieldPenalty, 0.1f); + + // Same element/neutral bonus. + // Same-element characters get a bonus of *3, while different-element characters get no bonus at all. + // For neutral particles/orbs, the multiplier is always *2. + if (entity.getAvatar().getSkillDepot() == null) { + continue; + } + + ElementType avatarElement = entity.getAvatar().getSkillDepot().getElementType(); + ElementType ballElement = switch (elemBall.getItemId()) { + case 2001, 2017 -> ElementType.Fire; + case 2002, 2018 -> ElementType.Water; + case 2003, 2019 -> ElementType.Grass; + case 2004, 2020 -> ElementType.Electric; + case 2005, 2021 -> ElementType.Wind; + case 2006, 2022 -> ElementType.Ice; + case 2007, 2023 -> ElementType.Rock; + default -> null; + }; + + float elementBonus = (ballElement == null) ? 2.0f : (avatarElement == ballElement) ? 3.0f : 1.0f; + + // Add the energy. + entity.addEnergy(baseEnergy * elementBonus * offFieldPenalty * elemBall.getCount(), PropChangeReason.PROP_CHANGE_REASON_ENERGY_BALL); + } + } + + /********** + Energy generation for NAs/CAs. + **********/ + private void generateEnergyForNormalAndCharged(EntityAvatar avatar) { + // This logic is based on the descriptions given in + // https://genshin-impact.fandom.com/wiki/Energy#Energy_Generated_by_Normal_Attacks + // https://library.keqingmains.com/combat-mechanics/energy#auto-attacking + // Those descriptions are lacking in some information, so this implementation most likely + // does not fully replicate the behavior of the official server. Open questions: + // - Does the probability for a character reset after some time? + // - Does the probability for a character reset when switching them out? + // - Does this really count every individual hit separately? + + // Get the avatar's weapon type. + WeaponType weaponType = avatar.getAvatar().getAvatarData().getWeaponType(); + + // Check if we already have probability data for this avatar. If not, insert it. + if (!this.avatarNormalProbabilities.containsKey(avatar)) { + this.avatarNormalProbabilities.put(avatar, weaponType.getEnergyGainInitialProbability()); + } + + // Roll for energy. + int currentProbability = this.avatarNormalProbabilities.get(avatar); + int roll = ThreadLocalRandom.current().nextInt(0, 100); + + // If the player wins the roll, we increase the avatar's energy and reset the probability. + if (roll < currentProbability) { + avatar.addEnergy(1.0f, PropChangeReason.PROP_CHANGE_REASON_ABILITY, true); + this.avatarNormalProbabilities.put(avatar, weaponType.getEnergyGainInitialProbability()); + } + // Otherwise, we increase the probability for the next hit. + else { + this.avatarNormalProbabilities.put(avatar, currentProbability + weaponType.getEnergyGainIncreaseProbability()); + } + } + + public void handleAttackHit(EvtBeingHitInfo hitInfo) { + // Get the attack result. + AttackResult attackRes = hitInfo.getAttackResult(); + + // Make sure the attack was performed by the currently active avatar. If not, we ignore the hit. + Optional attackerEntity = this.getCastingAvatarEntityForEnergy(attackRes.getAttackerId()); + if (attackerEntity.isEmpty() || this.player.getTeamManager().getCurrentAvatarEntity().getId() != attackerEntity.get().getId()) { + return; + } + + // Make sure the target is an actual enemy. + GameEntity targetEntity = this.player.getScene().getEntityById(attackRes.getDefenseId()); + if (!(targetEntity instanceof EntityMonster)) { + return; + } + + EntityMonster targetMonster = (EntityMonster)targetEntity; + MonsterType targetType = targetMonster.getMonsterData().getType(); + if (targetType != MonsterType.MONSTER_ORDINARY && targetType != MonsterType.MONSTER_BOSS) { + return; + } + + // Get the ability that caused this hit. + AbilityIdentifier ability = attackRes.getAbilityIdentifier(); + + // Make sure there is no actual "ability" associated with the hit. For now, this is how we + // identify normal and charged attacks. Note that this is not completely accurate: + // - Many character's charged attacks have an ability associated with them. This means that, + // for now, we don't identify charged attacks reliably. + // - There might also be some cases where we incorrectly identify something as a normal or + // charged attack that is not (Diluc's E?). + // - Catalyst normal attacks have an ability, so we don't handle those for now. + // ToDo: Fix all of that. + if (ability != AbilityIdentifier.getDefaultInstance()) { + return; + } + + // Handle the energy generation. + this.generateEnergyForNormalAndCharged(attackerEntity.get()); + } + + + /********** + Energy logic related to using skills. + **********/ + private void handleBurstCast(Avatar avatar, int skillId) { + // Don't do anything if energy usage is disabled. + if (!GAME_OPTIONS.energyUsage || !this.energyUsage) { + return; + } + + // If the cast skill was a burst, consume energy. + if (avatar.getSkillDepot() != null && skillId == avatar.getSkillDepot().getEnergySkill()) { + avatar.getAsEntity().clearEnergy(PropChangeReason.PROP_CHANGE_REASON_ABILITY); + } + } + + public void handleEvtDoSkillSuccNotify(GameSession session, int skillId, int casterId) { + // Determine the entity that has cast the skill. Cancel if we can't find that avatar. + Optional caster = this.player.getTeamManager().getActiveTeam().stream() + .filter(character -> character.getId() == casterId) + .findFirst(); + + if (caster.isEmpty()) { + return; + } + + Avatar avatar = caster.get().getAvatar(); + + // Handle elemental burst. + this.handleBurstCast(avatar, skillId); + } + + /********** + Monster energy drops. + **********/ + private void generateElemBallDrops(EntityMonster monster, int dropId) { + // Generate all drops specified for the given drop id. + if (!energyDropData.containsKey(dropId)) { + Grasscutter.getLogger().warn("No drop data for dropId {} found.", dropId); + return; + } + + for (EnergyDropInfo info : energyDropData.get(dropId)) { + this.generateElemBall(info.getBallId(), monster.getPosition(), info.getCount()); + } + } + public void handleMonsterEnergyDrop(EntityMonster monster, float hpBeforeDamage, float hpAfterDamage) { + // Make sure this is actually a monster. + // Note that some wildlife also has that type, like boars or birds. + MonsterType type = monster.getMonsterData().getType(); + if (type != MonsterType.MONSTER_ORDINARY && type != MonsterType.MONSTER_BOSS) { + return; + } + + // Calculate the HP tresholds for before and after the damage was taken. + float maxHp = monster.getFightProperty(FightProperty.FIGHT_PROP_MAX_HP); + float thresholdBefore = hpBeforeDamage / maxHp; + float thresholdAfter = hpAfterDamage / maxHp; + + // Determine the thresholds the monster has passed, and generate drops based on that. + for (HpDrops drop : monster.getMonsterData().getHpDrops()) { + if (drop.getDropId() == 0) { + continue; + } + + float threshold = drop.getHpPercent() / 100.0f; + if (threshold < thresholdBefore && threshold >= thresholdAfter) { + generateElemBallDrops(monster, drop.getDropId()); + } + } + + // Handle kill drops. + if (hpAfterDamage <= 0 && monster.getMonsterData().getKillDropId() != 0) { + generateElemBallDrops(monster, monster.getMonsterData().getKillDropId()); + } + } + + /********** + Utility. + **********/ + private void generateElemBall(int ballId, Position position, int count) { + // Generate a particle/orb with the specified parameters. + ItemData itemData = GameData.getItemDataMap().get(ballId); + if (itemData == null) { + return; + } + + EntityItem energyBall = new EntityItem(this.getPlayer().getScene(), this.getPlayer(), itemData, position, count); + this.getPlayer().getScene().addEntity(energyBall); + } + + private Optional getCastingAvatarEntityForEnergy(int invokeEntityId) { + // To determine the avatar that has cast the skill that caused the energy particle to be generated, + // we have to look at the entity that has invoked the ability. This can either be that avatar directly, + // or it can be an `EntityClientGadget`, owned (some way up the owner hierarchy) by the avatar + // that cast the skill. + + // Try to get the invoking entity from the scene. + GameEntity entity = player.getScene().getEntityById(invokeEntityId); + + // Determine the ID of the entity that originally cast this skill. If the scene entity is null, + // or not an `EntityClientGadget`, we assume that we are directly looking at the casting avatar + // (the null case will happen if the avatar was switched out between casting the skill and the + // particle being generated). If the scene entity is an `EntityClientGadget`, we need to find the + // ID of the original owner of that gadget. + int avatarEntityId = + (!(entity instanceof EntityClientGadget)) + ? invokeEntityId + : ((EntityClientGadget)entity).getOriginalOwnerEntityId(); + + // Finally, find the avatar entity in the player's team. + return player.getTeamManager().getActiveTeam() + .stream() + .filter(character -> character.getId() == avatarEntityId) + .findFirst(); + } + + public Boolean getEnergyUsage() { + return energyUsage; + } + + public void setEnergyUsage(Boolean energyUsage) { + this.energyUsage = energyUsage; + } +} \ No newline at end of file diff --git a/src/main/java/emu/grasscutter/game/managers/EnergyManager/SkillParticleGenerationEntry.java b/src/main/java/emu/grasscutter/game/managers/EnergyManager/SkillParticleGenerationEntry.java new file mode 100644 index 000000000..e99f99231 --- /dev/null +++ b/src/main/java/emu/grasscutter/game/managers/EnergyManager/SkillParticleGenerationEntry.java @@ -0,0 +1,16 @@ +package emu.grasscutter.game.managers.EnergyManager; + +import java.util.List; + +public class SkillParticleGenerationEntry { + private int avatarId; + private List amountList; + + public int getAvatarId() { + return this.avatarId; + } + + public List getAmountList() { + return this.amountList; + } +} diff --git a/src/main/java/emu/grasscutter/game/managers/EnergyManager/SkillParticleGenerationInfo.java b/src/main/java/emu/grasscutter/game/managers/EnergyManager/SkillParticleGenerationInfo.java new file mode 100644 index 000000000..a461b1035 --- /dev/null +++ b/src/main/java/emu/grasscutter/game/managers/EnergyManager/SkillParticleGenerationInfo.java @@ -0,0 +1,14 @@ +package emu.grasscutter.game.managers.EnergyManager; + +public class SkillParticleGenerationInfo { + private int value; + private int chance; + + public int getValue() { + return this.value; + } + + public int getChance() { + return this.chance; + } +} diff --git a/src/main/java/emu/grasscutter/game/managers/InsectCaptureManager.java b/src/main/java/emu/grasscutter/game/managers/InsectCaptureManager.java new file mode 100644 index 000000000..9fe5623b1 --- /dev/null +++ b/src/main/java/emu/grasscutter/game/managers/InsectCaptureManager.java @@ -0,0 +1,44 @@ +package emu.grasscutter.game.managers; + +import emu.grasscutter.Grasscutter; +import emu.grasscutter.data.GameData; +import emu.grasscutter.data.excels.EnvAnimalGatherConfigData; +import emu.grasscutter.data.excels.ItemData; +import emu.grasscutter.game.entity.EntityMonster; +import emu.grasscutter.game.entity.EntityVehicle; +import emu.grasscutter.game.entity.GameEntity; +import emu.grasscutter.game.inventory.GameItem; +import emu.grasscutter.game.player.Player; +import emu.grasscutter.game.props.ActionReason; +import emu.grasscutter.net.proto.VisionTypeOuterClass; + +public record InsectCaptureManager(Player player) { + public void arrestSmallCreature(GameEntity entity) { + //System.out.println("arrestSmallCreature!"); + EnvAnimalGatherConfigData gather; + int thingId; + if (entity instanceof EntityMonster monster) { + thingId = monster.getMonsterData().getId(); + gather = GameData.getEnvAnimalGatherConfigDataMap().get(thingId); + } else if (entity instanceof EntityVehicle gadget) { + thingId = gadget.getGadgetId(); + gather = GameData.getEnvAnimalGatherConfigDataMap().get(thingId); + } else { + return; + } + if (gather == null) { + Grasscutter.getLogger().warn("monster/gather(id={}) couldn't be caught.", thingId); + return; + } + String type = gather.getEntityType(); + if ((type.equals("Monster") && entity instanceof EntityMonster) || (type.equals("Gadget") && entity instanceof EntityVehicle)) { + EnvAnimalGatherConfigData.GatherItem gatherItem = gather.gatherItem(); + ItemData data = GameData.getItemDataMap().get(gatherItem.getId()); + GameItem item = new GameItem(data, gatherItem.getCount()); + player.getInventory().addItem(item, ActionReason.SubfieldDrop); + entity.getScene().removeEntity(entity, VisionTypeOuterClass.VisionType.VISION_TYPE_REMOVE); + } else { + Grasscutter.getLogger().warn("monster/gather(id={}) has a wrong type.", thingId); + } + } +} diff --git a/src/main/java/emu/grasscutter/game/managers/InventoryManager.java b/src/main/java/emu/grasscutter/game/managers/InventoryManager.java index f3b9c0293..3d50873bf 100644 --- a/src/main/java/emu/grasscutter/game/managers/InventoryManager.java +++ b/src/main/java/emu/grasscutter/game/managers/InventoryManager.java @@ -1,22 +1,24 @@ package emu.grasscutter.game.managers; import java.util.ArrayList; +import java.util.Arrays; import java.util.List; import java.util.Map; import java.util.stream.Collectors; +import emu.grasscutter.Grasscutter; import emu.grasscutter.data.GameData; +import emu.grasscutter.data.binout.OpenConfigEntry; +import emu.grasscutter.data.binout.OpenConfigEntry.SkillPointModifier; import emu.grasscutter.data.common.ItemParamData; -import emu.grasscutter.data.custom.OpenConfigEntry; -import emu.grasscutter.data.custom.OpenConfigEntry.SkillPointModifier; -import emu.grasscutter.data.def.AvatarPromoteData; -import emu.grasscutter.data.def.AvatarSkillData; -import emu.grasscutter.data.def.AvatarSkillDepotData; -import emu.grasscutter.data.def.ItemData; -import emu.grasscutter.data.def.WeaponPromoteData; -import emu.grasscutter.data.def.AvatarSkillDepotData.InherentProudSkillOpens; -import emu.grasscutter.data.def.AvatarTalentData; -import emu.grasscutter.data.def.ProudSkillData; +import emu.grasscutter.data.excels.AvatarPromoteData; +import emu.grasscutter.data.excels.AvatarSkillData; +import emu.grasscutter.data.excels.AvatarSkillDepotData; +import emu.grasscutter.data.excels.AvatarTalentData; +import emu.grasscutter.data.excels.ItemData; +import emu.grasscutter.data.excels.ProudSkillData; +import emu.grasscutter.data.excels.WeaponPromoteData; +import emu.grasscutter.data.excels.AvatarSkillDepotData.InherentProudSkillOpens; import emu.grasscutter.game.avatar.Avatar; import emu.grasscutter.game.inventory.GameItem; import emu.grasscutter.game.inventory.ItemType; @@ -27,6 +29,7 @@ import emu.grasscutter.game.shop.ShopChestBatchUseTable; import emu.grasscutter.game.shop.ShopChestTable; import emu.grasscutter.net.proto.ItemParamOuterClass.ItemParam; import emu.grasscutter.net.proto.MaterialInfoOuterClass.MaterialInfo; +import emu.grasscutter.server.packet.send.PacketForgeFormulaDataNotify; import emu.grasscutter.server.game.GameServer; import emu.grasscutter.server.packet.send.*; import emu.grasscutter.utils.Utils; @@ -38,6 +41,8 @@ public class InventoryManager { private final static int RELIC_MATERIAL_1 = 105002; // Sanctifying Unction private final static int RELIC_MATERIAL_2 = 105003; // Sanctifying Essence + private final static int RELIC_MATERIAL_EXP_1 = 2500; // Sanctifying Unction + private final static int RELIC_MATERIAL_EXP_2 = 10000; // Sanctifying Essence private final static int WEAPON_ORE_1 = 104011; // Enhancement Ore private final static int WEAPON_ORE_2 = 104012; // Fine Enhancement Ore @@ -85,6 +90,7 @@ public class InventoryManager { int moraCost = 0; int expGain = 0; + List foodRelics = new ArrayList(); for (long guid : foodRelicList) { // Add to delete queue GameItem food = player.getInventory().getItemByGuid(guid); @@ -96,23 +102,21 @@ public class InventoryManager { expGain += food.getItemData().getBaseConvExp(); // Feeding artifact with exp already if (food.getTotalExp() > 0) { - expGain += (int) Math.floor(food.getTotalExp() * .8f); + expGain += (food.getTotalExp() * 4) / 5; } + foodRelics.add(food); } + List payList = new ArrayList(); for (ItemParam itemParam : list) { - GameItem food = player.getInventory().getInventoryTab(ItemType.ITEM_MATERIAL).getItemById(itemParam.getItemId()); - if (food == null || food.getItemData().getMaterialType() != MaterialType.MATERIAL_RELIQUARY_MATERIAL) { - continue; - } - int amount = Math.min(food.getCount(), itemParam.getCount()); - int gain = 0; - if (food.getItemId() == RELIC_MATERIAL_2) { - gain = 10000 * amount; - } else if (food.getItemId() == RELIC_MATERIAL_1) { - gain = 2500 * amount; - } + int amount = itemParam.getCount(); // Previously this capped to inventory amount, but rejecting the payment makes more sense for an invalid order + int gain = amount * switch(itemParam.getItemId()) { + case RELIC_MATERIAL_1 -> RELIC_MATERIAL_EXP_1; + case RELIC_MATERIAL_2 -> RELIC_MATERIAL_EXP_2; + default -> 0; + }; expGain += gain; moraCost += gain; + payList.add(new ItemParamData(itemParam.getItemId(), itemParam.getCount())); } // Make sure exp gain is valid @@ -120,28 +124,14 @@ public class InventoryManager { return; } - // Check mora - if (player.getMora() < moraCost) { + // Confirm payment of materials and mora (assume food relics are payable afterwards) + payList.add(new ItemParamData(202, moraCost)); + if (!player.getInventory().payItems(payList.toArray(new ItemParamData[0]))) { return; } - player.setMora(player.getMora() - moraCost); - // Consume food items - for (long guid : foodRelicList) { - GameItem food = player.getInventory().getItemByGuid(guid); - if (food == null || !food.isDestroyable()) { - continue; - } - player.getInventory().removeItem(food); - } - for (ItemParam itemParam : list) { - GameItem food = player.getInventory().getInventoryTab(ItemType.ITEM_MATERIAL).getItemById(itemParam.getItemId()); - if (food == null || food.getItemData().getMaterialType() != MaterialType.MATERIAL_RELIQUARY_MATERIAL) { - continue; - } - int amount = Math.min(food.getCount(), itemParam.getCount()); - player.getInventory().removeItem(food, amount); - } + // Consume food relics + player.getInventory().removeItems(foodRelics); // Implement random rate boost int rate = 1; @@ -231,22 +221,16 @@ public class InventoryManager { } expGain += food.getItemData().getWeaponBaseExp(); if (food.getTotalExp() > 0) { - expGain += (int) Math.floor(food.getTotalExp() * .8f); + expGain += (food.getTotalExp() * 4) / 5; } } for (ItemParam param : itemParamList) { - GameItem food = player.getInventory().getInventoryTab(ItemType.ITEM_MATERIAL).getItemById(param.getItemId()); - if (food == null || food.getItemData().getMaterialType() != MaterialType.MATERIAL_WEAPON_EXP_STONE) { - continue; - } - int amount = Math.min(param.getCount(), food.getCount()); - if (food.getItemId() == WEAPON_ORE_3) { - expGain += 10000 * amount; - } else if (food.getItemId() == WEAPON_ORE_2) { - expGain += 2000 * amount; - } else if (food.getItemId() == WEAPON_ORE_1) { - expGain += 400 * amount; - } + expGain += param.getCount() * switch(param.getItemId()) { + case WEAPON_ORE_1 -> WEAPON_ORE_EXP_1; + case WEAPON_ORE_2 -> WEAPON_ORE_EXP_2; + case WEAPON_ORE_3 -> WEAPON_ORE_EXP_3; + default -> 0; + }; } // Try @@ -288,65 +272,45 @@ public class InventoryManager { } // Get exp gain - int expGain = 0, moraCost = 0; - + int expGain = 0, expGainFree = 0; + List foodWeapons = new ArrayList(); for (long guid : foodWeaponGuidList) { GameItem food = player.getInventory().getItemByGuid(guid); if (food == null || !food.isDestroyable()) { continue; } expGain += food.getItemData().getWeaponBaseExp(); - moraCost += (int) Math.floor(food.getItemData().getWeaponBaseExp() * .1f); if (food.getTotalExp() > 0) { - expGain += (int) Math.floor(food.getTotalExp() * .8f); + expGainFree += (food.getTotalExp() * 4) / 5; // No tax :D } + foodWeapons.add(food); } + List payList = new ArrayList(); for (ItemParam param : itemParamList) { - GameItem food = player.getInventory().getInventoryTab(ItemType.ITEM_MATERIAL).getItemById(param.getItemId()); - if (food == null || food.getItemData().getMaterialType() != MaterialType.MATERIAL_WEAPON_EXP_STONE) { - continue; - } - int amount = Math.min(param.getCount(), food.getCount()); - int gain = 0; - if (food.getItemId() == WEAPON_ORE_3) { - gain = 10000 * amount; - } else if (food.getItemId() == WEAPON_ORE_2) { - gain = 2000 * amount; - } else if (food.getItemId() == WEAPON_ORE_1) { - gain = 400 * amount; - } + int amount = param.getCount(); // Previously this capped to inventory amount, but rejecting the payment makes more sense for an invalid order + int gain = amount * switch(param.getItemId()) { + case WEAPON_ORE_1 -> WEAPON_ORE_EXP_1; + case WEAPON_ORE_2 -> WEAPON_ORE_EXP_2; + case WEAPON_ORE_3 -> WEAPON_ORE_EXP_3; + default -> 0; + }; expGain += gain; - moraCost += (int) Math.floor(gain * .1f); + payList.add(new ItemParamData(param.getItemId(), amount)); } // Make sure exp gain is valid + int moraCost = expGain / 10; + expGain += expGainFree; if (expGain <= 0) { return; } - - // Mora check - if (player.getMora() >= moraCost) { - player.setMora(player.getMora() - moraCost); - } else { + + // Confirm payment of materials and mora (assume food weapons are payable afterwards) + payList.add(new ItemParamData(202, moraCost)); + if (!player.getInventory().payItems(payList.toArray(new ItemParamData[0]))) { return; } - - // Consume weapon/items used to feed - for (long guid : foodWeaponGuidList) { - GameItem food = player.getInventory().getItemByGuid(guid); - if (food == null || !food.isDestroyable()) { - continue; - } - player.getInventory().removeItem(food); - } - for (ItemParam param : itemParamList) { - GameItem food = player.getInventory().getInventoryTab(ItemType.ITEM_MATERIAL).getItemById(param.getItemId()); - if (food == null || food.getItemData().getMaterialType() != MaterialType.MATERIAL_WEAPON_EXP_STONE) { - continue; - } - int amount = Math.min(param.getCount(), food.getCount()); - player.getInventory().removeItem(food, amount); - } + player.getInventory().removeItems(foodWeapons); // Level up int maxLevel = promoteData.getUnlockMaxLevel(); @@ -393,7 +357,7 @@ public class InventoryManager { player.sendPacket(new PacketWeaponUpgradeRsp(weapon, oldLevel, leftovers)); } - private List getLeftoverOres(float leftover) { + private List getLeftoverOres(int leftover) { List leftoverOreList = new ArrayList<>(3); if (leftover < WEAPON_ORE_EXP_1) { @@ -401,11 +365,11 @@ public class InventoryManager { } // Get leftovers - int ore3 = (int) Math.floor(leftover / WEAPON_ORE_EXP_3); + int ore3 = leftover / WEAPON_ORE_EXP_3; leftover = leftover % WEAPON_ORE_EXP_3; - int ore2 = (int) Math.floor(leftover / WEAPON_ORE_EXP_2); + int ore2 = leftover / WEAPON_ORE_EXP_2; leftover = leftover % WEAPON_ORE_EXP_2; - int ore1 = (int) Math.floor(leftover / WEAPON_ORE_EXP_1); + int ore1 = leftover / WEAPON_ORE_EXP_1; if (ore3 > 0) { leftoverOreList.add(ItemParam.newBuilder().setItemId(WEAPON_ORE_3).setCount(ore3).build()); @@ -496,27 +460,16 @@ public class InventoryManager { return; } - // Make sure player has promote items - for (ItemParamData cost : nextPromoteData.getCostItems()) { - GameItem feedItem = player.getInventory().getInventoryTab(ItemType.ITEM_MATERIAL).getItemById(cost.getId()); - if (feedItem == null || feedItem.getCount() < cost.getCount()) { - return; - } + // Pay materials and mora if possible + ItemParamData[] costs = nextPromoteData.getCostItems(); // Can this be null? + if (nextPromoteData.getCoinCost() > 0) { + costs = Arrays.copyOf(costs, costs.length + 1); + costs[costs.length-1] = new ItemParamData(202, nextPromoteData.getCoinCost()); } - - // Mora check - if (player.getMora() >= nextPromoteData.getCoinCost()) { - player.setMora(player.getMora() - nextPromoteData.getCoinCost()); - } else { + if (!player.getInventory().payItems(costs)) { return; } - // Consume promote filler items - for (ItemParamData cost : nextPromoteData.getCostItems()) { - GameItem feedItem = player.getInventory().getInventoryTab(ItemType.ITEM_MATERIAL).getItemById(cost.getId()); - player.getInventory().removeItem(feedItem, cost.getCount()); - } - int oldPromoteLevel = weapon.getPromoteLevel(); weapon.setPromoteLevel(nextPromoteLevel); weapon.save(); @@ -552,27 +505,16 @@ public class InventoryManager { return; } - // Make sure player has cost items - for (ItemParamData cost : nextPromoteData.getCostItems()) { - GameItem feedItem = player.getInventory().getInventoryTab(ItemType.ITEM_MATERIAL).getItemById(cost.getId()); - if (feedItem == null || feedItem.getCount() < cost.getCount()) { - return; - } + // Pay materials and mora if possible + ItemParamData[] costs = nextPromoteData.getCostItems(); // Can this be null? + if (nextPromoteData.getCoinCost() > 0) { + costs = Arrays.copyOf(costs, costs.length + 1); + costs[costs.length-1] = new ItemParamData(202, nextPromoteData.getCoinCost()); } - - // Mora check - if (player.getMora() >= nextPromoteData.getCoinCost()) { - player.setMora(player.getMora() - nextPromoteData.getCoinCost()); - } else { + if (!player.getInventory().payItems(costs)) { return; } - // Consume promote filler items - for (ItemParamData cost : nextPromoteData.getCostItems()) { - GameItem feedItem = player.getInventory().getInventoryTab(ItemType.ITEM_MATERIAL).getItemById(cost.getId()); - player.getInventory().removeItem(feedItem, cost.getCount()); - } - // Update promote level avatar.setPromoteLevel(nextPromoteLevel); @@ -616,34 +558,25 @@ public class InventoryManager { return; } - GameItem feedItem = player.getInventory().getInventoryTab(ItemType.ITEM_MATERIAL).getItemById(itemId); - - if (feedItem == null || feedItem.getItemData().getMaterialType() != MaterialType.MATERIAL_EXP_FRUIT || feedItem.getCount() < count) { - return; - } - // Calc exp - int expGain = 0, moraCost = 0; + int expGain = switch(itemId) { + case AVATAR_BOOK_1 -> AVATAR_BOOK_EXP_1 * count; + case AVATAR_BOOK_2 -> AVATAR_BOOK_EXP_2 * count; + case AVATAR_BOOK_3 -> AVATAR_BOOK_EXP_3 * count; + default -> 0; + }; - // TODO clean up - if (itemId == AVATAR_BOOK_3) { - expGain = AVATAR_BOOK_EXP_3 * count; - } else if (itemId == AVATAR_BOOK_2) { - expGain = AVATAR_BOOK_EXP_2 * count; - } else if (itemId == AVATAR_BOOK_1) { - expGain = AVATAR_BOOK_EXP_1 * count; - } - moraCost = (int) Math.floor(expGain * .2f); - - // Mora check - if (player.getMora() >= moraCost) { - player.setMora(player.getMora() - moraCost); - } else { + // Sanity check + if (expGain <= 0) { + return; + } + + // Payment check + int moraCost = expGain / 5; + ItemParamData[] costItems = new ItemParamData[] {new ItemParamData(itemId, count), new ItemParamData(202, moraCost)}; + if (!player.getInventory().payItems(costItems)) { return; } - - // Consume items - player.getInventory().removeItem(feedItem, count); // Level up upgradeAvatar(player, avatar, promoteData, expGain); @@ -764,33 +697,15 @@ public class InventoryManager { return; } - // Make sure player has cost items - for (ItemParamData cost : proudSkill.getCostItems()) { - if (cost.getId() == 0) { - continue; - } - GameItem feedItem = player.getInventory().getInventoryTab(ItemType.ITEM_MATERIAL).getItemById(cost.getId()); - if (feedItem == null || feedItem.getCount() < cost.getCount()) { - return; - } + // Pay materials and mora if possible + List costs = new ArrayList(proudSkill.getCostItems()); // Can this be null? + if (proudSkill.getCoinCost() > 0) { + costs.add(new ItemParamData(202, proudSkill.getCoinCost())); } - - // Mora check - if (player.getMora() >= proudSkill.getCoinCost()) { - player.setMora(player.getMora() - proudSkill.getCoinCost()); - } else { + if (!player.getInventory().payItems(costs.toArray(new ItemParamData[0]))) { return; } - // Consume promote filler items - for (ItemParamData cost : proudSkill.getCostItems()) { - if (cost.getId() == 0) { - continue; - } - GameItem feedItem = player.getInventory().getInventoryTab(ItemType.ITEM_MATERIAL).getItemById(cost.getId()); - player.getInventory().removeItem(feedItem, cost.getCount()); - } - // Upgrade skill avatar.getSkillLevelMap().put(skillId, nextLevel); avatar.save(); @@ -822,14 +737,11 @@ public class InventoryManager { return; } - GameItem costItem = player.getInventory().getInventoryTab(ItemType.ITEM_MATERIAL).getItemById(talentData.getMainCostItemId()); - if (costItem == null || costItem.getCount() < talentData.getMainCostItemCount()) { + // Pay constellation item if possible + if (!player.getInventory().payItem(talentData.getMainCostItemId(), 1)) { return; } - // Consume item - player.getInventory().removeItem(costItem, talentData.getMainCostItemCount()); - // Apply + recalc avatar.getTalentIdList().add(talentData.getId()); avatar.setCoreProudSkillLevel(currentTalentLevel + 1); @@ -932,6 +844,25 @@ public class InventoryManager { used = player.getTeamManager().healAvatar(target, SatiationParams[0], SatiationParams[1]) ? 1 : 0; } break; + case MATERIAL_CONSUME: + // Make sure we have usage data for this material. + if (useItem.getItemData().getItemUse() == null) { + break; + } + + // Handle forging blueprints. + if (useItem.getItemData().getItemUse().get(0).getUseOp().equals("ITEM_USE_UNLOCK_FORGE")) { + // Determine the forging item we should unlock. + int forgeId = Integer.parseInt(useItem.getItemData().getItemUse().get(0).getUseParam().get(0)); + + // Tell the client that this blueprint is now unlocked and add the unlocked item to the player. + player.sendPacket(new PacketForgeFormulaDataNotify(forgeId)); + player.getUnlockedForgingBlueprints().add(forgeId); + + // Use up the blueprint item. + used = 1; + } + break; case MATERIAL_CHEST: List shopChestTableList = player.getServer().getShopManager().getShopChestData(); List rewardItemList = new ArrayList<>(); diff --git a/src/main/java/emu/grasscutter/game/managers/MapMarkManager/MapMark.java b/src/main/java/emu/grasscutter/game/managers/MapMarkManager/MapMark.java index 2dcbf7972..e49d0c802 100644 --- a/src/main/java/emu/grasscutter/game/managers/MapMarkManager/MapMark.java +++ b/src/main/java/emu/grasscutter/game/managers/MapMarkManager/MapMark.java @@ -1,9 +1,9 @@ package emu.grasscutter.game.managers.MapMarkManager; import dev.morphia.annotations.Entity; -import emu.grasscutter.net.proto.MapMarkFromTypeOuterClass; -import emu.grasscutter.net.proto.MapMarkPointOuterClass; -import emu.grasscutter.net.proto.MapMarkPointTypeOuterClass; +import emu.grasscutter.net.proto.MapMarkFromTypeOuterClass.MapMarkFromType; +import emu.grasscutter.net.proto.MapMarkPointOuterClass.MapMarkPoint; +import emu.grasscutter.net.proto.MapMarkPointTypeOuterClass.MapMarkPointType; import emu.grasscutter.utils.Position; @Entity @@ -11,63 +11,56 @@ public class MapMark { private int sceneId; private String name; private Position position; - private MapMarkPointTypeOuterClass.MapMarkPointType pointType; - private int monsterId = 0; - private MapMarkFromTypeOuterClass.MapMarkFromType fromType; - private int questId = 7; - - public MapMark(Position position, MapMarkPointTypeOuterClass.MapMarkPointType type) { - this.position = position; + private MapMarkPointType mapMarkPointType; + private int monsterId; + private MapMarkFromType mapMarkFromType; + private int questId; + + @Deprecated // Morhpia + public MapMark() { + this.mapMarkPointType = MapMarkPointType.MAP_MARK_POINT_TYPE_MONSTER; + this.mapMarkFromType = MapMarkFromType.MAP_MARK_FROM_TYPE_MONSTER; } - public MapMark(MapMarkPointOuterClass.MapMarkPoint mapMarkPoint) { + public MapMark(MapMarkPoint mapMarkPoint) { this.sceneId = mapMarkPoint.getSceneId(); this.name = mapMarkPoint.getName(); - this.position = new Position(mapMarkPoint.getPos().getX(), mapMarkPoint.getPos().getY(), mapMarkPoint.getPos().getZ()); - this.pointType = mapMarkPoint.getPointType(); + this.position = new Position( + mapMarkPoint.getPos().getX(), + mapMarkPoint.getPos().getY(), + mapMarkPoint.getPos().getZ() + ); + this.mapMarkPointType = mapMarkPoint.getPointType(); this.monsterId = mapMarkPoint.getMonsterId(); - this.fromType = mapMarkPoint.getFromType(); + this.mapMarkFromType = mapMarkPoint.getFromType(); this.questId = mapMarkPoint.getQuestId(); } public int getSceneId() { return this.sceneId; } - + public String getName() { return this.name; } - + public Position getPosition() { return this.position; } - - public MapMarkPointTypeOuterClass.MapMarkPointType getMapMarkPointType() { - return this.pointType; + + public MapMarkPointType getMapMarkPointType() { + return this.mapMarkPointType; } - - public void setMapMarkPointType(MapMarkPointTypeOuterClass.MapMarkPointType pointType) { - this.pointType = pointType; - } - + public int getMonsterId() { return this.monsterId; } - - public void setMonsterId(int monsterId) { - this.monsterId = monsterId; + + public MapMarkFromType getMapMarkFromType() { + return this.mapMarkFromType; } - - public MapMarkFromTypeOuterClass.MapMarkFromType getMapMarkFromType() { - return this.fromType; - } - + public int getQuestId() { return this.questId; } - - public void setQuestId(int questId) { - this.questId = questId; - } - -} +} \ No newline at end of file diff --git a/src/main/java/emu/grasscutter/game/managers/MapMarkManager/MapMarksManager.java b/src/main/java/emu/grasscutter/game/managers/MapMarkManager/MapMarksManager.java index 5249f59d9..949786d12 100644 --- a/src/main/java/emu/grasscutter/game/managers/MapMarkManager/MapMarksManager.java +++ b/src/main/java/emu/grasscutter/game/managers/MapMarkManager/MapMarksManager.java @@ -1,61 +1,90 @@ package emu.grasscutter.game.managers.MapMarkManager; -import dev.morphia.annotations.Entity; +import emu.grasscutter.game.player.Player; +import emu.grasscutter.net.proto.MapMarkPointTypeOuterClass.MapMarkPointType; +import emu.grasscutter.net.proto.MarkMapReqOuterClass.MarkMapReq; +import emu.grasscutter.net.proto.MarkMapReqOuterClass.MarkMapReq.Operation; +import emu.grasscutter.server.packet.send.PacketMarkMapRsp; +import emu.grasscutter.server.packet.send.PacketSceneEntityAppearNotify; import emu.grasscutter.utils.Position; + import java.util.HashMap; -@Entity public class MapMarksManager { - - static final int mapMarkMaxCount = 150; + public static final int mapMarkMaxCount = 150; private HashMap mapMarks; + private final Player player; - public MapMarksManager() { - mapMarks = new HashMap(); + public MapMarksManager(Player player) { + this.player = player; + this.mapMarks = player.getMapMarks(); + if (this.mapMarks == null) { this.mapMarks = new HashMap<>(); } } - public MapMarksManager(HashMap mapMarks) { - this.mapMarks = mapMarks; - } - - public HashMap getAllMapMarks() { - return mapMarks; - } - - public MapMark getMapMark(Position position) { - String key = getMapMarkKey(position); - if (mapMarks.containsKey(key)) { - return mapMarks.get(key); - } else { - return null; + public void handleMapMarkReq(MarkMapReq req) { + Operation op = req.getOp(); + switch (op) { + case OPERATION_ADD -> { + MapMark createMark = new MapMark(req.getMark()); + // keep teleporting functionality on fishhook mark. + if (createMark.getMapMarkPointType() == MapMarkPointType.MAP_MARK_POINT_TYPE_FISH_POOL) { + teleport(player, createMark); + return; + } + addMapMark(createMark); + } + case OPERATION_MOD -> { + MapMark oldMark = new MapMark(req.getOld()); + removeMapMark(oldMark.getPosition()); + MapMark newMark = new MapMark(req.getMark()); + addMapMark(newMark); + } + case OPERATION_DEL -> { + MapMark deleteMark = new MapMark(req.getMark()); + removeMapMark(deleteMark.getPosition()); + } } + if (op != Operation.OPERATION_GET) { + saveMapMarks(); + } + player.getSession().send(new PacketMarkMapRsp(getMapMarks())); + } + + public HashMap getMapMarks() { + return mapMarks; } public String getMapMarkKey(Position position) { return "x" + (int)position.getX()+ "z" + (int)position.getZ(); } - public boolean removeMapMark(Position position) { - String key = getMapMarkKey(position); - if (mapMarks.containsKey(key)) { - mapMarks.remove(key); - return true; - } - return false; + public void removeMapMark(Position position) { + mapMarks.remove(getMapMarkKey(position)); } - public boolean addMapMark(MapMark mapMark) { + public void addMapMark(MapMark mapMark) { if (mapMarks.size() < mapMarkMaxCount) { - if (!mapMarks.containsKey(mapMark.getPosition())) { - mapMarks.put(getMapMarkKey(mapMark.getPosition()), mapMark); - return true; - } + mapMarks.put(getMapMarkKey(mapMark.getPosition()), mapMark); } - return false; } - public void setMapMarks(HashMap mapMarks) { - this.mapMarks = mapMarks; + private void saveMapMarks() { + player.setMapMarks(mapMarks); + player.save(); } + private void teleport(Player player, MapMark mapMark) { + float y; + try { + y = (float)Integer.parseInt(mapMark.getName()); + } catch (Exception e) { + y = 300; + } + Position pos = mapMark.getPosition(); + player.getPos().set(pos.getX(), y, pos.getZ()); + if (mapMark.getSceneId() != player.getSceneId()) { + player.getWorld().transferPlayerToScene(player, mapMark.getSceneId(), player.getPos()); + } + player.getScene().broadcastPacket(new PacketSceneEntityAppearNotify(player)); + } } diff --git a/src/main/java/emu/grasscutter/game/managers/MovementManager/MovementManager.java b/src/main/java/emu/grasscutter/game/managers/MovementManager/MovementManager.java deleted file mode 100644 index 23b45903a..000000000 --- a/src/main/java/emu/grasscutter/game/managers/MovementManager/MovementManager.java +++ /dev/null @@ -1,442 +0,0 @@ -package emu.grasscutter.game.managers.MovementManager; - -import emu.grasscutter.Grasscutter; -import emu.grasscutter.game.entity.EntityAvatar; -import emu.grasscutter.game.entity.GameEntity; -import emu.grasscutter.game.player.Player; -import emu.grasscutter.game.props.FightProperty; -import emu.grasscutter.game.props.LifeState; -import emu.grasscutter.game.props.PlayerProperty; -import emu.grasscutter.net.proto.EntityMoveInfoOuterClass; -import emu.grasscutter.net.proto.MotionInfoOuterClass.MotionInfo; -import emu.grasscutter.net.proto.MotionStateOuterClass.MotionState; -import emu.grasscutter.net.proto.PlayerDieTypeOuterClass.PlayerDieType; -import emu.grasscutter.net.proto.VectorOuterClass; -import emu.grasscutter.server.game.GameSession; -import emu.grasscutter.server.packet.send.*; -import emu.grasscutter.utils.Position; -import org.jetbrains.annotations.NotNull; - -import java.lang.Math; -import java.util.*; - -public class MovementManager { - - public HashMap> MotionStatesCategorized = new HashMap<>(); - - private enum ConsumptionType { - None(0), - - // consume - CLIMB_START(-500), - CLIMBING(-150), - CLIMB_JUMP(-2500), - DASH(-1800), - SPRINT(-360), - FLY(-60), - SWIM_DASH_START(-200), - SWIM_DASH(-200), - SWIMMING(-80), - FIGHT(0), - - // restore - STANDBY(500), - RUN(500), - WALK(500), - STANDBY_MOVE(500), - POWERED_FLY(500); - - public final int amount; - ConsumptionType(int amount) { - this.amount = amount; - } - } - - private class Consumption { - public ConsumptionType consumptionType; - public int amount; - public Consumption(ConsumptionType ct, int a) { - consumptionType = ct; - amount = a; - } - public Consumption(ConsumptionType ct) { - this(ct, ct.amount); - } - } - - private MotionState previousState = MotionState.MOTION_STANDBY; - private MotionState currentState = MotionState.MOTION_STANDBY; - private Position previousCoordinates = new Position(0, 0, 0); - private Position currentCoordinates = new Position(0, 0, 0); - - private final Player player; - - private float landSpeed = 0; - private long landTimeMillisecond = 0; - private Timer movementManagerTickTimer; - private GameSession cachedSession = null; - private GameEntity cachedEntity = null; - private int staminaRecoverDelay = 0; - private int skillCaster = 0; - private int skillCasting = 0; - - public MovementManager(Player player) { - previousCoordinates.add(new Position(0,0,0)); - this.player = player; - - MotionStatesCategorized.put("SWIM", new HashSet<>(Arrays.asList( - MotionState.MOTION_SWIM_MOVE, - MotionState.MOTION_SWIM_IDLE, - MotionState.MOTION_SWIM_DASH, - MotionState.MOTION_SWIM_JUMP - ))); - - MotionStatesCategorized.put("STANDBY", new HashSet<>(Arrays.asList( - MotionState.MOTION_STANDBY, - MotionState.MOTION_STANDBY_MOVE, - MotionState.MOTION_DANGER_STANDBY, - MotionState.MOTION_DANGER_STANDBY_MOVE, - MotionState.MOTION_LADDER_TO_STANDBY, - MotionState.MOTION_JUMP_UP_WALL_FOR_STANDBY - ))); - - MotionStatesCategorized.put("CLIMB", new HashSet<>(Arrays.asList( - MotionState.MOTION_CLIMB, - MotionState.MOTION_CLIMB_JUMP, - MotionState.MOTION_STANDBY_TO_CLIMB, - MotionState.MOTION_LADDER_IDLE, - MotionState.MOTION_LADDER_MOVE, - MotionState.MOTION_LADDER_SLIP, - MotionState.MOTION_STANDBY_TO_LADDER - ))); - - MotionStatesCategorized.put("FLY", new HashSet<>(Arrays.asList( - MotionState.MOTION_FLY, - MotionState.MOTION_FLY_IDLE, - MotionState.MOTION_FLY_SLOW, - MotionState.MOTION_FLY_FAST, - MotionState.MOTION_POWERED_FLY - ))); - - MotionStatesCategorized.put("RUN", new HashSet<>(Arrays.asList( - MotionState.MOTION_DASH, - MotionState.MOTION_DANGER_DASH, - MotionState.MOTION_DASH_BEFORE_SHAKE, - MotionState.MOTION_RUN, - MotionState.MOTION_DANGER_RUN, - MotionState.MOTION_WALK, - MotionState.MOTION_DANGER_WALK - ))); - - MotionStatesCategorized.put("FIGHT", new HashSet<>(Arrays.asList( - MotionState.MOTION_FIGHT - ))); - - - } - - public void handle(GameSession session, EntityMoveInfoOuterClass.EntityMoveInfo moveInfo, GameEntity entity) { - if (movementManagerTickTimer == null) { - movementManagerTickTimer = new Timer(); - movementManagerTickTimer.scheduleAtFixedRate(new MotionManagerTick(), 0, 200); - } - // cache info for later use in tick - cachedSession = session; - cachedEntity = entity; - - MotionInfo motionInfo = moveInfo.getMotionInfo(); - moveEntity(entity, moveInfo); - VectorOuterClass.Vector posVector = motionInfo.getPos(); - Position newPos = new Position(posVector.getX(), - posVector.getY(), posVector.getZ());; - if (newPos.getX() != 0 && newPos.getY() != 0 && newPos.getZ() != 0) { - currentCoordinates = newPos; - } - currentState = motionInfo.getState(); - Grasscutter.getLogger().debug("" + currentState + "\t" + (moveInfo.getIsReliable() ? "reliable" : "")); - handleFallOnGround(motionInfo); - } - - public void resetTimer() { - Grasscutter.getLogger().debug("MovementManager ticker stopped"); - movementManagerTickTimer.cancel(); - movementManagerTickTimer = null; - } - - private void moveEntity(GameEntity entity, EntityMoveInfoOuterClass.EntityMoveInfo moveInfo) { - entity.getPosition().set(moveInfo.getMotionInfo().getPos()); - entity.getRotation().set(moveInfo.getMotionInfo().getRot()); - entity.setLastMoveSceneTimeMs(moveInfo.getSceneTime()); - entity.setLastMoveReliableSeq(moveInfo.getReliableSeq()); - entity.setMotionState(moveInfo.getMotionInfo().getState()); - } - - private boolean isPlayerMoving() { - float diffX = currentCoordinates.getX() - previousCoordinates.getX(); - float diffY = currentCoordinates.getY() - previousCoordinates.getY(); - float diffZ = currentCoordinates.getZ() - previousCoordinates.getZ(); - // Grasscutter.getLogger().debug("isPlayerMoving: " + previousCoordinates + ", " + currentCoordinates + ", " + diffX + ", " + diffY + ", " + diffZ); - return Math.abs(diffX) > 0.2 || Math.abs(diffY) > 0.1 || Math.abs(diffZ) > 0.2; - } - - private int getCurrentStamina() { - return player.getProperty(PlayerProperty.PROP_CUR_PERSIST_STAMINA); - } - - private int getMaximumStamina() { - return player.getProperty(PlayerProperty.PROP_MAX_STAMINA); - } - - // Returns new stamina - public int updateStamina(GameSession session, int amount) { - int currentStamina = session.getPlayer().getProperty(PlayerProperty.PROP_CUR_PERSIST_STAMINA); - if (amount == 0) { - return currentStamina; - } - int playerMaxStamina = session.getPlayer().getProperty(PlayerProperty.PROP_MAX_STAMINA); - int newStamina = currentStamina + amount; - if (newStamina < 0) { - newStamina = 0; - } - if (newStamina > playerMaxStamina) { - newStamina = playerMaxStamina; - } - session.getPlayer().setProperty(PlayerProperty.PROP_CUR_PERSIST_STAMINA, newStamina); - session.send(new PacketPlayerPropNotify(player, PlayerProperty.PROP_CUR_PERSIST_STAMINA)); - return newStamina; - } - - private void handleFallOnGround(@NotNull MotionInfo motionInfo) { - MotionState state = motionInfo.getState(); - // land speed and fall on ground event arrive in different packets - // cache land speed - if (state == MotionState.MOTION_LAND_SPEED) { - landSpeed = motionInfo.getSpeed().getY(); - landTimeMillisecond = System.currentTimeMillis(); - } - if (state == MotionState.MOTION_FALL_ON_GROUND) { - // if not received immediately after MOTION_LAND_SPEED, discard this packet. - // TODO: Test in high latency. - int maxDelay = 200; - if ((System.currentTimeMillis() - landTimeMillisecond) > maxDelay) { - Grasscutter.getLogger().debug("MOTION_FALL_ON_GROUND received after " + maxDelay + "ms, discard."); - return; - } - float currentHP = cachedEntity.getFightProperty(FightProperty.FIGHT_PROP_CUR_HP); - float maxHP = cachedEntity.getFightProperty(FightProperty.FIGHT_PROP_MAX_HP); - float damage = 0; - Grasscutter.getLogger().debug("LandSpeed: " + landSpeed); - if (landSpeed < -23.5) { - damage = (float)(maxHP * 0.33); - } - if (landSpeed < -25) { - damage = (float)(maxHP * 0.5); - } - if (landSpeed < -26.5) { - damage = (float)(maxHP * 0.66); - } - if (landSpeed < -28) { - damage = (maxHP * 1); - } - float newHP = currentHP - damage; - if (newHP < 0) { - newHP = 0; - } - Grasscutter.getLogger().debug("Max: " + maxHP + "\tCurr: " + currentHP + "\tDamage: " + damage + "\tnewHP: " + newHP); - cachedEntity.setFightProperty(FightProperty.FIGHT_PROP_CUR_HP, newHP); - cachedEntity.getWorld().broadcastPacket(new PacketEntityFightPropUpdateNotify(cachedEntity, FightProperty.FIGHT_PROP_CUR_HP)); - if (newHP == 0) { - killAvatar(cachedSession, cachedEntity, PlayerDieType.PLAYER_DIE_FALL); - } - landSpeed = 0; - } - } - - private void handleDrowning() { - int stamina = getCurrentStamina(); - if (stamina < 10) { - boolean isSwimming = MotionStatesCategorized.get("SWIM").contains(currentState); - Grasscutter.getLogger().debug(player.getProperty(PlayerProperty.PROP_CUR_PERSIST_STAMINA) + "/" + player.getProperty(PlayerProperty.PROP_MAX_STAMINA) + "\t" + currentState + "\t" + isSwimming); - if (isSwimming && currentState != MotionState.MOTION_SWIM_IDLE) { - killAvatar(cachedSession, cachedEntity, PlayerDieType.PLAYER_DIE_DRAWN); - } - } - } - - public void killAvatar(GameSession session, GameEntity entity, PlayerDieType dieType) { - cachedSession.send(new PacketAvatarLifeStateChangeNotify( - cachedSession.getPlayer().getTeamManager().getCurrentAvatarEntity().getAvatar(), - LifeState.LIFE_DEAD, - dieType - )); - cachedSession.send(new PacketLifeStateChangeNotify( - cachedEntity, - LifeState.LIFE_DEAD, - dieType - )); - cachedEntity.setFightProperty(FightProperty.FIGHT_PROP_CUR_HP, 0); - cachedEntity.getWorld().broadcastPacket(new PacketEntityFightPropUpdateNotify(cachedEntity, FightProperty.FIGHT_PROP_CUR_HP)); - entity.getWorld().broadcastPacket(new PacketLifeStateChangeNotify(0, entity, LifeState.LIFE_DEAD)); - session.getPlayer().getScene().removeEntity(entity); - ((EntityAvatar)entity).onDeath(dieType, 0); - } - - private class MotionManagerTick extends TimerTask - { - public void run() { - if (Grasscutter.getConfig().OpenStamina) { - boolean moving = isPlayerMoving(); - if (moving || (getCurrentStamina() < getMaximumStamina())) { - // Grasscutter.getLogger().debug("Player moving: " + moving + ", stamina full: " + (getCurrentStamina() >= getMaximumStamina()) + ", recalculate stamina"); - Consumption consumption = new Consumption(ConsumptionType.None); - - // TODO: refactor these conditions. - if (MotionStatesCategorized.get("CLIMB").contains(currentState)) { - consumption = getClimbConsumption(); - } else if (MotionStatesCategorized.get("SWIM").contains((currentState))) { - consumption = getSwimConsumptions(); - } else if (MotionStatesCategorized.get("RUN").contains(currentState)) { - consumption = getRunWalkDashConsumption(); - } else if (MotionStatesCategorized.get("FLY").contains(currentState)) { - consumption = getFlyConsumption(); - } else if (MotionStatesCategorized.get("STANDBY").contains(currentState)) { - consumption = getStandConsumption(); - } else if (MotionStatesCategorized.get("FIGHT").contains(currentState)) { - consumption = getFightConsumption(); - } - - // delay 2 seconds before start recovering - as official server does. - if (cachedSession != null) { - if (consumption.amount < 0) { - staminaRecoverDelay = 0; - } - if (consumption.amount > 0 && consumption.consumptionType != ConsumptionType.POWERED_FLY) { - if (staminaRecoverDelay < 10) { - staminaRecoverDelay++; - consumption = new Consumption(ConsumptionType.None); - } - } - // Grasscutter.getLogger().debug(getCurrentStamina() + "/" + getMaximumStamina() + "\t" + currentState + "\t" + "isMoving: " + isPlayerMoving() + "\t(" + consumption.consumptionType + "," + consumption.amount + ")"); - updateStamina(cachedSession, consumption.amount); - } - - // tick triggered - handleDrowning(); - } - } - - previousState = currentState; - previousCoordinates = new Position(currentCoordinates.getX(), - currentCoordinates.getY(), currentCoordinates.getZ());; - } - } - - private Consumption getClimbConsumption() { - Consumption consumption = new Consumption(ConsumptionType.None); - if (currentState == MotionState.MOTION_CLIMB) { - consumption = new Consumption(ConsumptionType.CLIMBING); - if (previousState != MotionState.MOTION_CLIMB && previousState != MotionState.MOTION_CLIMB_JUMP) { - consumption = new Consumption(ConsumptionType.CLIMB_START); - } - if (!isPlayerMoving()) { - consumption = new Consumption(ConsumptionType.None); - } - } - if (currentState == MotionState.MOTION_CLIMB_JUMP) { - if (previousState != MotionState.MOTION_CLIMB_JUMP) { - consumption = new Consumption(ConsumptionType.CLIMB_JUMP); - } - } - return consumption; - } - - private Consumption getSwimConsumptions() { - Consumption consumption = new Consumption(ConsumptionType.None); - if (currentState == MotionState.MOTION_SWIM_MOVE) { - consumption = new Consumption(ConsumptionType.SWIMMING); - } - if (currentState == MotionState.MOTION_SWIM_DASH) { - consumption = new Consumption(ConsumptionType.SWIM_DASH_START); - if (previousState == MotionState.MOTION_SWIM_DASH) { - consumption = new Consumption(ConsumptionType.SWIM_DASH); - } - } - return consumption; - } - - private Consumption getRunWalkDashConsumption() { - Consumption consumption = new Consumption(ConsumptionType.None); - if (currentState == MotionState.MOTION_DASH_BEFORE_SHAKE) { - consumption = new Consumption(ConsumptionType.DASH); - if (previousState == MotionState.MOTION_DASH_BEFORE_SHAKE) { - // only charge once - consumption = new Consumption(ConsumptionType.SPRINT); - } - } - if (currentState == MotionState.MOTION_DASH) { - consumption = new Consumption(ConsumptionType.SPRINT); - } - if (currentState == MotionState.MOTION_RUN) { - consumption = new Consumption(ConsumptionType.RUN); - } - if (currentState == MotionState.MOTION_WALK) { - consumption = new Consumption(ConsumptionType.WALK); - } - return consumption; - } - - private Consumption getFlyConsumption() { - Consumption consumption = new Consumption(ConsumptionType.FLY); - HashMap glidingCostReduction = new HashMap<>() {{ - put(212301, 0.8f); // Amber - put(222301, 0.8f); // Venti - }}; - float reduction = 1; - for (EntityAvatar entity: cachedSession.getPlayer().getTeamManager().getActiveTeam()) { - for (int skillId: entity.getAvatar().getProudSkillList()) { - if (glidingCostReduction.containsKey(skillId)) { - reduction = glidingCostReduction.get(skillId); - } - } - } - consumption.amount *= reduction; - - // POWERED_FLY, e.g. wind tunnel - if (currentState == MotionState.MOTION_POWERED_FLY) { - consumption = new Consumption(ConsumptionType.POWERED_FLY); - } - return consumption; - } - - private Consumption getStandConsumption() { - Consumption consumption = new Consumption(ConsumptionType.None); - if (currentState == MotionState.MOTION_STANDBY) { - consumption = new Consumption(ConsumptionType.STANDBY); - } - if (currentState == MotionState.MOTION_STANDBY_MOVE) { - consumption = new Consumption(ConsumptionType.STANDBY_MOVE); - } - return consumption; - } - - private Consumption getFightConsumption() { - Consumption consumption = new Consumption(ConsumptionType.None); - HashMap fightingCost = new HashMap<>() {{ - put(10013, -1000); // Kamisato Ayaka - put(10413, -1000); // Mona - }}; - if (fightingCost.containsKey(skillCasting)) { - consumption = new Consumption(ConsumptionType.FIGHT, fightingCost.get(skillCasting)); - // only handle once, so reset. - skillCasting = 0; - skillCaster = 0; - } - return consumption; - } - - public void notifySkill(int caster, int skillId) { - skillCaster = caster; - skillCasting = skillId; - } -} - diff --git a/src/main/java/emu/grasscutter/game/managers/MultiplayerManager.java b/src/main/java/emu/grasscutter/game/managers/MultiplayerManager.java index 247b3356d..91566ef20 100644 --- a/src/main/java/emu/grasscutter/game/managers/MultiplayerManager.java +++ b/src/main/java/emu/grasscutter/game/managers/MultiplayerManager.java @@ -27,7 +27,7 @@ public class MultiplayerManager { public void applyEnterMp(Player player, int targetUid) { Player target = getServer().getPlayerByUid(targetUid); if (target == null) { - player.sendPacket(new PacketPlayerApplyEnterMpResultNotify(targetUid, "", false, PlayerApplyEnterMpResultNotifyOuterClass.PlayerApplyEnterMpResultNotify.Reason.PLAYER_CANNOT_ENTER_MP)); + player.sendPacket(new PacketPlayerApplyEnterMpResultNotify(targetUid, "", false, PlayerApplyEnterMpResultNotifyOuterClass.PlayerApplyEnterMpResultNotify.Reason.REASON_PLAYER_CANNOT_ENTER_MP)); return; } @@ -72,12 +72,12 @@ public class MultiplayerManager { // Sanity checks - Dont let the requesting player join if they are already in multiplayer if (requester.getWorld().isMultiplayer()) { - request.getRequester().sendPacket(new PacketPlayerApplyEnterMpResultNotify(hostPlayer, false, PlayerApplyEnterMpResultNotifyOuterClass.PlayerApplyEnterMpResultNotify.Reason.PLAYER_CANNOT_ENTER_MP)); + request.getRequester().sendPacket(new PacketPlayerApplyEnterMpResultNotify(hostPlayer, false, PlayerApplyEnterMpResultNotifyOuterClass.PlayerApplyEnterMpResultNotify.Reason.REASON_PLAYER_CANNOT_ENTER_MP)); return; } // Response packet - request.getRequester().sendPacket(new PacketPlayerApplyEnterMpResultNotify(hostPlayer, isAgreed, PlayerApplyEnterMpResultNotifyOuterClass.PlayerApplyEnterMpResultNotify.Reason.PLAYER_JUDGE)); + request.getRequester().sendPacket(new PacketPlayerApplyEnterMpResultNotify(hostPlayer, isAgreed, PlayerApplyEnterMpResultNotifyOuterClass.PlayerApplyEnterMpResultNotify.Reason.REASON_PLAYER_JUDGE)); // Declined if (!isAgreed) { @@ -93,7 +93,7 @@ public class MultiplayerManager { world.addPlayer(hostPlayer); // Rejoin packet - hostPlayer.sendPacket(new PacketPlayerEnterSceneNotify(hostPlayer, hostPlayer, EnterType.ENTER_SELF, EnterReason.HostFromSingleToMp, hostPlayer.getScene().getId(), hostPlayer.getPos())); + hostPlayer.sendPacket(new PacketPlayerEnterSceneNotify(hostPlayer, hostPlayer, EnterType.ENTER_TYPE_SELF, EnterReason.HostFromSingleToMp, hostPlayer.getScene().getId(), hostPlayer.getPos())); } // Set scene pos and id of requester to the host player's @@ -105,7 +105,7 @@ public class MultiplayerManager { hostPlayer.getWorld().addPlayer(requester); // Packet - requester.sendPacket(new PacketPlayerEnterSceneNotify(requester, hostPlayer, EnterType.ENTER_OTHER, EnterReason.TeamJoin, hostPlayer.getScene().getId(), hostPlayer.getPos())); + requester.sendPacket(new PacketPlayerEnterSceneNotify(requester, hostPlayer, EnterType.ENTER_TYPE_OTHER, EnterReason.TeamJoin, hostPlayer.getScene().getId(), hostPlayer.getPos())); } public boolean leaveCoop(Player player) { @@ -126,7 +126,7 @@ public class MultiplayerManager { world.addPlayer(player); // Packet - player.sendPacket(new PacketPlayerEnterSceneNotify(player, EnterType.ENTER_SELF, EnterReason.TeamBack, player.getScene().getId(), player.getPos())); + player.sendPacket(new PacketPlayerEnterSceneNotify(player, EnterType.ENTER_TYPE_SELF, EnterReason.TeamBack, player.getScene().getId(), player.getPos())); return true; } @@ -153,7 +153,7 @@ public class MultiplayerManager { World world = new World(victim); world.addPlayer(victim); - victim.sendPacket(new PacketPlayerEnterSceneNotify(victim, EnterType.ENTER_SELF, EnterReason.TeamKick, victim.getScene().getId(), victim.getPos())); + victim.sendPacket(new PacketPlayerEnterSceneNotify(victim, EnterType.ENTER_TYPE_SELF, EnterReason.TeamKick, victim.getScene().getId(), victim.getPos())); return true; } } diff --git a/src/main/java/emu/grasscutter/game/managers/SotSManager.java b/src/main/java/emu/grasscutter/game/managers/SotSManager.java new file mode 100644 index 000000000..b80058142 --- /dev/null +++ b/src/main/java/emu/grasscutter/game/managers/SotSManager.java @@ -0,0 +1,192 @@ +package emu.grasscutter.game.managers; + +import ch.qos.logback.classic.Logger; +import emu.grasscutter.Grasscutter; +import emu.grasscutter.game.entity.EntityAvatar; +import emu.grasscutter.game.player.Player; +import emu.grasscutter.game.props.FightProperty; +import emu.grasscutter.game.props.PlayerProperty; +import emu.grasscutter.net.proto.ChangeHpReasonOuterClass.ChangeHpReason; +import emu.grasscutter.net.proto.PropChangeReasonOuterClass.PropChangeReason; +import emu.grasscutter.server.game.GameSession; +import emu.grasscutter.server.packet.send.PacketEntityFightPropChangeReasonNotify; +import emu.grasscutter.server.packet.send.PacketEntityFightPropUpdateNotify; + +import java.util.List; +import java.util.Timer; +import java.util.TimerTask; + +// Statue of the Seven Manager +public class SotSManager { + + // NOTE: Spring volume balance *1 = fight prop HP *100 + + private final Player player; + private final Logger logger = Grasscutter.getLogger(); + private Timer autoRecoverTimer; + private final boolean enablePriorityHealing = false; + + public final static int GlobalMaximumSpringVolume = 8500000; + + public SotSManager(Player player) { + this.player = player; + } + + public boolean getIsAutoRecoveryEnabled() { + return player.getProperty(PlayerProperty.PROP_IS_SPRING_AUTO_USE) == 1; + } + + public void setIsAutoRecoveryEnabled(boolean enabled) { + player.setProperty(PlayerProperty.PROP_IS_SPRING_AUTO_USE, enabled ? 1 : 0); + player.save(); + } + + public int getAutoRecoveryPercentage() { + return player.getProperty(PlayerProperty.PROP_SPRING_AUTO_USE_PERCENT); + } + + public void setAutoRecoveryPercentage(int percentage) { + player.setProperty(PlayerProperty.PROP_SPRING_AUTO_USE_PERCENT, percentage); + player.save(); + } + + public long getLastUsed() { + return player.getSpringLastUsed(); + } + + public void setLastUsed() { + player.setSpringLastUsed(System.currentTimeMillis() / 1000); + player.save(); + } + + public int getMaxVolume() { + return player.getProperty(PlayerProperty.PROP_MAX_SPRING_VOLUME); + } + + public void setMaxVolume(int volume) { + player.setProperty(PlayerProperty.PROP_MAX_SPRING_VOLUME, volume); + player.save(); + } + + public int getCurrentVolume() { + return player.getProperty(PlayerProperty.PROP_CUR_SPRING_VOLUME); + } + + public void setCurrentVolume(int volume) { + player.setProperty(PlayerProperty.PROP_CUR_SPRING_VOLUME, volume); + setLastUsed(); + player.save(); + } + + public void handleEnterTransPointRegionNotify() { + logger.trace("Player entered statue region"); + autoRevive(); + if (autoRecoverTimer == null) { + autoRecoverTimer = new Timer(); + autoRecoverTimer.schedule(new AutoRecoverTimerTick(), 2500, 15000); + } + } + + public void handleExitTransPointRegionNotify() { + logger.trace("Player left statue region"); + if (autoRecoverTimer != null) { + autoRecoverTimer.cancel(); + autoRecoverTimer = null; + } + } + + // autoRevive automatically revives all team members. + public void autoRevive() { + player.getTeamManager().getActiveTeam().forEach(entity -> { + boolean isAlive = entity.isAlive(); + if (isAlive) { + return; + } + logger.trace("Reviving avatar " + entity.getAvatar().getAvatarData().getName()); + player.getTeamManager().reviveAvatar(entity.getAvatar()); + player.getTeamManager().healAvatar(entity.getAvatar(), 30, 0); + }); + } + + private class AutoRecoverTimerTick extends TimerTask { + // autoRecover checks player setting to see if auto recover is enabled, and refill HP to the predefined level. + public void run() { + refillSpringVolume(); + + logger.trace("isAutoRecoveryEnabled: " + getIsAutoRecoveryEnabled() + "\tautoRecoverPercentage: " + getAutoRecoveryPercentage()); + + if (getIsAutoRecoveryEnabled()) { + List activeTeam = player.getTeamManager().getActiveTeam(); + // When the statue does not have enough remaining volume: + // Enhanced experience: Enable priority healing + // The current active character will get healed first, then sequential. + // Vanilla experience: Disable priority healing + // Sequential healing based on character index. + int priorityIndex = enablePriorityHealing ? player.getTeamManager().getCurrentCharacterIndex() : -1; + if (priorityIndex >= 0) { + checkAndHealAvatar(activeTeam.get(priorityIndex)); + } + for (int i = 0; i < activeTeam.size(); i++) { + if (i != priorityIndex) { + checkAndHealAvatar(activeTeam.get(i)); + } + } + } + } + } + + public void checkAndHealAvatar(EntityAvatar entity) { + int maxHP = (int) (entity.getFightProperty(FightProperty.FIGHT_PROP_MAX_HP) * 100); + int currentHP = (int) (entity.getFightProperty(FightProperty.FIGHT_PROP_CUR_HP) * 100); + if (currentHP == maxHP) { + return; + } + int targetHP = maxHP * getAutoRecoveryPercentage() / 100; + + if (targetHP > currentHP) { + int needHP = targetHP - currentHP; + int currentVolume = getCurrentVolume(); + if (currentVolume >= needHP) { + // sufficient + setCurrentVolume(currentVolume - needHP); + } else { + // insufficient balance + needHP = currentVolume; + setCurrentVolume(0); + } + if (needHP > 0) { + logger.trace("Healing avatar " + entity.getAvatar().getAvatarData().getName() + " +" + needHP); + player.getTeamManager().healAvatar(entity.getAvatar(), 0, needHP); + player.getSession().send(new PacketEntityFightPropChangeReasonNotify(entity, FightProperty.FIGHT_PROP_CUR_HP, + ((float) needHP / 100), List.of(3), PropChangeReason.PROP_CHANGE_REASON_STATUE_RECOVER, + ChangeHpReason.CHANGE_HP_REASON_CHANGE_HP_ADD_STATUE)); + player.getSession().send(new PacketEntityFightPropUpdateNotify(entity, FightProperty.FIGHT_PROP_CUR_HP)); + + } + } + } + + public void refillSpringVolume() { + // Temporary: Max spring volume depends on level of the statues in Mondstadt and Liyue. Override until we have statue level. + // TODO: remove + // https://genshin-impact.fandom.com/wiki/Statue_of_The_Seven#:~:text=region%20of%20Inazuma.-,Statue%20Levels,-Upon%20first%20unlocking + setMaxVolume(8500000); + // Temporary: Auto enable 100% statue recovery until we can adjust statue settings in game + // TODO: remove + setAutoRecoveryPercentage(100); + setIsAutoRecoveryEnabled(true); + + int maxVolume = getMaxVolume(); + int currentVolume = getCurrentVolume(); + if (currentVolume < maxVolume) { + long now = System.currentTimeMillis() / 1000; + int secondsSinceLastUsed = (int) (now - getLastUsed()); + // 15s = 1% max volume + int volumeRefilled = secondsSinceLastUsed * maxVolume / 15 / 100; + logger.trace("Statue has refilled HP volume: " + volumeRefilled); + currentVolume = Math.min(currentVolume + volumeRefilled, maxVolume); + logger.trace("Statue remaining HP volume: " + currentVolume); + setCurrentVolume(currentVolume); + } + } +} diff --git a/src/main/java/emu/grasscutter/game/managers/SotSManager/SotSManager.java b/src/main/java/emu/grasscutter/game/managers/SotSManager/SotSManager.java deleted file mode 100644 index 0bfdf9454..000000000 --- a/src/main/java/emu/grasscutter/game/managers/SotSManager/SotSManager.java +++ /dev/null @@ -1,175 +0,0 @@ -package emu.grasscutter.game.managers.SotSManager; - -import emu.grasscutter.Grasscutter; -import emu.grasscutter.game.avatar.Avatar; -import emu.grasscutter.game.entity.EntityAvatar; -import emu.grasscutter.game.entity.GameEntity; -import emu.grasscutter.game.managers.MovementManager.MovementManager; -import emu.grasscutter.game.player.Player; -import emu.grasscutter.game.props.FightProperty; -import emu.grasscutter.game.props.PlayerProperty; -import emu.grasscutter.game.world.World; -import emu.grasscutter.net.proto.ChangeHpReasonOuterClass; -import emu.grasscutter.net.proto.PropChangeReasonOuterClass; -import emu.grasscutter.server.game.GameSession; -import emu.grasscutter.server.packet.send.PacketAvatarFightPropUpdateNotify; -import emu.grasscutter.server.packet.send.PacketAvatarLifeStateChangeNotify; -import emu.grasscutter.server.packet.send.PacketEntityFightPropChangeReasonNotify; -import emu.grasscutter.server.packet.send.PacketEntityFightPropUpdateNotify; - -import java.util.List; -import java.util.Timer; -import java.util.TimerTask; - -// Statue of the Seven Manager -public class SotSManager { - - // NOTE: Spring volume balance *1 = fight prop HP *100 - - private final Player player; - private Timer autoRecoverTimer; - - public SotSManager(Player player) { - this.player = player; - } - - public boolean getIsAutoRecoveryEnabled() { - return player.getProperty(PlayerProperty.PROP_IS_SPRING_AUTO_USE) == 1; - } - - public void setIsAutoRecoveryEnabled(boolean enabled) { - player.setProperty(PlayerProperty.PROP_IS_SPRING_AUTO_USE, enabled ? 1 : 0); - } - - public int getAutoRecoveryPercentage() { - return player.getProperty(PlayerProperty.PROP_SPRING_AUTO_USE_PERCENT); - } - - public void setAutoRecoveryPercentage(int percentage) { - player.setProperty(PlayerProperty.PROP_SPRING_AUTO_USE_PERCENT, percentage); - } - - // autoRevive automatically revives all team members. - public void autoRevive(GameSession session) { - player.getTeamManager().getActiveTeam().forEach(entity -> { - boolean isAlive = entity.isAlive(); - float currentHP = entity.getAvatar().getFightProperty(FightProperty.FIGHT_PROP_CUR_HP); - float maxHP = entity.getAvatar().getFightProperty(FightProperty.FIGHT_PROP_MAX_HP); -// Grasscutter.getLogger().debug("" + entity.getAvatar().getAvatarData().getName() + "\t" + currentHP + "/" + maxHP + "\t" + (isAlive ? "ALIVE":"DEAD")); - float newHP = (float)(maxHP * 0.3); - if (currentHP < newHP) { - updateAvatarCurHP(session, entity, newHP); - } - if (!isAlive) { - entity.getWorld().broadcastPacket(new PacketAvatarLifeStateChangeNotify(entity.getAvatar())); - } - }); - } - - public void scheduleAutoRecover(GameSession session) { - if (autoRecoverTimer == null) { - autoRecoverTimer = new Timer(); - autoRecoverTimer.schedule(new AutoRecoverTimerTick(session), 2500); - } - } - - public void cancelAutoRecover() { - if (autoRecoverTimer != null) { - autoRecoverTimer.cancel(); - autoRecoverTimer = null; - } - } - - private class AutoRecoverTimerTick extends TimerTask - { - private GameSession session; - - public AutoRecoverTimerTick(GameSession session) { - this.session = session; - } - public void run() { - autoRecover(session); - cancelAutoRecover(); - } - } - - public void refillSpringVolume() { - // TODO: max spring volume depends on level of the statues in Mondstadt and Liyue. - // https://genshin-impact.fandom.com/wiki/Statue_of_The_Seven#:~:text=region%20of%20Inazuma.-,Statue%20Levels,-Upon%20first%20unlocking - player.setProperty(PlayerProperty.PROP_MAX_SPRING_VOLUME, 8500000); - - long now = System.currentTimeMillis() / 1000; - long secondsSinceLastUsed = now - player.getSpringLastUsed(); - float percentageRefilled = (float)secondsSinceLastUsed / 15 / 100; // 15s = 1% max volume - int maxVolume = player.getProperty(PlayerProperty.PROP_MAX_SPRING_VOLUME); - int currentVolume = player.getProperty(PlayerProperty.PROP_CUR_SPRING_VOLUME); - if (currentVolume < maxVolume) { - int volumeRefilled = (int)(percentageRefilled * maxVolume); - int newVolume = currentVolume + volumeRefilled; - if (currentVolume + volumeRefilled > maxVolume) { - newVolume = maxVolume; - } - player.setProperty(PlayerProperty.PROP_CUR_SPRING_VOLUME, newVolume); - } - player.setSpringLastUsed(now); - player.save(); - } - - // autoRecover checks player setting to see if auto recover is enabled, and refill HP to the predefined level. - public void autoRecover(GameSession session) { - // TODO: In MP, respect SotS settings from the HOST. - boolean isAutoRecoveryEnabled = getIsAutoRecoveryEnabled(); - int autoRecoverPercentage = getAutoRecoveryPercentage(); - Grasscutter.getLogger().debug("isAutoRecoveryEnabled: " + isAutoRecoveryEnabled + "\tautoRecoverPercentage: " + autoRecoverPercentage); - - if (isAutoRecoveryEnabled) { - player.getTeamManager().getActiveTeam().forEach(entity -> { - float maxHP = entity.getFightProperty(FightProperty.FIGHT_PROP_MAX_HP); - float currentHP = entity.getFightProperty(FightProperty.FIGHT_PROP_CUR_HP); - if (currentHP == maxHP) { - return; - } - float targetHP = maxHP * autoRecoverPercentage / 100; - - if (targetHP > currentHP) { - float needHP = targetHP - currentHP; - float needSV = needHP * 100; // convert HP needed to Spring Volume needed - - int sotsSVBalance = player.getProperty(PlayerProperty.PROP_CUR_SPRING_VOLUME); - if (sotsSVBalance >= needSV) { - // sufficient - sotsSVBalance -= needSV; - } else { - // insufficient balance - needSV = sotsSVBalance; - sotsSVBalance = 0; - } - player.setProperty(PlayerProperty.PROP_CUR_SPRING_VOLUME, sotsSVBalance); - player.setSpringLastUsed(System.currentTimeMillis() / 1000); - - float newHP = currentHP + needSV / 100; // convert SV to HP - - updateAvatarCurHP(session, entity, newHP); - } - }); - } - } - - private void updateAvatarCurHP(GameSession session, EntityAvatar entity, float newHP) { - // TODO: Figure out why client shows current HP instead of added HP. - // Say an avatar had 12000 and now has 14000, it should show "2000". - // The client always show "+14000" which is incorrect. - entity.setFightProperty(FightProperty.FIGHT_PROP_CUR_HP, newHP); - session.send(new PacketEntityFightPropChangeReasonNotify(entity, FightProperty.FIGHT_PROP_CUR_HP, - newHP, List.of(3), PropChangeReasonOuterClass.PropChangeReason.PROP_CHANGE_STATUE_RECOVER, - ChangeHpReasonOuterClass.ChangeHpReason.ChangeHpAddStatue)); - session.send(new PacketEntityFightPropUpdateNotify(entity, FightProperty.FIGHT_PROP_CUR_HP)); - - Avatar avatar = entity.getAvatar(); - avatar.setCurrentHp(newHP); - session.send(new PacketAvatarFightPropUpdateNotify(avatar, FightProperty.FIGHT_PROP_CUR_HP)); - player.save(); - } - - -} diff --git a/src/main/java/emu/grasscutter/game/managers/StaminaManager/AfterUpdateStaminaListener.java b/src/main/java/emu/grasscutter/game/managers/StaminaManager/AfterUpdateStaminaListener.java new file mode 100644 index 000000000..11a5c9178 --- /dev/null +++ b/src/main/java/emu/grasscutter/game/managers/StaminaManager/AfterUpdateStaminaListener.java @@ -0,0 +1,12 @@ +package emu.grasscutter.game.managers.StaminaManager; + +public interface AfterUpdateStaminaListener { + /** + * onBeforeUpdateStamina() will be called before StaminaManager attempt to update the player's current stamina. + * This gives listeners a chance to intercept this update. + * + * @param reason Why updating stamina. + * @param newStamina New Stamina value. + */ + void onAfterUpdateStamina(String reason, int newStamina, boolean isCharacterStamina); +} diff --git a/src/main/java/emu/grasscutter/game/managers/StaminaManager/BeforeUpdateStaminaListener.java b/src/main/java/emu/grasscutter/game/managers/StaminaManager/BeforeUpdateStaminaListener.java new file mode 100644 index 000000000..39075f35b --- /dev/null +++ b/src/main/java/emu/grasscutter/game/managers/StaminaManager/BeforeUpdateStaminaListener.java @@ -0,0 +1,20 @@ +package emu.grasscutter.game.managers.StaminaManager; + +public interface BeforeUpdateStaminaListener { + /** + * onBeforeUpdateStamina() will be called before StaminaManager attempt to update the player's current stamina. + * This gives listeners a chance to intercept this update. + * @param reason Why updating stamina. + * @param newStamina New ABSOLUTE stamina value. + * @return true if you want to cancel this update, otherwise false. + */ + int onBeforeUpdateStamina(String reason, int newStamina, boolean isCharacterStamina); + /** + * onBeforeUpdateStamina() will be called before StaminaManager attempt to update the player's current stamina. + * This gives listeners a chance to intercept this update. + * @param reason Why updating stamina. + * @param consumption ConsumptionType and RELATIVE stamina change amount. + * @return true if you want to cancel this update, otherwise false. + */ + Consumption onBeforeUpdateStamina(String reason, Consumption consumption, boolean isCharacterStamina); +} \ No newline at end of file diff --git a/src/main/java/emu/grasscutter/game/managers/StaminaManager/Consumption.java b/src/main/java/emu/grasscutter/game/managers/StaminaManager/Consumption.java new file mode 100644 index 000000000..a6185f063 --- /dev/null +++ b/src/main/java/emu/grasscutter/game/managers/StaminaManager/Consumption.java @@ -0,0 +1,18 @@ +package emu.grasscutter.game.managers.StaminaManager; + +public class Consumption { + public ConsumptionType type = ConsumptionType.None; + public int amount = 0; + + public Consumption(ConsumptionType type, int amount) { + this.type = type; + this.amount = amount; + } + + public Consumption(ConsumptionType type) { + this(type, type.amount); + } + + public Consumption() { + } +} diff --git a/src/main/java/emu/grasscutter/game/managers/StaminaManager/ConsumptionType.java b/src/main/java/emu/grasscutter/game/managers/StaminaManager/ConsumptionType.java new file mode 100644 index 000000000..506bf1728 --- /dev/null +++ b/src/main/java/emu/grasscutter/game/managers/StaminaManager/ConsumptionType.java @@ -0,0 +1,37 @@ +package emu.grasscutter.game.managers.StaminaManager; + +public enum ConsumptionType { + None(0), + + // consume + CLIMBING(-150), + CLIMB_START(-500), + CLIMB_JUMP(-2500), + DASH(-360), + FIGHT(0), // See StaminaManager.getFightConsumption() + FLY(-60), + // Slow swimming is handled per movement, not per second. + // Arm movement frequency depends on gender/age/height. + // TODO: Instead of cost -80 per tick, find a proper way to calculate cost. + SKIFF_DASH(-204), + SPRINT(-1800), + SWIM_DASH_START(-2000), + SWIM_DASH(-204), // -10.2 per second, 5Hz = -204 each tick + SWIMMING(-80), + TALENT_DASH(-300), // -1500 per second, 5Hz = -300 each tick + TALENT_DASH_START(-1000), + + // restore + POWERED_FLY(500), + POWERED_SKIFF(500), + RUN(500), + SKIFF(500), + STANDBY(500), + WALK(500); + + public final int amount; + + ConsumptionType(int amount) { + this.amount = amount; + } +} \ No newline at end of file diff --git a/src/main/java/emu/grasscutter/game/managers/StaminaManager/README.md b/src/main/java/emu/grasscutter/game/managers/StaminaManager/README.md new file mode 100644 index 000000000..39a4e7988 --- /dev/null +++ b/src/main/java/emu/grasscutter/game/managers/StaminaManager/README.md @@ -0,0 +1,73 @@ +# Stamina Manager + +--- +## UpdateStamina +```java +// will use consumption.consumptionType as reason +public int updateStaminaRelative(GameSession session, Consumption consumption); +``` +```java +public int updateStaminaAbsolute(GameSession session, String reason, int newStamina) +``` + +--- +## Pause and Resume +```java +public void startSustainedStaminaHandler() +``` +```java +public void stopSustainedStaminaHandler() +``` + + +--- +## Stamina change listeners and intercepting +### BeforeUpdateStaminaListener +```java + +import emu.grasscutter.game.managers.StaminaManager.BeforeUpdateStaminaListener; + +// Listener sample: plugin disable CLIMB_JUMP stamina cost. +private class MyClass implements BeforeUpdateStaminaListener { + // Make your class implement the listener, and pass in your class as a listener. + + public MyClass() { + getStaminaManager().registerBeforeUpdateStaminaListener("myClass", this); + } + + @Override + public boolean onBeforeUpdateStamina(String reason, int newStamina) { + // do not intercept this update + return false; + } + + @Override + public boolean onBeforeUpdateStamina(String reason, Consumption consumption) { + // Try to intercept if this update is CLIMB_JUMP + if (consumption.consumptionType == ConsumptionType.CLIMB_JUMP) { + return true; + } + // If it is not CLIMB_JUMP, do not intercept. + return false; + } +} +``` +### AfterUpdateStaminaListener +```java + +import emu.grasscutter.game.managers.StaminaManager.AfterUpdateStaminaListener; + +// Listener sample: plugin listens for changes already made. +private class MyClass implements AfterUpdateStaminaListener { + // Make your class implement the listener, and pass in your class as a listener. + + public MyClass() { + registerAfterUpdateStaminaListener("myClass", this); + } + + @Override + public void onAfterUpdateStamina(String reason, int newStamina) { + // ... + } +} +``` \ No newline at end of file diff --git a/src/main/java/emu/grasscutter/game/managers/StaminaManager/StaminaManager.java b/src/main/java/emu/grasscutter/game/managers/StaminaManager/StaminaManager.java new file mode 100644 index 000000000..4d69e7a99 --- /dev/null +++ b/src/main/java/emu/grasscutter/game/managers/StaminaManager/StaminaManager.java @@ -0,0 +1,717 @@ +package emu.grasscutter.game.managers.StaminaManager; + +import ch.qos.logback.classic.Logger; +import emu.grasscutter.Grasscutter; +import emu.grasscutter.command.commands.NoStaminaCommand; +import emu.grasscutter.data.GameData; +import emu.grasscutter.game.avatar.Avatar; +import emu.grasscutter.game.entity.EntityAvatar; +import emu.grasscutter.game.entity.GameEntity; +import emu.grasscutter.game.player.Player; +import emu.grasscutter.game.props.FightProperty; +import emu.grasscutter.game.props.LifeState; +import emu.grasscutter.game.props.PlayerProperty; +import emu.grasscutter.game.props.WeaponType; +import emu.grasscutter.net.proto.EntityMoveInfoOuterClass.EntityMoveInfo; +import emu.grasscutter.net.proto.MotionInfoOuterClass.MotionInfo; +import emu.grasscutter.net.proto.MotionStateOuterClass.MotionState; +import emu.grasscutter.net.proto.PlayerDieTypeOuterClass.PlayerDieType; +import emu.grasscutter.net.proto.VectorOuterClass.Vector; +import emu.grasscutter.net.proto.VehicleInteractTypeOuterClass.VehicleInteractType; +import emu.grasscutter.server.game.GameSession; +import emu.grasscutter.server.packet.send.*; +import emu.grasscutter.utils.Position; +import org.jetbrains.annotations.NotNull; + +import java.util.*; + +import static emu.grasscutter.Configuration.GAME_OPTIONS; + +public class StaminaManager { + + // TODO: Skiff state detection? + private final Player player; + private static final HashMap> MotionStatesCategorized = new HashMap<>() {{ + put("CLIMB", new HashSet<>(List.of( + MotionState.MOTION_STATE_CLIMB, // sustained, when not moving no cost no recover + MotionState.MOTION_STATE_STANDBY_TO_CLIMB // NOT OBSERVED, see MOTION_JUMP_UP_WALL_FOR_STANDBY + ))); + put("DASH", new HashSet<>(List.of( + MotionState.MOTION_STATE_DANGER_DASH, // sustained + MotionState.MOTION_STATE_DASH // sustained + ))); + put("FLY", new HashSet<>(List.of( + MotionState.MOTION_STATE_FLY, // sustained + MotionState.MOTION_STATE_FLY_FAST, // sustained + MotionState.MOTION_STATE_FLY_SLOW, // sustained + MotionState.MOTION_STATE_POWERED_FLY // sustained, recover + ))); + put("RUN", new HashSet<>(List.of( + MotionState.MOTION_STATE_DANGER_RUN, // sustained, recover + MotionState.MOTION_STATE_RUN // sustained, recover + ))); + put("SKIFF", new HashSet<>(List.of( + MotionState.MOTION_STATE_SKIFF_BOARDING, // NOT OBSERVED even when boarding + MotionState.MOTION_STATE_SKIFF_DASH, // sustained, observed with waverider entity ID. + MotionState.MOTION_STATE_SKIFF_NORMAL, // sustained, OBSERVED when both normal and dashing + MotionState.MOTION_STATE_SKIFF_POWERED_DASH // sustained, recover + ))); + put("STANDBY", new HashSet<>(List.of( + MotionState.MOTION_STATE_DANGER_STANDBY_MOVE, // sustained, recover + MotionState.MOTION_STATE_DANGER_STANDBY, // sustained, recover + MotionState.MOTION_STATE_LADDER_TO_STANDBY, // NOT OBSERVED + MotionState.MOTION_STATE_STANDBY_MOVE, // sustained, recover + MotionState.MOTION_STATE_STANDBY // sustained, recover + ))); + put("SWIM", new HashSet<>(List.of( + MotionState.MOTION_STATE_SWIM_IDLE, // sustained + MotionState.MOTION_STATE_SWIM_DASH, // immediate and sustained + MotionState.MOTION_STATE_SWIM_JUMP, // NOT OBSERVED + MotionState.MOTION_STATE_SWIM_MOVE // sustained + ))); + put("WALK", new HashSet<>(List.of( + MotionState.MOTION_STATE_DANGER_WALK, // sustained, recover + MotionState.MOTION_STATE_WALK // sustained, recover + ))); + put("OTHER", new HashSet<>(List.of( + MotionState.MOTION_STATE_CLIMB_JUMP, // cost only once if repeated without switching state + MotionState.MOTION_STATE_DASH_BEFORE_SHAKE, // immediate one time sprint charge. + MotionState.MOTION_STATE_FIGHT, // immediate, if sustained then subsequent will be MOTION_NOTIFY + MotionState.MOTION_STATE_JUMP_UP_WALL_FOR_STANDBY, // immediate, observed when RUN/WALK->CLIMB + MotionState.MOTION_STATE_NOTIFY, // can be either cost or recover - check previous state and check skill casting + MotionState.MOTION_STATE_SIT_IDLE, // sustained, recover + MotionState.MOTION_STATE_JUMP // recover + ))); + put("NOCOST_NORECOVER", new HashSet<>(List.of( + MotionState.MOTION_STATE_LADDER_SLIP, // NOT OBSERVED + MotionState.MOTION_STATE_SLIP, // sustained, no cost no recover + MotionState.MOTION_STATE_FLY_IDLE // NOT OBSERVED + ))); + put("IGNORE", new HashSet<>(List.of( + // these states have no impact on stamina + MotionState.MOTION_STATE_CROUCH_IDLE, + MotionState.MOTION_STATE_CROUCH_MOVE, + MotionState.MOTION_STATE_CROUCH_ROLL, + MotionState.MOTION_STATE_DESTROY_VEHICLE, + MotionState.MOTION_STATE_FALL_ON_GROUND, + MotionState.MOTION_STATE_FOLLOW_ROUTE, + MotionState.MOTION_STATE_FORCE_SET_POS, + MotionState.MOTION_STATE_GO_UPSTAIRS, + MotionState.MOTION_STATE_JUMP_OFF_WALL, + MotionState.MOTION_STATE_LADDER_IDLE, + MotionState.MOTION_STATE_LADDER_MOVE, + MotionState.MOTION_STATE_LAND_SPEED, + MotionState.MOTION_STATE_MOVE_FAIL_ACK, + MotionState.MOTION_STATE_NONE, + MotionState.MOTION_STATE_NUM, + MotionState.MOTION_STATE_QUEST_FORCE_DRAG, + MotionState.MOTION_STATE_RESET, + MotionState.MOTION_STATE_STANDBY_TO_LADDER, + MotionState.MOTION_STATE_WATERFALL + ))); + }}; + + private final Logger logger = Grasscutter.getLogger(); + public final static int GlobalCharacterMaximumStamina = 24000; + public final static int GlobalVehicleMaxStamina = 24000; + private Position currentCoordinates = new Position(0, 0, 0); + private Position previousCoordinates = new Position(0, 0, 0); + private MotionState currentState = MotionState.MOTION_STATE_STANDBY; + private MotionState previousState = MotionState.MOTION_STATE_STANDBY; + private Timer sustainedStaminaHandlerTimer; + private GameSession cachedSession = null; + private GameEntity cachedEntity = null; + private int staminaRecoverDelay = 0; + private final HashMap beforeUpdateStaminaListeners = new HashMap<>(); + private final HashMap afterUpdateStaminaListeners = new HashMap<>(); + private int lastSkillId = 0; + private int lastSkillCasterId = 0; + private boolean lastSkillFirstTick = true; + private int vehicleId = -1; + private int vehicleStamina = GlobalVehicleMaxStamina; + private static final HashSet TalentMovements = new HashSet<>(List.of( + 10013, 10413 + )); + private static final HashMap ClimbFoodReductionMap = new HashMap<>() {{ + // TODO: get real food id + put(0, 0.8f); // Sample food + }}; + private static final HashMap DashFoodReductionMap = new HashMap<>() {{ + // TODO: get real food id + put(0, 0.8f); // Sample food + }}; + private static final HashMap FlyFoodReductionMap = new HashMap<>() {{ + // TODO: get real food id + put(0, 0.8f); // Sample food + }}; + private static final HashMap SwimFoodReductionMap = new HashMap<>() {{ + // TODO: get real food id + put(0, 0.8f); // Sample food + }}; + private static final HashMap ClimbTalentReductionMap = new HashMap<>() {{ + put(262301, 0.8f); + }}; + private static final HashMap FlyTalentReductionMap = new HashMap<>() {{ + put(212301, 0.8f); + put(222301, 0.8f); + }}; + private static final HashMap SwimTalentReductionMap = new HashMap<>() {{ + put(242301, 0.8f); + put(542301, 0.8f); + }}; + + public static void initialize() { + // TODO: Initialize foods etc. + } + + public StaminaManager(Player player) { + this.player = player; + } + + // Accessors + + public void setSkillCast(int skillId, int skillCasterId) { + lastSkillFirstTick = true; + lastSkillId = skillId; + lastSkillCasterId = skillCasterId; + } + + public int getMaxCharacterStamina() { + return player.getProperty(PlayerProperty.PROP_MAX_STAMINA); + } + + public int getCurrentCharacterStamina() { + return player.getProperty(PlayerProperty.PROP_CUR_PERSIST_STAMINA); + } + + public int getMaxVehicleStamina() { + return GlobalVehicleMaxStamina; + } + + public int getCurrentVehicleStamina() { + return vehicleStamina; + } + + public boolean registerBeforeUpdateStaminaListener(String listenerName, BeforeUpdateStaminaListener listener) { + if (beforeUpdateStaminaListeners.containsKey(listenerName)) { + return false; + } + beforeUpdateStaminaListeners.put(listenerName, listener); + return true; + } + + public boolean unregisterBeforeUpdateStaminaListener(String listenerName) { + if (!beforeUpdateStaminaListeners.containsKey(listenerName)) { + return false; + } + beforeUpdateStaminaListeners.remove(listenerName); + return true; + } + + public boolean registerAfterUpdateStaminaListener(String listenerName, AfterUpdateStaminaListener listener) { + if (afterUpdateStaminaListeners.containsKey(listenerName)) { + return false; + } + afterUpdateStaminaListeners.put(listenerName, listener); + return true; + } + + public boolean unregisterAfterUpdateStaminaListener(String listenerName) { + if (!afterUpdateStaminaListeners.containsKey(listenerName)) { + return false; + } + afterUpdateStaminaListeners.remove(listenerName); + return true; + } + + private boolean isPlayerMoving() { + float diffX = currentCoordinates.getX() - previousCoordinates.getX(); + float diffY = currentCoordinates.getY() - previousCoordinates.getY(); + float diffZ = currentCoordinates.getZ() - previousCoordinates.getZ(); + logger.trace("isPlayerMoving: " + previousCoordinates + ", " + currentCoordinates + + ", " + diffX + ", " + diffY + ", " + diffZ); + return Math.abs(diffX) > 0.3 || Math.abs(diffY) > 0.2 || Math.abs(diffZ) > 0.3; + } + + public int updateStaminaRelative(GameSession session, Consumption consumption, boolean isCharacterStamina) { + int currentStamina = isCharacterStamina ? getCurrentCharacterStamina() : getCurrentVehicleStamina(); + if (consumption.amount == 0) { + return currentStamina; + } + // notify will update + for (Map.Entry listener : beforeUpdateStaminaListeners.entrySet()) { + Consumption overriddenConsumption = listener.getValue().onBeforeUpdateStamina(consumption.type.toString(), consumption, isCharacterStamina); + if ((overriddenConsumption.type != consumption.type) && (overriddenConsumption.amount != consumption.amount)) { + logger.debug("Stamina update relative(" + + consumption.type.toString() + ", " + consumption.amount + ") overridden to relative(" + + consumption.type.toString() + ", " + consumption.amount + ") by: " + listener.getKey()); + return currentStamina; + } + } + int maxStamina = isCharacterStamina ? getMaxCharacterStamina() : getMaxVehicleStamina(); + logger.trace((isCharacterStamina ? "C " : "V ") + currentStamina + "/" + maxStamina + "\t" + currentState + "\t" + + (isPlayerMoving() ? "moving" : " ") + "\t(" + consumption.type + "," + + consumption.amount + ")"); + int newStamina = currentStamina + consumption.amount; + if (newStamina < 0) { + newStamina = 0; + } else if (newStamina > maxStamina) { + newStamina = maxStamina; + } + return setStamina(session, consumption.type.toString(), newStamina, isCharacterStamina); + } + + public int updateStaminaAbsolute(GameSession session, String reason, int newStamina, boolean isCharacterStamina) { + int currentStamina = isCharacterStamina ? getCurrentCharacterStamina() : getCurrentVehicleStamina(); + // notify will update + for (Map.Entry listener : beforeUpdateStaminaListeners.entrySet()) { + int overriddenNewStamina = listener.getValue().onBeforeUpdateStamina(reason, newStamina, isCharacterStamina); + if (overriddenNewStamina != newStamina) { + logger.debug("Stamina update absolute(" + + reason + ", " + newStamina + ") overridden to absolute(" + + reason + ", " + newStamina + ") by: " + listener.getKey()); + return currentStamina; + } + } + int maxStamina = isCharacterStamina ? getMaxCharacterStamina() : getMaxVehicleStamina(); + if (newStamina < 0) { + newStamina = 0; + } else if (newStamina > maxStamina) { + newStamina = maxStamina; + } + return setStamina(session, reason, newStamina, isCharacterStamina); + } + + // Returns new stamina and sends PlayerPropNotify or VehicleStaminaNotify + public int setStamina(GameSession session, String reason, int newStamina, boolean isCharacterStamina) { + // Target Player + if (!GAME_OPTIONS.staminaUsage || session.getPlayer().getStamina()) { + newStamina = getMaxCharacterStamina(); + } + + // set stamina if is character stamina + if (isCharacterStamina) { + player.setProperty(PlayerProperty.PROP_CUR_PERSIST_STAMINA, newStamina); + session.send(new PacketPlayerPropNotify(player, PlayerProperty.PROP_CUR_PERSIST_STAMINA)); + } else { + vehicleStamina = newStamina; + session.send(new PacketVehicleStaminaNotify(vehicleId, ((float) newStamina) / 100)); + } + // notify updated + for (Map.Entry listener : afterUpdateStaminaListeners.entrySet()) { + listener.getValue().onAfterUpdateStamina(reason, newStamina, isCharacterStamina); + } + return newStamina; + } + + // Kills avatar, removes entity and sends notification. + // TODO: Probably move this to Avatar class? since other components may also need to kill avatar. + public void killAvatar(GameSession session, GameEntity entity, PlayerDieType dieType) { + session.send(new PacketAvatarLifeStateChangeNotify(player.getTeamManager().getCurrentAvatarEntity().getAvatar(), + LifeState.LIFE_DEAD, dieType)); + session.send(new PacketLifeStateChangeNotify(entity, LifeState.LIFE_DEAD, dieType)); + entity.setFightProperty(FightProperty.FIGHT_PROP_CUR_HP, 0); + entity.getWorld().broadcastPacket(new PacketEntityFightPropUpdateNotify(entity, FightProperty.FIGHT_PROP_CUR_HP)); + entity.getWorld().broadcastPacket(new PacketLifeStateChangeNotify(0, entity, LifeState.LIFE_DEAD)); + player.getScene().removeEntity(entity); + ((EntityAvatar) entity).onDeath(dieType, 0); + } + + public void startSustainedStaminaHandler() { + if (!player.isPaused() && sustainedStaminaHandlerTimer == null) { + sustainedStaminaHandlerTimer = new Timer(); + sustainedStaminaHandlerTimer.scheduleAtFixedRate(new SustainedStaminaHandler(), 0, 200); + logger.debug("[MovementManager] SustainedStaminaHandlerTimer started"); + } + } + + public void stopSustainedStaminaHandler() { + if (sustainedStaminaHandlerTimer != null) { + sustainedStaminaHandlerTimer.cancel(); + sustainedStaminaHandlerTimer = null; + logger.debug("[MovementManager] SustainedStaminaHandlerTimer stopped"); + } + } + + // Handlers + + // External trigger handler + + public void handleEvtDoSkillSuccNotify(GameSession session, int skillId, int casterId) { + // Ignore if skill not cast by not current active avatar + if (casterId != player.getTeamManager().getCurrentAvatarEntity().getId()) { + return; + } + setSkillCast(skillId, casterId); + // Handle immediate stamina cost + Avatar currentAvatar = player.getTeamManager().getCurrentAvatarEntity().getAvatar(); + if (currentAvatar.getAvatarData().getWeaponType() == WeaponType.WEAPON_CLAYMORE) { + // Exclude claymore as their stamina cost starts when MixinStaminaCost gets in + return; + } + // TODO: Differentiate normal attacks from charged attacks and exclude + // TODO: Temporary: Exclude non-claymore attacks for now + /* + if (BowAvatars.contains(currentAvatarId) + || SwordAvatars.contains(currentAvatarId) + || PolearmAvatars.contains(currentAvatarId) + || CatalystAvatars.contains(currentAvatarId) + ) { + return; + } + */ + //handleImmediateStamina(session, skillId); + } + + public void handleMixinCostStamina(boolean isSwim) { + // Talent moving and claymore avatar charged attack duration + // logger.trace("abilityMixinCostStamina: isSwim: " + isSwim + "\tlastSkill: " + lastSkillId); + if (lastSkillCasterId == player.getTeamManager().getCurrentAvatarEntity().getId()) { + handleImmediateStamina(cachedSession, lastSkillId); + } + } + + public void handleCombatInvocationsNotify(@NotNull GameSession session, @NotNull EntityMoveInfo moveInfo, @NotNull GameEntity entity) { + // cache info for later use in SustainedStaminaHandler tick + cachedSession = session; + cachedEntity = entity; + MotionInfo motionInfo = moveInfo.getMotionInfo(); + MotionState motionState = motionInfo.getState(); + int notifyEntityId = entity.getId(); + int currentAvatarEntityId = session.getPlayer().getTeamManager().getCurrentAvatarEntity().getId(); + if (notifyEntityId != currentAvatarEntityId && notifyEntityId != vehicleId) { + return; + } + currentState = motionState; + // logger.trace(currentState + "\t" + (notifyEntityId == currentAvatarEntityId ? "character" : "vehicle")); + Vector posVector = motionInfo.getPos(); + Position newPos = new Position(posVector.getX(), posVector.getY(), posVector.getZ()); + if (newPos.getX() != 0 && newPos.getY() != 0 && newPos.getZ() != 0) { + currentCoordinates = newPos; + } + startSustainedStaminaHandler(); + handleImmediateStamina(session, motionState); + } + + public void handleVehicleInteractReq(GameSession session, int vehicleId, VehicleInteractType vehicleInteractType) { + if (vehicleInteractType == VehicleInteractType.VEHICLE_INTERACT_TYPE_IN) { + this.vehicleId = vehicleId; + // Reset character stamina here to prevent falling into water immediately on ejection if char stamina is + // close to empty when boarding. + updateStaminaAbsolute(session, "board vehicle", getMaxCharacterStamina(), true); + updateStaminaAbsolute(session, "board vehicle", getMaxVehicleStamina(), false); + } else { + this.vehicleId = -1; + } + } + + // Internal handler + + private void handleImmediateStamina(GameSession session, @NotNull MotionState motionState) { + switch (motionState) { + case MOTION_STATE_CLIMB: + if (currentState != MotionState.MOTION_STATE_CLIMB) { + updateStaminaRelative(session, new Consumption(ConsumptionType.CLIMB_START), true); + } + break; + case MOTION_STATE_DASH_BEFORE_SHAKE: + if (previousState != MotionState.MOTION_STATE_DASH_BEFORE_SHAKE) { + updateStaminaRelative(session, new Consumption(ConsumptionType.SPRINT), true); + } + break; + case MOTION_STATE_CLIMB_JUMP: + if (previousState != MotionState.MOTION_STATE_CLIMB_JUMP) { + updateStaminaRelative(session, new Consumption(ConsumptionType.CLIMB_JUMP), true); + } + break; + case MOTION_STATE_SWIM_DASH: + if (previousState != MotionState.MOTION_STATE_SWIM_DASH) { + updateStaminaRelative(session, new Consumption(ConsumptionType.SWIM_DASH_START), true); + } + break; + } + } + + private void handleImmediateStamina(GameSession session, int skillId) { + Consumption consumption = getFightConsumption(skillId); + updateStaminaRelative(session, consumption, true); + } + + private class SustainedStaminaHandler extends TimerTask { + public void run() { + boolean moving = isPlayerMoving(); + int currentCharacterStamina = getCurrentCharacterStamina(); + int maxCharacterStamina = getMaxCharacterStamina(); + int currentVehicleStamina = getCurrentVehicleStamina(); + int maxVehicleStamina = getMaxVehicleStamina(); + if (moving || (currentCharacterStamina < maxCharacterStamina) || (currentVehicleStamina < maxVehicleStamina)) { + logger.trace("Player moving: " + moving + ", stamina full: " + + (currentCharacterStamina >= maxCharacterStamina) + ", recalculate stamina"); + boolean isCharacterStamina = true; + Consumption consumption; + if (MotionStatesCategorized.get("CLIMB").contains(currentState)) { + consumption = getClimbConsumption(); + } else if (MotionStatesCategorized.get("DASH").contains(currentState)) { + consumption = getDashConsumption(); + } else if (MotionStatesCategorized.get("FLY").contains(currentState)) { + consumption = getFlyConsumption(); + } else if (MotionStatesCategorized.get("RUN").contains(currentState)) { + consumption = new Consumption(ConsumptionType.RUN); + } else if (MotionStatesCategorized.get("SKIFF").contains(currentState)) { + consumption = getSkiffConsumption(); + isCharacterStamina = false; + } else if (MotionStatesCategorized.get("STANDBY").contains(currentState)) { + consumption = new Consumption(ConsumptionType.STANDBY); + } else if (MotionStatesCategorized.get("SWIM").contains(currentState)) { + consumption = getSwimConsumptions(); + } else if (MotionStatesCategorized.get("WALK").contains(currentState)) { + consumption = new Consumption(ConsumptionType.WALK); + } else if (MotionStatesCategorized.get("NOCOST_NORECOVER").contains(currentState)) { + consumption = new Consumption(); + } else if (MotionStatesCategorized.get("OTHER").contains(currentState)) { + consumption = getOtherConsumptions(); + } else { // ignore + return; + } + + if (consumption.amount < 0 && isCharacterStamina) { + // Do not apply reduction factor when recovering stamina + if (player.getTeamManager().getTeamResonances().contains(10301)) { + consumption.amount *= 0.85f; + } + } + // Delay 1 seconds before starts recovering stamina + if (consumption.amount != 0 && cachedSession != null) { + if (consumption.amount < 0) { + staminaRecoverDelay = 0; + } + if (consumption.amount > 0 + && consumption.type != ConsumptionType.POWERED_FLY + && consumption.type != ConsumptionType.POWERED_SKIFF) { + // For POWERED_* recover immediately - things like Amber's gliding exam and skiff challenges may require this. + if (staminaRecoverDelay < 5) { + // For others recover after 1 seconds (5 ticks) - as official server does. + staminaRecoverDelay++; + consumption.amount = 0; + logger.trace("Delaying recovery: " + staminaRecoverDelay); + } + } + updateStaminaRelative(cachedSession, consumption, isCharacterStamina); + } + } + previousState = currentState; + previousCoordinates = new Position( + currentCoordinates.getX(), + currentCoordinates.getY(), + currentCoordinates.getZ() + ); + } + } + + private void handleDrowning() { + // TODO: fix drowning waverider entity + int stamina = getCurrentCharacterStamina(); + if (stamina < 10) { + logger.trace(getCurrentCharacterStamina() + "/" + + getMaxCharacterStamina() + "\t" + currentState); + if (currentState != MotionState.MOTION_STATE_SWIM_IDLE) { + killAvatar(cachedSession, cachedEntity, PlayerDieType.PLAYER_DIE_TYPE_DRAWN); + } + } + } + + // Consumption Calculators + + // Stamina Consumption Reduction: https://genshin-impact.fandom.com/wiki/Stamina + + private Consumption getFightConsumption(int skillCasting) { + // Talent moving + if (TalentMovements.contains(skillCasting)) { + // TODO: recover 1000 if kamisato hits an enemy at the end of dashing + return getTalentMovingSustainedCost(skillCasting); + } + // Bow avatar charged attack + Avatar currentAvatar = player.getTeamManager().getCurrentAvatarEntity().getAvatar(); + + switch (currentAvatar.getAvatarData().getWeaponType()) { + case WEAPON_BOW: + return getBowSustainedCost(skillCasting); + case WEAPON_CLAYMORE: + return getClaymoreSustainedCost(skillCasting); + case WEAPON_CATALYST: + return getCatalystCost(skillCasting); + case WEAPON_POLE: + return getPolearmCost(skillCasting); + case WEAPON_SWORD_ONE_HAND: + return getSwordCost(skillCasting); + } + + return new Consumption(); + } + + private Consumption getClimbConsumption() { + Consumption consumption = new Consumption(); + if (currentState == MotionState.MOTION_STATE_CLIMB && isPlayerMoving()) { + consumption.type = ConsumptionType.CLIMBING; + consumption.amount = ConsumptionType.CLIMBING.amount; + } + // Climbing specific reductions + consumption.amount *= getFoodCostReductionFactor(ClimbFoodReductionMap); + consumption.amount *= getTalentCostReductionFactor(ClimbTalentReductionMap); + return consumption; + } + + private Consumption getSwimConsumptions() { + handleDrowning(); + Consumption consumption = new Consumption(); + if (currentState == MotionState.MOTION_STATE_SWIM_MOVE) { + consumption.type = ConsumptionType.SWIMMING; + consumption.amount = ConsumptionType.SWIMMING.amount; + } + if (currentState == MotionState.MOTION_STATE_SWIM_DASH) { + consumption.type = ConsumptionType.SWIM_DASH; + consumption.amount = ConsumptionType.SWIM_DASH.amount; + } + // Swimming specific reductions + consumption.amount *= getFoodCostReductionFactor(SwimFoodReductionMap); + consumption.amount *= getTalentCostReductionFactor(SwimTalentReductionMap); + return consumption; + } + + private Consumption getDashConsumption() { + Consumption consumption = new Consumption(); + if (currentState == MotionState.MOTION_STATE_DASH) { + consumption.type = ConsumptionType.DASH; + consumption.amount = ConsumptionType.DASH.amount; + // Dashing specific reductions + consumption.amount *= getFoodCostReductionFactor(DashFoodReductionMap); + } + return consumption; + } + + private Consumption getFlyConsumption() { + // POWERED_FLY, e.g. wind tunnel + if (currentState == MotionState.MOTION_STATE_POWERED_FLY) { + return new Consumption(ConsumptionType.POWERED_FLY); + } + Consumption consumption = new Consumption(ConsumptionType.FLY); + // Flying specific reductions + consumption.amount *= getFoodCostReductionFactor(FlyFoodReductionMap); + consumption.amount *= getTalentCostReductionFactor(FlyTalentReductionMap); + return consumption; + } + + private Consumption getSkiffConsumption() { + // No known reduction for skiffing. + return switch (currentState) { + case MOTION_STATE_SKIFF_DASH -> new Consumption(ConsumptionType.SKIFF_DASH); + case MOTION_STATE_SKIFF_POWERED_DASH -> new Consumption(ConsumptionType.POWERED_SKIFF); + case MOTION_STATE_SKIFF_NORMAL -> new Consumption(ConsumptionType.SKIFF); + default -> new Consumption(); + }; + } + + private Consumption getOtherConsumptions() { + switch (currentState) { + case MOTION_STATE_NOTIFY: +// if (BowSkills.contains(lastSkillId)) { +// return new Consumption(ConsumptionType.FIGHT, 500); +// } + break; + case MOTION_STATE_FIGHT: + // TODO: what if charged attack + return new Consumption(ConsumptionType.FIGHT, 500); + } + + return new Consumption(); + } + + // Reduction getter + + private float getTalentCostReductionFactor(HashMap talentReductionMap) { + // All known talents reductions are not stackable + float reduction = 1; + for (EntityAvatar entity : cachedSession.getPlayer().getTeamManager().getActiveTeam()) { + for (int skillId : entity.getAvatar().getProudSkillList()) { + if (talentReductionMap.containsKey(skillId)) { + float potentialLowerReduction = talentReductionMap.get(skillId); + if (potentialLowerReduction < reduction) { + reduction = potentialLowerReduction; + } + } + } + } + return reduction; + } + + private float getFoodCostReductionFactor(HashMap foodReductionMap) { + // All known food reductions are not stackable + // TODO: Check consumed food (buff?) and return proper factor + float reduction = 1; + return reduction; + } + + private Consumption getTalentMovingSustainedCost(int skillId) { + if (lastSkillFirstTick) { + lastSkillFirstTick = false; + return new Consumption(ConsumptionType.TALENT_DASH, -1000); + } else { + return new Consumption(ConsumptionType.TALENT_DASH, -500); + } + } + + private Consumption getBowSustainedCost(int skillId) { + // Note that bow skills actually recovers stamina + // Character specific handling + // switch (skillId) { + // // No known bow skills cost stamina + // } + return new Consumption(ConsumptionType.FIGHT, +500); + } + + private Consumption getCatalystCost(int skillId) { + Consumption consumption = new Consumption(ConsumptionType.FIGHT, -5000); + // Character specific handling + switch (skillId) { + // TODO: + } + return consumption; + } + + private Consumption getClaymoreSustainedCost(int skillId) { + Consumption consumption = new Consumption(ConsumptionType.FIGHT, -1333); // 4000 / 3 = 1333 + // Character specific handling + switch (skillId) { + case 10571: + case 10532: + consumption.amount = 0; + break; + case 10160: + if (player.getTeamManager().getCurrentAvatarEntity().getAvatar().getProudSkillList().contains(162101)) { + consumption.amount /= 2; + } + break; + } + return consumption; + } + + private Consumption getPolearmCost(int skillId) { + Consumption consumption = new Consumption(ConsumptionType.FIGHT, -2500); + // Character specific handling + switch (skillId) { + // TODO: + } + return consumption; + } + + private Consumption getSwordCost(int skillId) { + Consumption consumption = new Consumption(ConsumptionType.FIGHT, -2000); + // Character specific handling + switch (skillId) { + case 10421: + consumption.amount = -2500; + break; + } + return consumption; + } +} diff --git a/src/main/java/emu/grasscutter/game/player/InvokeHandler.java b/src/main/java/emu/grasscutter/game/player/InvokeHandler.java index edfcbfc83..a266c7411 100644 --- a/src/main/java/emu/grasscutter/game/player/InvokeHandler.java +++ b/src/main/java/emu/grasscutter/game/player/InvokeHandler.java @@ -21,16 +21,16 @@ public class InvokeHandler { public synchronized void addEntry(ForwardType forward, T entry) { switch (forward) { - case FORWARD_TO_ALL -> entryListForwardAll.add(entry); - case FORWARD_TO_ALL_EXCEPT_CUR, FORWARD_TO_ALL_EXIST_EXCEPT_CUR -> entryListForwardAllExceptCur.add(entry); - case FORWARD_TO_HOST -> entryListForwardHost.add(entry); + case FORWARD_TYPE_TO_ALL -> entryListForwardAll.add(entry); + case FORWARD_TYPE_TO_ALL_EXCEPT_CUR, FORWARD_TYPE_TO_ALL_EXIST_EXCEPT_CUR -> entryListForwardAllExceptCur.add(entry); + case FORWARD_TYPE_TO_HOST -> entryListForwardHost.add(entry); default -> { } } } public synchronized void update(Player player) { - if (player.getWorld() == null) { + if (player.getWorld() == null || player.getScene() == null) { this.entryListForwardAll.clear(); this.entryListForwardAllExceptCur.clear(); this.entryListForwardHost.clear(); diff --git a/src/main/java/emu/grasscutter/game/player/Player.java b/src/main/java/emu/grasscutter/game/player/Player.java index 1eb5e3526..ac22900dd 100644 --- a/src/main/java/emu/grasscutter/game/player/Player.java +++ b/src/main/java/emu/grasscutter/game/player/Player.java @@ -4,13 +4,17 @@ import dev.morphia.annotations.*; import emu.grasscutter.GameConstants; import emu.grasscutter.Grasscutter; import emu.grasscutter.data.GameData; -import emu.grasscutter.data.def.PlayerLevelData; +import emu.grasscutter.data.excels.PlayerLevelData; import emu.grasscutter.database.DatabaseHelper; import emu.grasscutter.game.Account; import emu.grasscutter.game.CoopRequest; +import emu.grasscutter.game.ability.AbilityManager; import emu.grasscutter.game.avatar.Avatar; import emu.grasscutter.game.avatar.AvatarProfileData; import emu.grasscutter.game.avatar.AvatarStorage; +import emu.grasscutter.game.entity.EntityMonster; +import emu.grasscutter.game.entity.EntityVehicle; +import emu.grasscutter.game.managers.DeforestationManager.DeforestationManager; import emu.grasscutter.game.entity.EntityGadget; import emu.grasscutter.game.entity.EntityItem; import emu.grasscutter.game.entity.GameEntity; @@ -22,12 +26,15 @@ import emu.grasscutter.game.inventory.GameItem; import emu.grasscutter.game.inventory.Inventory; import emu.grasscutter.game.mail.Mail; import emu.grasscutter.game.mail.MailHandler; -import emu.grasscutter.game.managers.MovementManager.MovementManager; -import emu.grasscutter.game.managers.SotSManager.SotSManager; +import emu.grasscutter.game.managers.InsectCaptureManager; +import emu.grasscutter.game.managers.StaminaManager.StaminaManager; +import emu.grasscutter.game.managers.SotSManager; +import emu.grasscutter.game.managers.EnergyManager.EnergyManager; import emu.grasscutter.game.props.ActionReason; import emu.grasscutter.game.props.EntityType; import emu.grasscutter.game.props.PlayerProperty; import emu.grasscutter.game.props.SceneType; +import emu.grasscutter.game.quest.QuestManager; import emu.grasscutter.game.shop.ShopLimit; import emu.grasscutter.game.managers.MapMarkManager.*; import emu.grasscutter.game.tower.TowerManager; @@ -44,10 +51,12 @@ import emu.grasscutter.net.proto.OnlinePlayerInfoOuterClass.OnlinePlayerInfo; import emu.grasscutter.net.proto.PlayerLocationInfoOuterClass.PlayerLocationInfo; import emu.grasscutter.net.proto.ProfilePictureOuterClass.ProfilePicture; import emu.grasscutter.net.proto.SocialDetailOuterClass.SocialDetail; + import emu.grasscutter.server.event.player.PlayerJoinEvent; import emu.grasscutter.server.event.player.PlayerQuitEvent; import emu.grasscutter.server.game.GameServer; import emu.grasscutter.server.game.GameSession; +import emu.grasscutter.server.game.GameSession.SessionState; import emu.grasscutter.server.packet.send.*; import emu.grasscutter.utils.DateHelper; import emu.grasscutter.utils.Position; @@ -59,12 +68,11 @@ import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap; import java.util.*; import java.util.concurrent.LinkedBlockingQueue; +import static emu.grasscutter.Configuration.*; + @Entity(value = "players", useDiscriminator = false) public class Player { - @Transient private static int GlobalMaximumSpringVolume = 8500000; - @Transient private static int GlobalMaximumStamina = 24000; - @Id private int id; @Indexed(options = @IndexOptions(unique = true)) private String accountId; @@ -76,11 +84,18 @@ public class Player { private Position pos; private Position rotation; private PlayerBirthday birthday; + private PlayerCodex codex; private Map properties; private Set nameCardList; private Set flyCloakList; private Set costumeList; + private Set unlockedForgingBlueprints; + + private Integer widgetId; + + private Set realmList; + private Integer currentRealmId; @Transient private long nextGuid = 0; @Transient private int peerId; @@ -92,8 +107,11 @@ public class Player { @Transient private FriendsList friendsList; @Transient private MailHandler mailHandler; @Transient private MessageHandler messageHandler; - + @Transient private AbilityManager abilityManager; + @Transient private QuestManager questManager; + @Transient private SotSManager sotsManager; + @Transient private InsectCaptureManager insectCaptureManager; private TeamManager teamManager; @@ -111,6 +129,7 @@ public class Player { private int mainCharacterId; private boolean godmode; + private boolean stamina; private boolean moonCard; private Date moonCardStartTime; private int moonCardDuration; @@ -131,11 +150,13 @@ public class Player { @Transient private final InvokeHandler abilityInvokeHandler; @Transient private final InvokeHandler clientAbilityInitFinishHandler; - private MapMarksManager mapMarksManager; - @Transient private MovementManager movementManager; + @Transient private MapMarksManager mapMarksManager; + @Transient private StaminaManager staminaManager; + @Transient private EnergyManager energyManager; + @Transient private DeforestationManager deforestationManager; private long springLastUsed; - + private HashMap mapMarks; @Deprecated @SuppressWarnings({"rawtypes", "unchecked"}) // Morphia only! @@ -145,6 +166,11 @@ public class Player { this.friendsList = new FriendsList(this); this.mailHandler = new MailHandler(this); this.towerManager = new TowerManager(this); + this.abilityManager = new AbilityManager(this); + this.deforestationManager = new DeforestationManager(this); + this.insectCaptureManager = new InsectCaptureManager(this); + + this.setQuestManager(new QuestManager(this)); this.pos = new Position(); this.rotation = new Position(); this.properties = new HashMap<>(); @@ -159,6 +185,7 @@ public class Player { this.nameCardList = new HashSet<>(); this.flyCloakList = new HashSet<>(); this.costumeList = new HashSet<>(); + this.unlockedForgingBlueprints = new HashSet<>(); this.setSceneId(3); this.setRegionId(1); @@ -173,13 +200,15 @@ public class Player { this.birthday = new PlayerBirthday(); this.rewardedLevels = new HashSet<>(); this.moonCardGetTimes = new HashSet<>(); + this.codex = new PlayerCodex(this); this.shopLimit = new ArrayList<>(); this.expeditionInfo = new HashMap<>(); this.messageHandler = null; - this.mapMarksManager = new MapMarksManager(); - this.movementManager = new MovementManager(this); + this.mapMarksManager = new MapMarksManager(this); + this.staminaManager = new StaminaManager(this); this.sotsManager = new SotSManager(this); + this.energyManager = new EnergyManager(this); } // On player creation @@ -192,6 +221,7 @@ public class Player { this.signature = ""; this.teamManager = new TeamManager(this); this.birthday = new PlayerBirthday(); + this.codex = new PlayerCodex(this); this.setProperty(PlayerProperty.PROP_PLAYER_LEVEL, 1); this.setProperty(PlayerProperty.PROP_IS_SPRING_AUTO_USE, 1); this.setProperty(PlayerProperty.PROP_SPRING_AUTO_USE_PERCENT, 50); @@ -205,9 +235,11 @@ public class Player { this.getPos().set(GameConstants.START_POSITION); this.getRotation().set(0, 307, 0); this.messageHandler = null; - this.mapMarksManager = new MapMarksManager(); - this.movementManager = new MovementManager(this); + this.mapMarksManager = new MapMarksManager(this); + this.staminaManager = new StaminaManager(this); this.sotsManager = new SotSManager(this); + this.energyManager = new EnergyManager(this); + this.deforestationManager = new DeforestationManager(this); } public int getUid() { @@ -229,7 +261,6 @@ public class Player { public void setAccount(Account account) { this.account = account; - this.account.setPlayerId(getUid()); } public GameSession getSession() { @@ -295,6 +326,39 @@ public class Player { this.updateProfile(); } + public Integer getWidgetId() { + return widgetId; + } + + public void setWidgetId(Integer widgetId) { + this.widgetId = widgetId; + } + + public Set getRealmList() { + return realmList; + } + + public void setRealmList(Set realmList) { + this.realmList = realmList; + } + + public void addRealmList(int realmId) { + if (this.realmList == null) { + this.realmList = new HashSet<>(); + } else if (this.realmList.contains(realmId)) { + return; + } + this.realmList.add(realmId); + } + + public Integer getCurrentRealmId() { + return currentRealmId; + } + + public void setCurrentRealmId(Integer currentRealmId) { + this.currentRealmId = currentRealmId; + } + public Position getPos() { return pos; } @@ -353,7 +417,7 @@ public class Player { } private float getExpModifier() { - return Grasscutter.getConfig().getGameServerOptions().getGameRates().ADVENTURE_EXP_RATE; + return GAME_OPTIONS.rates.adventureExp; } // Affected by exp rate @@ -409,6 +473,14 @@ public class Player { return towerManager; } + public QuestManager getQuestManager() { + return questManager; + } + + public void setQuestManager(QuestManager questManager) { + this.questManager = questManager; + } + public PlayerGachaInfo getGachaInfo() { return gachaInfo; } @@ -446,8 +518,12 @@ public class Player { return this.nameCardList; } + public Set getUnlockedForgingBlueprints() { + return unlockedForgingBlueprints; + } + public MpSettingType getMpSetting() { - return MpSettingType.MP_SETTING_ENTER_AFTER_APPLY; // TEMP + return MpSettingType.MP_SETTING_TYPE_ENTER_AFTER_APPLY; // TEMP } public Queue getAttackResults() { @@ -699,7 +775,6 @@ public class Player { return expeditionInfo.get(avaterGuid); } - public List getShopLimit() { return shopLimit; } @@ -727,7 +802,14 @@ public class Player { } this.save(); } - + public boolean getStamina() { + // Get Stamina + return stamina; + } + public void setStamina(boolean stamina) { + // Set Stamina + this.stamina = stamina; + } public boolean inGodmode() { return godmode; } @@ -744,7 +826,7 @@ public class Player { this.hasSentAvatarDataNotify = hasSentAvatarDataNotify; } - public void addAvatar(Avatar avatar) { + public void addAvatar(Avatar avatar, boolean addToCurrentTeam) { boolean result = getAvatars().addAvatar(avatar); if (result) { @@ -755,14 +837,22 @@ public class Player { if (hasSentAvatarDataNotify()) { // Recalc stats avatar.recalcStats(); - // Packet - sendPacket(new PacketAvatarAddNotify(avatar, false)); + // Packet, show notice on left if the avatar will be added to the team + sendPacket(new PacketAvatarAddNotify(avatar, addToCurrentTeam && this.getTeamManager().canAddAvatarToCurrentTeam())); + if (addToCurrentTeam) { + // If space in team, add + this.getTeamManager().addAvatarToCurrentTeam(avatar); + } } } else { // Failed adding avatar } } + public void addAvatar(Avatar avatar) { + addAvatar(avatar, true); + } + public void addFlycloak(int flycloakId) { this.getFlyCloakList().add(flycloakId); this.sendPacket(new PacketAvatarGainFlycloakNotify(flycloakId)); @@ -830,62 +920,58 @@ public class Player { public void interactWith(int gadgetEntityId) { GameEntity entity = getScene().getEntityById(gadgetEntityId); - if (entity == null) { return; } // Handle - if (entity instanceof EntityItem) { + if (entity instanceof EntityItem drop) { // Pick item - EntityItem drop = (EntityItem) entity; if (!drop.isShare()) // check drop owner to avoid someone picked up item in others' world { int dropOwner = (int)(drop.getGuid() >> 32); - if (dropOwner != getUid()) + if (dropOwner != getUid()) { return; + } } entity.getScene().removeEntity(entity); GameItem item = new GameItem(drop.getItemData(), drop.getCount()); // Add to inventory boolean success = getInventory().addItem(item, ActionReason.SubfieldDrop); if (success) { - - if (!drop.isShare()) // not shared drop - this.sendPacket(new PacketGadgetInteractRsp(drop, InteractType.INTERACT_PICK_ITEM)); - else - this.getScene().broadcastPacket(new PacketGadgetInteractRsp(drop, InteractType.INTERACT_PICK_ITEM)); + if (!drop.isShare()) { // not shared drop + this.sendPacket(new PacketGadgetInteractRsp(drop, InteractType.INTERACT_TYPE_PICK_ITEM)); + }else{ + this.getScene().broadcastPacket(new PacketGadgetInteractRsp(drop, InteractType.INTERACT_TYPE_PICK_ITEM)); + } } - } else if (entity instanceof EntityGadget) { - EntityGadget gadget = (EntityGadget) entity; - + } else if (entity instanceof EntityGadget gadget) { if (gadget.getGadgetData().getType() == EntityType.RewardStatue) { if (scene.getChallenge() != null) { scene.getChallenge().getStatueDrops(this); } - - this.sendPacket(new PacketGadgetInteractRsp(gadget, InteractType.INTERACT_OPEN_STATUE)); + this.sendPacket(new PacketGadgetInteractRsp(gadget, InteractType.INTERACT_TYPE_OPEN_STATUE)); } + } else if (entity instanceof EntityMonster monster) { + insectCaptureManager.arrestSmallCreature(monster); + } else if (entity instanceof EntityVehicle vehicle) {// try to arrest it, example: glowworm + insectCaptureManager.arrestSmallCreature(vehicle); } else { // Delete directly entity.getScene().removeEntity(entity); } - - return; } public void onPause() { - + getStaminaManager().stopSustainedStaminaHandler(); } public void onUnpause() { - + getStaminaManager().startSustainedStaminaHandler(); } public void sendPacket(BasePacket packet) { - if (this.hasSentAvatarDataNotify) { - this.getSession().send(packet); - } + this.getSession().send(packet); } public OnlinePlayerInfo getOnlinePlayerInfo() { @@ -920,6 +1006,8 @@ public class Player { return this.birthday.getDay() > 0; } + public PlayerCodex getCodex(){ return this.codex; } + public Set getRewardedLevels() { return rewardedLevels; } @@ -944,8 +1032,8 @@ public class Player { } } } else { - List showAvatarList = DatabaseHelper.getPlayerById(id).getShowAvatarList(); - AvatarStorage avatars = DatabaseHelper.getPlayerById(id).getAvatars(); + List showAvatarList = DatabaseHelper.getPlayerByUid(id).getShowAvatarList(); + AvatarStorage avatars = DatabaseHelper.getPlayerByUid(id).getAvatars(); avatars.loadFromDatabase(); if (showAvatarList != null) { for (int avatarId : showAvatarList) { @@ -985,7 +1073,7 @@ public class Player { player = this; shouldRecalc = false; } else { - player = DatabaseHelper.getPlayerById(id); + player = DatabaseHelper.getPlayerByUid(id); player.getAvatars().loadFromDatabase(); player.getInventory().loadFromDatabase(); shouldRecalc = true; @@ -1024,10 +1112,26 @@ public class Player { return mapMarksManager; } - public MovementManager getMovementManager() { return movementManager; } + public StaminaManager getStaminaManager() { return staminaManager; } public SotSManager getSotSManager() { return sotsManager; } + public EnergyManager getEnergyManager() { + return this.energyManager; + } + + public AbilityManager getAbilityManager() { + return abilityManager; + } + + public DeforestationManager getDeforestationManager() { + return deforestationManager; + } + + public HashMap getMapMarks() { return mapMarks; } + + public void setMapMarks(HashMap newMarks) { mapMarks = newMarks; } + public synchronized void onTick() { // Check ping if (this.getLastPingTime() > System.currentTimeMillis() + 60000) { @@ -1039,7 +1143,10 @@ public class Player { while (it.hasNext()) { CoopRequest req = it.next(); if (req.isExpired()) { - req.getRequester().sendPacket(new PacketPlayerApplyEnterMpResultNotify(this, false, PlayerApplyEnterMpResultNotifyOuterClass.PlayerApplyEnterMpResultNotify.Reason.SYSTEM_JUDGE)); + req.getRequester().sendPacket(new PacketPlayerApplyEnterMpResultNotify( + this, + false, + PlayerApplyEnterMpResultNotifyOuterClass.PlayerApplyEnterMpResultNotify.Reason.REASON_SYSTEM_JUDGE)); it.remove(); } } @@ -1083,6 +1190,7 @@ public class Player { @PostLoad private void onLoad() { + this.getCodex().setPlayer(this); this.getTeamManager().setPlayer(this); this.getTowerManager().setPlayer(this); } @@ -1090,23 +1198,20 @@ public class Player { public void save() { DatabaseHelper.savePlayer(this); } - - public void onLogin() { + + // Called from tokenrsp + public void loadFromDatabase() { // Make sure these exist if (this.getTeamManager() == null) { this.teamManager = new TeamManager(this); } + if (this.getCodex() == null) { + this.codex = new PlayerCodex(this); + } if (this.getProfile().getUid() == 0) { this.getProfile().syncWithCharacter(this); } - // Check if player object exists in server - // TODO - optimize - Player exists = this.getServer().getPlayerByUid(getUid()); - if (exists != null) { - exists.getSession().close(); - } - // Load from db this.getAvatars().loadFromDatabase(); this.getInventory().loadFromDatabase(); @@ -1114,17 +1219,36 @@ public class Player { this.getFriendsList().loadFromDatabase(); this.getMailHandler().loadFromDatabase(); + this.getQuestManager().loadFromDatabase(); - // Create world - World world = new World(this); - world.addPlayer(this); - - // Add to gameserver + // Add to gameserver (Always handle last) if (getSession().isActive()) { getServer().registerPlayer(this); getProfile().setPlayer(this); // Set online } + } + + public void onLogin() { + // Quest - Commented out because a problem is caused if you log out while this quest is active + /* + if (getQuestManager().getMainQuestById(351) == null) { + GameQuest quest = getQuestManager().addQuest(35104); + if (quest != null) { + quest.finish(); + } + + getQuestManager().addQuest(35101); + + this.setSceneId(3); + this.getPos().set(GameConstants.START_POSITION); + } + */ + + // Create world + World world = new World(this); + world.addPlayer(this); + // Multiplayer setting this.setProperty(PlayerProperty.PROP_PLAYER_MP_SETTING_TYPE, this.getMpSetting().getNumber()); this.setProperty(PlayerProperty.PROP_IS_MP_MODE_AVAILABLE, 1); @@ -1134,6 +1258,14 @@ public class Player { session.send(new PacketStoreWeightLimitNotify()); session.send(new PacketPlayerStoreNotify(this)); session.send(new PacketAvatarDataNotify(this)); + session.send(new PacketFinishedParentQuestNotify(this)); + session.send(new PacketQuestListNotify(this)); + session.send(new PacketCodexDataFullNotify(this)); + session.send(new PacketAllWidgetDataNotify(this)); + session.send(new PacketWidgetGadgetAllDataNotify()); + session.send(new PacketPlayerHomeCompInfoNotify(this)); + session.send(new PacketHomeComfortInfoNotify(this)); + session.send(new PacketForgeDataNotify(this)); getTodayMoonCard(); // The timer works at 0:0, some users log in after that, use this method to check if they have received a reward today or not. If not, send the reward. @@ -1143,6 +1275,9 @@ public class Player { // First notify packets sent this.setHasSentAvatarDataNotify(true); + + // Set session state + session.setState(SessionState.ACTIVE); // Call join event. PlayerJoinEvent event = new PlayerJoinEvent(this); event.call(); @@ -1151,31 +1286,48 @@ public class Player { } public void onLogout() { - // stop stamina calculation - getMovementManager().resetTimer(); + try{ + // stop stamina calculation + getStaminaManager().stopSustainedStaminaHandler(); - // force to leave the dungeon - if (getScene().getSceneType() == SceneType.SCENE_DUNGEON) { + // force to leave the dungeon (inside has a "if") this.getServer().getDungeonManager().exitDungeon(this); + + // Leave world + if (this.getWorld() != null) { + this.getWorld().removePlayer(this); + } + + // Status stuff + this.getProfile().syncWithCharacter(this); + this.getProfile().setPlayer(null); // Set offline + + this.getCoopRequests().clear(); + + // Save to db + this.save(); + this.getTeamManager().saveAvatars(); + this.getFriendsList().save(); + + // Call quit event. + PlayerQuitEvent event = new PlayerQuitEvent(this); event.call(); + + //reset wood + getDeforestationManager().resetWood(); + + }catch (Throwable e){ + e.printStackTrace(); + Grasscutter.getLogger().warn("Player (UID {}) save failure", getUid()); + }finally { + removeFromServer(); } - // Leave world - if (this.getWorld() != null) { - this.getWorld().removePlayer(this); - } + } - // Status stuff - this.getProfile().syncWithCharacter(this); - this.getProfile().setPlayer(null); // Set offline - - this.getCoopRequests().clear(); - - // Save to db - this.save(); - this.getTeamManager().saveAvatars(); - this.getFriendsList().save(); - - // Call quit event. - PlayerQuitEvent event = new PlayerQuitEvent(this); event.call(); + public void removeFromServer() { + // Remove from server. + //Note: DON'T DELETE BY UID,BECAUSE THERE ARE MULTIPLE SAME UID PLAYERS WHEN DUPLICATED LOGIN! + //so I decide to delete by object rather than uid + getServer().getPlayers().values().removeIf(player1 -> player1 == this); } public enum SceneLoadState { @@ -1214,7 +1366,7 @@ public class Player { } else if (prop == PlayerProperty.PROP_LAST_CHANGE_AVATAR_TIME) { // 10001 // TODO: implement sanity check } else if (prop == PlayerProperty.PROP_MAX_SPRING_VOLUME) { // 10002 - if (!(value >= 0 && value <= GlobalMaximumSpringVolume)) { return false; } + if (!(value >= 0 && value <= SotSManager.GlobalMaximumSpringVolume)) { return false; } } else if (prop == PlayerProperty.PROP_CUR_SPRING_VOLUME) { // 10003 int playerMaximumSpringVolume = getProperty(PlayerProperty.PROP_MAX_SPRING_VOLUME); if (!(value >= 0 && value <= playerMaximumSpringVolume)) { return false; } @@ -1231,7 +1383,7 @@ public class Player { } else if (prop == PlayerProperty.PROP_IS_TRANSFERABLE) { // 10009 if (!(0 <= value && value <= 1)) { return false; } } else if (prop == PlayerProperty.PROP_MAX_STAMINA) { // 10010 - if (!(value >= 0 && value <= GlobalMaximumStamina)) { return false; } + if (!(value >= 0 && value <= StaminaManager.GlobalCharacterMaximumStamina)) { return false; } } else if (prop == PlayerProperty.PROP_CUR_PERSIST_STAMINA) { // 10011 int playerMaximumStamina = getProperty(PlayerProperty.PROP_MAX_STAMINA); if (!(value >= 0 && value <= playerMaximumStamina)) { return false; } @@ -1242,7 +1394,7 @@ public class Player { } else if (prop == PlayerProperty.PROP_PLAYER_EXP) { // 10014 if (!(0 <= value)) { return false; } } else if (prop == PlayerProperty.PROP_PLAYER_HCOIN) { // 10015 - // see 10015 + // see PlayerProperty.PROP_PLAYER_HCOIN comments } else if (prop == PlayerProperty.PROP_PLAYER_SCOIN) { // 10016 // See 10015 } else if (prop == PlayerProperty.PROP_PLAYER_MP_SETTING_TYPE) { // 10017 diff --git a/src/main/java/emu/grasscutter/game/player/PlayerCodex.java b/src/main/java/emu/grasscutter/game/player/PlayerCodex.java new file mode 100644 index 000000000..191fc4112 --- /dev/null +++ b/src/main/java/emu/grasscutter/game/player/PlayerCodex.java @@ -0,0 +1,165 @@ +package emu.grasscutter.game.player; + +import dev.morphia.annotations.Entity; +import dev.morphia.annotations.Transient; +import emu.grasscutter.data.GameData; +import emu.grasscutter.data.excels.CodexAnimalData; +import emu.grasscutter.data.excels.CodexReliquaryData; +import emu.grasscutter.game.entity.EntityMonster; +import emu.grasscutter.game.entity.GameEntity; +import emu.grasscutter.game.inventory.GameItem; +import emu.grasscutter.game.inventory.ItemType; +import emu.grasscutter.game.inventory.MaterialType; +import emu.grasscutter.server.packet.send.PacketCodexDataUpdateNotify; + +import java.util.*; + +@Entity +public class PlayerCodex { + @Transient private Player player; + + //itemId is not codexId! + private Set unlockedWeapon; + private Map unlockedAnimal; + private Set unlockedMaterial; + private Set unlockedBook; + private Set unlockedTip; + private Set unlockedView; + private Set unlockedReliquary; + private Set unlockedReliquarySuitCodex; + + public PlayerCodex(){ + this.unlockedWeapon = new HashSet<>(); + this.unlockedAnimal = new HashMap<>(); + this.unlockedMaterial = new HashSet<>(); + this.unlockedBook = new HashSet<>(); + this.unlockedTip = new HashSet<>(); + this.unlockedView = new HashSet<>(); + this.unlockedReliquary = new HashSet<>(); + this.unlockedReliquarySuitCodex = new HashSet<>(); + } + + public PlayerCodex(Player player){ + this(); + this.player = player; + } + + public void setPlayer(Player player) { + this.player = player; + } + + public void checkAddedItem(GameItem item){ + ItemType type = item.getItemData().getItemType(); + if (type == ItemType.ITEM_WEAPON){ + if(!getUnlockedWeapon().contains(item.getItemId())){ + getUnlockedWeapon().add(item.getItemId()); + var codexItem = GameData.getCodexWeaponDataIdMap().get(item.getItemId()); + if(codexItem != null){ + player.save(); + this.player.sendPacket(new PacketCodexDataUpdateNotify(2, codexItem.getId())); + } + } + } + else if(type == ItemType.ITEM_MATERIAL){ + if( item.getItemData().getMaterialType() == MaterialType.MATERIAL_FOOD || + item.getItemData().getMaterialType() == MaterialType.MATERIAL_WIDGET|| + item.getItemData().getMaterialType() == MaterialType.MATERIAL_EXCHANGE|| + item.getItemData().getMaterialType() == MaterialType.MATERIAL_AVATAR_MATERIAL|| + item.getItemData().getMaterialType() == MaterialType.MATERIAL_NOTICE_ADD_HP){ + if (!getUnlockedMaterial().contains(item.getItemId())) { + var codexMaterial = GameData.getCodexMaterialDataIdMap().get(item.getItemId()); + if (codexMaterial != null) { + getUnlockedMaterial().add(item.getItemId()); + player.save(); + this.player.sendPacket(new PacketCodexDataUpdateNotify(4, codexMaterial.getId())); + } + } + } + } + else if(type == ItemType.ITEM_RELIQUARY) { + if(!getUnlockedReliquary().contains(item.getItemId())){ + getUnlockedReliquary().add(item.getItemId()); + checkUnlockedSuits(item); + } + } + } + + public void checkAnimal(GameEntity target, CodexAnimalData.CodexAnimalUnlockCondition condition){ + if(target instanceof EntityMonster){ + var monsterId = ((EntityMonster)target).getMonsterData().getId(); + var codexAnimal = GameData.getCodexAnimalDataMap().get(monsterId); + + if(!getUnlockedAnimal().containsKey(monsterId)) { + if (codexAnimal != null) { + if(codexAnimal.getUnlockCondition() == condition || codexAnimal.getUnlockCondition() == null){ + getUnlockedAnimal().put(monsterId, 1); + } + } + }else{ + getUnlockedAnimal().put(monsterId, getUnlockedAnimal().get(monsterId) + 1); + } + player.save(); + this.player.sendPacket(new PacketCodexDataUpdateNotify(3, monsterId)); + } + } + + public void checkUnlockedSuits(GameItem item){ + int reliquaryId = item.getItemId(); + Optional excelReliquarySuitList = GameData.getcodexReliquaryArrayList().stream().filter( + x -> x.getCupId() == reliquaryId + || x.getLeatherId() == reliquaryId + || x.getCapId() == reliquaryId + || x.getFlowerId() == reliquaryId + || x.getSandId() == reliquaryId + ).findFirst(); + if(excelReliquarySuitList.isPresent()) { + var excelReliquarySuit = excelReliquarySuitList.get(); + if(!getUnlockedReliquarySuitCodex().contains(excelReliquarySuit.getId())){ + if( + getUnlockedReliquary().contains(excelReliquarySuit.getCupId()) && + getUnlockedReliquary().contains(excelReliquarySuit.getLeatherId()) && + getUnlockedReliquary().contains(excelReliquarySuit.getCapId()) && + getUnlockedReliquary().contains(excelReliquarySuit.getFlowerId()) && + getUnlockedReliquary().contains(excelReliquarySuit.getSandId()) + ){ + getUnlockedReliquarySuitCodex().add(excelReliquarySuit.getId()); + player.save(); + this.player.sendPacket(new PacketCodexDataUpdateNotify(8, excelReliquarySuit.getId())); + } + } + } + } + + public Set getUnlockedWeapon() { + return unlockedWeapon; + } + + public Map getUnlockedAnimal() { + return unlockedAnimal; + } + + public Set getUnlockedMaterial() { + return unlockedMaterial; + } + + public Set getUnlockedBook() { + return unlockedBook; + } + + public Set getUnlockedTip() { + return unlockedTip; + } + + public Set getUnlockedView() { + return unlockedView; + } + + public Set getUnlockedReliquary() { + return unlockedReliquary; + } + + public Set getUnlockedReliquarySuitCodex() { + return unlockedReliquarySuitCodex; + } + +} \ No newline at end of file diff --git a/src/main/java/emu/grasscutter/game/player/TeamInfo.java b/src/main/java/emu/grasscutter/game/player/TeamInfo.java index 5794a7913..89383e486 100644 --- a/src/main/java/emu/grasscutter/game/player/TeamInfo.java +++ b/src/main/java/emu/grasscutter/game/player/TeamInfo.java @@ -4,10 +4,10 @@ import java.util.ArrayList; import java.util.List; import dev.morphia.annotations.Entity; -import emu.grasscutter.GameConstants; -import emu.grasscutter.Grasscutter; import emu.grasscutter.game.avatar.Avatar; +import static emu.grasscutter.Configuration.*; + @Entity public class TeamInfo { private String name; @@ -15,7 +15,7 @@ public class TeamInfo { public TeamInfo() { this.name = ""; - this.avatars = new ArrayList<>(Grasscutter.getConfig().getGameServerOptions().MaxAvatarsInTeam); + this.avatars = new ArrayList<>(GAME_OPTIONS.avatarLimits.singlePlayerTeam); } public TeamInfo(List avatars) { @@ -44,7 +44,7 @@ public class TeamInfo { } public boolean addAvatar(Avatar avatar) { - if (size() >= Grasscutter.getConfig().getGameServerOptions().MaxAvatarsInTeam || contains(avatar)) { + if (contains(avatar)) { return false; } @@ -64,7 +64,7 @@ public class TeamInfo { } public void copyFrom(TeamInfo team) { - copyFrom(team, Grasscutter.getConfig().getGameServerOptions().MaxAvatarsInTeam); + copyFrom(team, GAME_OPTIONS.avatarLimits.singlePlayerTeam); } public void copyFrom(TeamInfo team, int maxTeamSize) { diff --git a/src/main/java/emu/grasscutter/game/player/TeamManager.java b/src/main/java/emu/grasscutter/game/player/TeamManager.java index 16e8942ad..f969e694e 100644 --- a/src/main/java/emu/grasscutter/game/player/TeamManager.java +++ b/src/main/java/emu/grasscutter/game/player/TeamManager.java @@ -5,8 +5,7 @@ import java.util.*; import dev.morphia.annotations.Entity; import dev.morphia.annotations.Transient; import emu.grasscutter.GameConstants; -import emu.grasscutter.Grasscutter; -import emu.grasscutter.data.def.AvatarSkillDepotData; +import emu.grasscutter.data.excels.AvatarSkillDepotData; import emu.grasscutter.game.avatar.Avatar; import emu.grasscutter.game.entity.EntityAvatar; import emu.grasscutter.game.entity.EntityBaseGadget; @@ -39,6 +38,8 @@ import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap; import it.unimi.dsi.fastutil.ints.IntOpenHashSet; import it.unimi.dsi.fastutil.ints.IntSet; +import static emu.grasscutter.Configuration.*; + @Entity public class TeamManager { @Transient private Player player; @@ -103,6 +104,20 @@ public class TeamManager { this.mpTeam = mpTeam; } + /** + * Search through all teams and if the team matches, return that index. + * Otherwise, return -1. + * No match could mean that the team does not currently belong to the player. + */ + public int getTeamId(TeamInfo team) { + for (int i = 1; i <= this.teams.size(); i++) { + if (this.teams.get(i).equals(team)) { + return i; + } + } + return -1; + } + public int getCurrentTeamId() { // Starts from 1 return currentTeamIndex; @@ -173,17 +188,119 @@ public class TeamManager { public int getMaxTeamSize() { if (getPlayer().isInMultiplayer()) { - int max = Grasscutter.getConfig().getGameServerOptions().MaxAvatarsInTeamMultiplayer; + int max = GAME_OPTIONS.avatarLimits.multiplayerTeam; if (getPlayer().getWorld().getHost() == this.getPlayer()) { return Math.max(1, (int) Math.ceil(max / (double) getWorld().getPlayerCount())); } return Math.max(1, (int) Math.floor(max / (double) getWorld().getPlayerCount())); } - return Grasscutter.getConfig().getGameServerOptions().MaxAvatarsInTeam; + + return GAME_OPTIONS.avatarLimits.singlePlayerTeam; } // Methods - + + /** + * Returns true if there is space to add the number of avatars to the team. + */ + public boolean canAddAvatarsToTeam(TeamInfo team, int avatars) { + return team.size() + avatars <= getMaxTeamSize(); + } + + /** + * Returns true if there is space to add to the team. + */ + public boolean canAddAvatarToTeam(TeamInfo team) { + return canAddAvatarsToTeam(team, 1); + } + + /** + * Returns true if there is space to add the number of avatars to the current team. + * If the current team is temporary, returns false. + */ + public boolean canAddAvatarsToCurrentTeam(int avatars) { + if (this.useTemporarilyTeamIndex != -1){ + return false; + } + return canAddAvatarsToTeam(this.getCurrentTeamInfo(), avatars); + } + + /** + * Returns true if there is space to add to the current team. + * If the current team is temporary, returns false. + */ + public boolean canAddAvatarToCurrentTeam() { + return canAddAvatarsToCurrentTeam(1); + } + + /** + * Try to add the collection of avatars to the team. + * Returns true if all were successfully added. + * If some can not be added, returns false and does not add any. + */ + public boolean addAvatarsToTeam(TeamInfo team, Collection avatars) { + if (!canAddAvatarsToTeam(team, avatars.size())) { + return false; + } + + // Convert avatars into a collection of avatar IDs, then add + team.getAvatars().addAll(avatars.stream().map(a -> a.getAvatarId()).toList()); + + // Update team + if (this.getPlayer().isInMultiplayer()) { + if (team.equals(this.getMpTeam())) { + // MP team Packet + this.updateTeamEntities(new PacketChangeMpTeamAvatarRsp(getPlayer(), team)); + } + } else { + // SP team update packet + getPlayer().sendPacket(new PacketAvatarTeamUpdateNotify(getPlayer())); + + int teamId = this.getTeamId(team); + if (teamId != -1) { + // This is one of the player's teams + // Update entites + if (teamId == this.getCurrentTeamId()) { + this.updateTeamEntities(new PacketSetUpAvatarTeamRsp(getPlayer(), teamId, team)); + } else { + getPlayer().sendPacket(new PacketSetUpAvatarTeamRsp(getPlayer(), teamId, team)); + } + } + } + + return true; + } + + /** + * Try to add an avatar to a team. + * Returns true if successful. + */ + public boolean addAvatarToTeam(TeamInfo team, Avatar avatar){ + return addAvatarsToTeam(team, Collections.singleton(avatar)); + } + + /** + * Try to add the collection of avatars to the current team. + * Will not modify a temporary team. + * Returns true if all were successfully added. + * If some can not be added, returns false and does not add any. + */ + public boolean addAvatarsToCurrentTeam(Collection avatars) { + if (this.useTemporarilyTeamIndex != -1){ + return false; + } + return addAvatarsToTeam(this.getCurrentTeamInfo(), avatars); + } + + /** + * Try to add an avatar to the current team. + * Will not modify a temporary team. + * Returns true if successful. + */ + public boolean addAvatarToCurrentTeam(Avatar avatar) { + return addAvatarsToCurrentTeam(Collections.singleton(avatar)); + } + private void updateTeamResonances() { Int2IntOpenHashMap map = new Int2IntOpenHashMap(); @@ -235,7 +352,7 @@ public class TeamManager { // Add back entities into team for (int i = 0; i < this.getCurrentTeamInfo().getAvatars().size(); i++) { int avatarId = this.getCurrentTeamInfo().getAvatars().get(i); - EntityAvatar entity = null; + EntityAvatar entity; if (existingAvatars.containsKey(avatarId)) { entity = existingAvatars.get(avatarId); @@ -302,8 +419,8 @@ public class TeamManager { // Set team data LinkedHashSet newTeam = new LinkedHashSet<>(); - for (int i = 0; i < list.size(); i++) { - Avatar avatar = getPlayer().getAvatars().getAvatarByGuid(list.get(i)); + for (Long aLong : list) { + Avatar avatar = getPlayer().getAvatars().getAvatarByGuid(aLong); if (avatar == null || newTeam.contains(avatar)) { // Should never happen return; @@ -313,19 +430,7 @@ public class TeamManager { // Clear current team info and add avatars from our new team teamInfo.getAvatars().clear(); - for (Avatar avatar : newTeam) { - teamInfo.addAvatar(avatar); - } - - // Update packet - getPlayer().sendPacket(new PacketAvatarTeamUpdateNotify(getPlayer())); - - // Update entites - if (teamId == this.getCurrentTeamId()) { - this.updateTeamEntities(new PacketSetUpAvatarTeamRsp(getPlayer(), teamId, teamInfo)); - } else { - getPlayer().sendPacket(new PacketSetUpAvatarTeamRsp(getPlayer(), teamId, teamInfo)); - } + this.addAvatarsToTeam(teamInfo, newTeam); } public void setupMpTeam(List list) { @@ -338,8 +443,8 @@ public class TeamManager { // Set team data LinkedHashSet newTeam = new LinkedHashSet<>(); - for (int i = 0; i < list.size(); i++) { - Avatar avatar = getPlayer().getAvatars().getAvatarByGuid(list.get(i)); + for (Long aLong : list) { + Avatar avatar = getPlayer().getAvatars().getAvatarByGuid(aLong); if (avatar == null || newTeam.contains(avatar)) { // Should never happen return; @@ -349,16 +454,11 @@ public class TeamManager { // Clear current team info and add avatars from our new team teamInfo.getAvatars().clear(); - for (Avatar avatar : newTeam) { - teamInfo.addAvatar(avatar); - } - - // Packet - this.updateTeamEntities(new PacketChangeMpTeamAvatarRsp(getPlayer(), teamInfo)); + this.addAvatarsToTeam(teamInfo, newTeam); } public void setupTemporaryTeam(List> guidList) { - var team = guidList.stream().map(list -> { + this.temporaryTeam = guidList.stream().map(list -> { // Sanity checks if (list.size() == 0 || list.size() > getMaxTeamSize()) { return null; @@ -366,8 +466,8 @@ public class TeamManager { // Set team data LinkedHashSet newTeam = new LinkedHashSet<>(); - for (int i = 0; i < list.size(); i++) { - Avatar avatar = getPlayer().getAvatars().getAvatarByGuid(list.get(i)); + for (Long aLong : list) { + Avatar avatar = getPlayer().getAvatars().getAvatarByGuid(aLong); if (avatar == null || newTeam.contains(avatar)) { // Should never happen return null; @@ -383,7 +483,6 @@ public class TeamManager { .filter(Objects::nonNull) .map(TeamInfo::new) .toList(); - this.temporaryTeam = team; } public void useTemporaryTeam(int index) { @@ -455,7 +554,7 @@ public class TeamManager { this.setCurrentCharacterIndex(index); // Old entity motion state - oldEntity.setMotionState(MotionState.MOTION_STANDBY); + oldEntity.setMotionState(MotionState.MOTION_STATE_STANDBY); // Remove and Add getPlayer().getScene().replaceEntity(oldEntity, newEntity); @@ -472,7 +571,7 @@ public class TeamManager { PlayerDieType dieType = deadAvatar.getKilledType(); int killedBy = deadAvatar.getKilledBy(); - if (dieType == PlayerDieType.PLAYER_DIE_DRAWN) { + if (dieType == PlayerDieType.PLAYER_DIE_TYPE_DRAWN) { // Died in water. Do not replace // The official server has skipped this notify and will just respawn the team immediately after the animation. // TODO: Perhaps find a way to get vanilla experience? @@ -557,7 +656,7 @@ public class TeamManager { // return; // } // } - player.getMovementManager().resetTimer(); // prevent drowning immediately after respawn + player.getStaminaManager().stopSustainedStaminaHandler(); // prevent drowning immediately after respawn // Revive all team members for (EntityAvatar entity : getActiveTeam()) { @@ -570,7 +669,7 @@ public class TeamManager { } // Teleport player - getPlayer().sendPacket(new PacketPlayerEnterSceneNotify(getPlayer(), EnterType.ENTER_SELF, EnterReason.Revival, 3, GameConstants.START_POSITION)); + getPlayer().sendPacket(new PacketPlayerEnterSceneNotify(getPlayer(), EnterType.ENTER_TYPE_SELF, EnterReason.Revival, 3, GameConstants.START_POSITION)); // Set player position player.setSceneId(3); diff --git a/src/main/java/emu/grasscutter/game/props/ElementType.java b/src/main/java/emu/grasscutter/game/props/ElementType.java index 23362c39f..12a30f6fc 100644 --- a/src/main/java/emu/grasscutter/game/props/ElementType.java +++ b/src/main/java/emu/grasscutter/game/props/ElementType.java @@ -9,21 +9,22 @@ import it.unimi.dsi.fastutil.ints.Int2ObjectMap; import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap; public enum ElementType { - None (0, FightProperty.FIGHT_PROP_MAX_FIRE_ENERGY), - Fire (1, FightProperty.FIGHT_PROP_MAX_FIRE_ENERGY, 10101, "TeamResonance_Fire_Lv2"), - Water (2, FightProperty.FIGHT_PROP_MAX_WATER_ENERGY, 10201, "TeamResonance_Water_Lv2"), - Grass (3, FightProperty.FIGHT_PROP_MAX_GRASS_ENERGY), - Electric (4, FightProperty.FIGHT_PROP_MAX_ELEC_ENERGY, 10401, "TeamResonance_Electric_Lv2"), - Ice (5, FightProperty.FIGHT_PROP_MAX_ICE_ENERGY, 10601, "TeamResonance_Ice_Lv2"), - Frozen (6, FightProperty.FIGHT_PROP_MAX_ICE_ENERGY), - Wind (7, FightProperty.FIGHT_PROP_MAX_WIND_ENERGY, 10301, "TeamResonance_Wind_Lv2"), - Rock (8, FightProperty.FIGHT_PROP_MAX_ROCK_ENERGY, 10701, "TeamResonance_Rock_Lv2"), - AntiFire (9, FightProperty.FIGHT_PROP_MAX_FIRE_ENERGY), - Default (255, FightProperty.FIGHT_PROP_MAX_FIRE_ENERGY, 10801, "TeamResonance_AllDifferent"); + None (0, FightProperty.FIGHT_PROP_CUR_FIRE_ENERGY, FightProperty.FIGHT_PROP_MAX_FIRE_ENERGY), + Fire (1, FightProperty.FIGHT_PROP_CUR_FIRE_ENERGY, FightProperty.FIGHT_PROP_MAX_FIRE_ENERGY, 10101, "TeamResonance_Fire_Lv2"), + Water (2, FightProperty.FIGHT_PROP_CUR_WATER_ENERGY, FightProperty.FIGHT_PROP_MAX_WATER_ENERGY, 10201, "TeamResonance_Water_Lv2"), + Grass (3, FightProperty.FIGHT_PROP_CUR_GRASS_ENERGY, FightProperty.FIGHT_PROP_MAX_GRASS_ENERGY), + Electric (4, FightProperty.FIGHT_PROP_CUR_ELEC_ENERGY, FightProperty.FIGHT_PROP_MAX_ELEC_ENERGY, 10401, "TeamResonance_Electric_Lv2"), + Ice (5, FightProperty.FIGHT_PROP_CUR_ICE_ENERGY, FightProperty.FIGHT_PROP_MAX_ICE_ENERGY, 10601, "TeamResonance_Ice_Lv2"), + Frozen (6, FightProperty.FIGHT_PROP_CUR_ICE_ENERGY, FightProperty.FIGHT_PROP_MAX_ICE_ENERGY), + Wind (7, FightProperty.FIGHT_PROP_CUR_WIND_ENERGY, FightProperty.FIGHT_PROP_MAX_WIND_ENERGY, 10301, "TeamResonance_Wind_Lv2"), + Rock (8, FightProperty.FIGHT_PROP_CUR_ROCK_ENERGY, FightProperty.FIGHT_PROP_MAX_ROCK_ENERGY, 10701, "TeamResonance_Rock_Lv2"), + AntiFire (9, FightProperty.FIGHT_PROP_CUR_FIRE_ENERGY, FightProperty.FIGHT_PROP_MAX_FIRE_ENERGY), + Default (255, FightProperty.FIGHT_PROP_CUR_FIRE_ENERGY, FightProperty.FIGHT_PROP_MAX_FIRE_ENERGY, 10801, "TeamResonance_AllDifferent"); private final int value; private final int teamResonanceId; - private final FightProperty energyProperty; + private final FightProperty curEnergyProp; + private final FightProperty maxEnergyProp; private final int configHash; private static final Int2ObjectMap map = new Int2ObjectOpenHashMap<>(); private static final Map stringMap = new HashMap<>(); @@ -35,13 +36,14 @@ public enum ElementType { }); } - private ElementType(int value, FightProperty energyProperty) { - this(value, energyProperty, 0, null); + private ElementType(int value, FightProperty curEnergyProp, FightProperty maxEnergyProp) { + this(value, curEnergyProp, maxEnergyProp, 0, null); } - private ElementType(int value, FightProperty energyProperty, int teamResonanceId, String configName) { + private ElementType(int value, FightProperty curEnergyProp, FightProperty maxEnergyProp, int teamResonanceId, String configName) { this.value = value; - this.energyProperty = energyProperty; + this.curEnergyProp = curEnergyProp; + this.maxEnergyProp = maxEnergyProp; this.teamResonanceId = teamResonanceId; if (configName != null) { this.configHash = Utils.abilityHash(configName); @@ -54,8 +56,12 @@ public enum ElementType { return value; } - public FightProperty getEnergyProperty() { - return energyProperty; + public FightProperty getCurEnergyProp() { + return curEnergyProp; + } + + public FightProperty getMaxEnergyProp() { + return maxEnergyProp; } public int getTeamResonanceId() { diff --git a/src/main/java/emu/grasscutter/game/props/MonsterType.java b/src/main/java/emu/grasscutter/game/props/MonsterType.java new file mode 100644 index 000000000..abbc33ba0 --- /dev/null +++ b/src/main/java/emu/grasscutter/game/props/MonsterType.java @@ -0,0 +1,44 @@ +package emu.grasscutter.game.props; + +import java.util.HashMap; +import java.util.Map; +import java.util.stream.Stream; + +import it.unimi.dsi.fastutil.ints.Int2ObjectMap; +import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap; + +public enum MonsterType { + MONSTER_NONE (0), + MONSTER_ORDINARY (1), + MONSTER_BOSS (2), + MONSTER_ENV_ANIMAL (3), + MONSTER_LITTLE_MONSTER (4), + MONSTER_FISH (5); + + private final int value; + private static final Int2ObjectMap map = new Int2ObjectOpenHashMap<>(); + private static final Map stringMap = new HashMap<>(); + + static { + Stream.of(values()).forEach(e -> { + map.put(e.getValue(), e); + stringMap.put(e.name(), e); + }); + } + + private MonsterType(int value) { + this.value = value; + } + + public int getValue() { + return value; + } + + public static MonsterType getTypeByValue(int value) { + return map.getOrDefault(value, MONSTER_NONE); + } + + public static MonsterType getTypeByName(String name) { + return stringMap.getOrDefault(name, MONSTER_NONE); + } +} diff --git a/src/main/java/emu/grasscutter/game/props/OpenState.java b/src/main/java/emu/grasscutter/game/props/OpenState.java index 7cba2fcf3..d566e9051 100644 --- a/src/main/java/emu/grasscutter/game/props/OpenState.java +++ b/src/main/java/emu/grasscutter/game/props/OpenState.java @@ -94,6 +94,7 @@ public enum OpenState { OPEN_STATE_GUIDE_RELICRESOLVE(84), OPEN_STATE_GUIDE_GGUIDE(85), OPEN_STATE_GUIDE_GGUIDE_HINT(86), + OPEN_STATE_GUIDE_RIGHT_TEAM(90), // mobile phone only! OPEN_STATE_CITY_REPUATION_MENGDE(800), OPEN_STATE_CITY_REPUATION_LIYUE(801), OPEN_STATE_CITY_REPUATION_UI_HINT(802), diff --git a/src/main/java/emu/grasscutter/game/props/PlayerProperty.java b/src/main/java/emu/grasscutter/game/props/PlayerProperty.java index 3cb67d9bb..85a9456cf 100644 --- a/src/main/java/emu/grasscutter/game/props/PlayerProperty.java +++ b/src/main/java/emu/grasscutter/game/props/PlayerProperty.java @@ -30,7 +30,7 @@ public enum PlayerProperty { // his gems and then got a money refund, so negative is allowed. PROP_PLAYER_SCOIN (10016), // Mora [0, +inf) PROP_PLAYER_MP_SETTING_TYPE (10017), // Do you allow other players to join your game? [0=no 1=direct 2=approval] - PROP_IS_MP_MODE_AVAILABLE (10018), // Are you not in a quest or something that disables MP? [0, 1] + PROP_IS_MP_MODE_AVAILABLE (10018), // 0 if in quest or something that disables MP [0, 1] PROP_PLAYER_WORLD_LEVEL (10019), // [0, 8] PROP_PLAYER_RESIN (10020), // Original Resin [0, +inf) PROP_PLAYER_WAIT_SUB_HCOIN (10022), diff --git a/src/main/java/emu/grasscutter/game/props/WeaponType.java b/src/main/java/emu/grasscutter/game/props/WeaponType.java new file mode 100644 index 000000000..11654464f --- /dev/null +++ b/src/main/java/emu/grasscutter/game/props/WeaponType.java @@ -0,0 +1,69 @@ +package emu.grasscutter.game.props; + +import java.util.HashMap; +import java.util.Map; +import java.util.stream.Stream; + +import it.unimi.dsi.fastutil.ints.Int2ObjectMap; +import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap; + +public enum WeaponType { + WEAPON_NONE (0), + WEAPON_SWORD_ONE_HAND (1, 10, 5), + WEAPON_CROSSBOW (2), + WEAPON_STAFF (3), + WEAPON_DOUBLE_DAGGER (4), + WEAPON_KATANA (5), + WEAPON_SHURIKEN (6), + WEAPON_STICK (7), + WEAPON_SPEAR (8), + WEAPON_SHIELD_SMALL (9), + WEAPON_CATALYST (10, 0, 10), + WEAPON_CLAYMORE (11, 0, 10), + WEAPON_BOW (12, 0, 5), + WEAPON_POLE (13, 0, 4); + + private final int value; + private int energyGainInitialProbability; + private int energyGainIncreaseProbability; + + private static final Int2ObjectMap map = new Int2ObjectOpenHashMap<>(); + private static final Map stringMap = new HashMap<>(); + + static { + Stream.of(values()).forEach(e -> { + map.put(e.getValue(), e); + stringMap.put(e.name(), e); + }); + } + + private WeaponType(int value) { + this.value = value; + } + + private WeaponType(int value, int energyGainInitialProbability, int energyGainIncreaseProbability) { + this.value = value; + this.energyGainInitialProbability = energyGainInitialProbability; + this.energyGainIncreaseProbability = energyGainIncreaseProbability; + } + + public int getValue() { + return value; + } + + public int getEnergyGainInitialProbability() { + return energyGainInitialProbability; + } + + public int getEnergyGainIncreaseProbability() { + return energyGainIncreaseProbability; + } + + public static WeaponType getTypeByValue(int value) { + return map.getOrDefault(value, WEAPON_NONE); + } + + public static WeaponType getTypeByName(String name) { + return stringMap.getOrDefault(name, WEAPON_NONE); + } +} diff --git a/src/main/java/emu/grasscutter/game/quest/GameMainQuest.java b/src/main/java/emu/grasscutter/game/quest/GameMainQuest.java new file mode 100644 index 000000000..4dc31a5ae --- /dev/null +++ b/src/main/java/emu/grasscutter/game/quest/GameMainQuest.java @@ -0,0 +1,143 @@ +package emu.grasscutter.game.quest; + +import java.util.HashMap; +import java.util.Map; + +import emu.grasscutter.server.packet.send.PacketCodexDataUpdateNotify; +import org.bson.types.ObjectId; + +import dev.morphia.annotations.Entity; +import dev.morphia.annotations.Id; +import dev.morphia.annotations.Indexed; +import dev.morphia.annotations.Transient; +import emu.grasscutter.data.GameData; +import emu.grasscutter.data.binout.MainQuestData; +import emu.grasscutter.data.excels.RewardData; +import emu.grasscutter.database.DatabaseHelper; +import emu.grasscutter.game.player.Player; +import emu.grasscutter.game.props.ActionReason; +import emu.grasscutter.game.quest.enums.ParentQuestState; +import emu.grasscutter.game.quest.enums.QuestState; +import emu.grasscutter.net.proto.ChildQuestOuterClass.ChildQuest; +import emu.grasscutter.net.proto.ParentQuestOuterClass.ParentQuest; +import emu.grasscutter.net.proto.QuestOuterClass.Quest; +import emu.grasscutter.server.packet.send.PacketFinishedParentQuestUpdateNotify; +import emu.grasscutter.server.packet.send.PacketQuestListUpdateNotify; +import emu.grasscutter.server.packet.send.PacketQuestProgressUpdateNotify; +import emu.grasscutter.utils.Utils; +import it.unimi.dsi.fastutil.ints.Int2ObjectMap; +import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap; + +@Entity(value = "quests", useDiscriminator = false) +public class GameMainQuest { + @Id private ObjectId id; + + @Indexed private int ownerUid; + @Transient private Player owner; + + private Map childQuests; + + private int parentQuestId; + private int[] questVars; + private ParentQuestState state; + private boolean isFinished; + + @Deprecated // Morphia only. Do not use. + public GameMainQuest() {} + + public GameMainQuest(Player player, int parentQuestId) { + this.owner = player; + this.ownerUid = player.getUid(); + this.parentQuestId = parentQuestId; + this.childQuests = new HashMap<>(); + this.questVars = new int[5]; + this.state = ParentQuestState.PARENT_QUEST_STATE_NONE; + } + + public int getParentQuestId() { + return parentQuestId; + } + + public int getOwnerUid() { + return ownerUid; + } + + public Player getOwner() { + return owner; + } + + public void setOwner(Player player) { + if (player.getUid() != this.getOwnerUid()) return; + this.owner = player; + } + + public Map getChildQuests() { + return childQuests; + } + + public GameQuest getChildQuestById(int id) { + return this.getChildQuests().get(id); + } + + public int[] getQuestVars() { + return questVars; + } + + public ParentQuestState getState() { + return state; + } + + public boolean isFinished() { + return isFinished; + } + + public void finish() { + this.isFinished = true; + this.state = ParentQuestState.PARENT_QUEST_STATE_FINISHED; + + this.getOwner().getSession().send(new PacketFinishedParentQuestUpdateNotify(this)); + this.getOwner().getSession().send(new PacketCodexDataUpdateNotify(this)); + + this.save(); + + // Add rewards + MainQuestData mainQuestData = GameData.getMainQuestDataMap().get(this.getParentQuestId()); + for (int rewardId : mainQuestData.getRewardIdList()) { + RewardData rewardData = GameData.getRewardDataMap().get(rewardId); + + if (rewardData == null) { + continue; + } + + getOwner().getInventory().addItemParamDatas(rewardData.getRewardItemList(), ActionReason.QuestReward); + } + } + + public void save() { + DatabaseHelper.saveQuest(this); + } + + public ParentQuest toProto() { + ParentQuest.Builder proto = ParentQuest.newBuilder() + .setParentQuestId(getParentQuestId()) + .setIsFinished(isFinished()) + .setParentQuestState(getState().getValue()); + + for (GameQuest quest : this.getChildQuests().values()) { + ChildQuest childQuest = ChildQuest.newBuilder() + .setQuestId(quest.getQuestId()) + .setState(quest.getState().getValue()) + .build(); + + proto.addChildQuestList(childQuest); + } + + if (getQuestVars() != null) { + for (int i : getQuestVars()) { + proto.addQuestVar(i); + } + } + + return proto.build(); + } +} diff --git a/src/main/java/emu/grasscutter/game/quest/GameQuest.java b/src/main/java/emu/grasscutter/game/quest/GameQuest.java new file mode 100644 index 000000000..57d8655fb --- /dev/null +++ b/src/main/java/emu/grasscutter/game/quest/GameQuest.java @@ -0,0 +1,226 @@ +package emu.grasscutter.game.quest; + +import dev.morphia.annotations.Entity; +import dev.morphia.annotations.Transient; +import emu.grasscutter.Grasscutter; +import emu.grasscutter.data.GameData; +import emu.grasscutter.data.binout.MainQuestData; +import emu.grasscutter.data.binout.MainQuestData.SubQuestData; +import emu.grasscutter.data.excels.QuestData; +import emu.grasscutter.data.excels.QuestData.QuestCondition; +import emu.grasscutter.game.player.Player; +import emu.grasscutter.game.quest.enums.LogicType; +import emu.grasscutter.game.quest.enums.QuestState; +import emu.grasscutter.net.proto.QuestOuterClass.Quest; +import emu.grasscutter.server.packet.send.PacketCodexDataUpdateNotify; +import emu.grasscutter.server.packet.send.PacketQuestListUpdateNotify; +import emu.grasscutter.server.packet.send.PacketQuestProgressUpdateNotify; +import emu.grasscutter.utils.Utils; + +@Entity +public class GameQuest { + @Transient private GameMainQuest mainQuest; + @Transient private QuestData questData; + + private int questId; + private int mainQuestId; + private QuestState state; + + private int startTime; + private int acceptTime; + private int finishTime; + + private int[] finishProgressList; + private int[] failProgressList; + + @Deprecated // Morphia only. Do not use. + public GameQuest() {} + + public GameQuest(GameMainQuest mainQuest, QuestData questData) { + this.mainQuest = mainQuest; + this.questId = questData.getId(); + this.mainQuestId = questData.getMainId(); + this.questData = questData; + this.acceptTime = Utils.getCurrentSeconds(); + this.startTime = this.acceptTime; + this.state = QuestState.QUEST_STATE_UNFINISHED; + + if (questData.getFinishCond() != null && questData.getAcceptCond().length != 0) { + this.finishProgressList = new int[questData.getFinishCond().length]; + } + + if (questData.getFailCond() != null && questData.getFailCond().length != 0) { + this.failProgressList = new int[questData.getFailCond().length]; + } + + this.mainQuest.getChildQuests().put(this.questId, this); + } + + public GameMainQuest getMainQuest() { + return mainQuest; + } + + public void setMainQuest(GameMainQuest mainQuest) { + this.mainQuest = mainQuest; + } + + public Player getOwner() { + return getMainQuest().getOwner(); + } + + public int getQuestId() { + return questId; + } + + public int getMainQuestId() { + return mainQuestId; + } + + public QuestData getData() { + return questData; + } + + public void setConfig(QuestData config) { + if (this.getQuestId() != config.getId()) return; + this.questData = config; + } + + public QuestState getState() { + return state; + } + + public void setState(QuestState state) { + this.state = state; + } + + public int getStartTime() { + return startTime; + } + + public void setStartTime(int startTime) { + this.startTime = startTime; + } + + public int getAcceptTime() { + return acceptTime; + } + + public void setAcceptTime(int acceptTime) { + this.acceptTime = acceptTime; + } + + public int getFinishTime() { + return finishTime; + } + + public void setFinishTime(int finishTime) { + this.finishTime = finishTime; + } + + public int[] getFinishProgressList() { + return finishProgressList; + } + + public void setFinishProgress(int index, int value) { + finishProgressList[index] = value; + } + + public int[] getFailProgressList() { + return failProgressList; + } + + public void setFailProgress(int index, int value) { + failProgressList[index] = value; + } + + public void finish() { + this.state = QuestState.QUEST_STATE_FINISHED; + this.finishTime = Utils.getCurrentSeconds(); + + if (this.getFinishProgressList() != null) { + for (int i = 0 ; i < getFinishProgressList().length; i++) { + getFinishProgressList()[i] = 1; + } + } + + this.getOwner().getSession().send(new PacketQuestProgressUpdateNotify(this)); + this.getOwner().getSession().send(new PacketQuestListUpdateNotify(this)); + + if (this.getData().finishParent()) { + // This quest finishes the questline - the main quest will also save the quest to db so we dont have to call save() here + this.getMainQuest().finish(); + } else { + // Try and accept other quests if possible + this.tryAcceptQuestLine(); + this.save(); + } + } + + public boolean tryAcceptQuestLine() { + try { + MainQuestData questConfig = GameData.getMainQuestDataMap().get(this.getMainQuestId()); + + for (SubQuestData subQuest : questConfig.getSubQuests()) { + GameQuest quest = getMainQuest().getChildQuestById(subQuest.getSubId()); + + if (quest == null) { + QuestData questData = GameData.getQuestDataMap().get(subQuest.getSubId()); + + if (questData == null || questData.getAcceptCond() == null + || questData.getAcceptCond().length == 0) { + continue; + } + + int[] accept = new int[questData.getAcceptCond().length]; + + // TODO + for (int i = 0; i < questData.getAcceptCond().length; i++) { + QuestCondition condition = questData.getAcceptCond()[i]; + boolean result = getOwner().getServer().getQuestHandler().triggerCondition(this, condition, + condition.getParam()); + + accept[i] = result ? 1 : 0; + } + + boolean shouldAccept = LogicType.calculate(questData.getAcceptCondComb(), accept); + + if (shouldAccept) { + this.getOwner().getQuestManager().addQuest(questData.getId()); + } + } + } + } catch (Exception e) { + Grasscutter.getLogger().error("An error occurred while trying to accept quest.", e); + } + + return false; + } + + public void save() { + getMainQuest().save(); + } + + public Quest toProto() { + Quest.Builder proto = Quest.newBuilder() + .setQuestId(this.getQuestId()) + .setState(this.getState().getValue()) + .setParentQuestId(this.getMainQuestId()) + .setStartTime(this.getStartTime()) + .setStartGameTime(438) + .setAcceptTime(this.getAcceptTime()); + + if (this.getFinishProgressList() != null) { + for (int i : this.getFinishProgressList()) { + proto.addFinishProgressList(i); + } + } + + if (this.getFailProgressList() != null) { + for (int i : this.getFailProgressList()) { + proto.addFailProgressList(i); + } + } + + return proto.build(); + } +} diff --git a/src/main/java/emu/grasscutter/game/quest/QuestManager.java b/src/main/java/emu/grasscutter/game/quest/QuestManager.java new file mode 100644 index 000000000..838cb301f --- /dev/null +++ b/src/main/java/emu/grasscutter/game/quest/QuestManager.java @@ -0,0 +1,186 @@ +package emu.grasscutter.game.quest; + +import java.util.HashSet; +import java.util.List; +import java.util.Set; +import java.util.function.Consumer; + +import emu.grasscutter.data.GameData; +import emu.grasscutter.data.excels.QuestData; +import emu.grasscutter.data.excels.QuestData.QuestCondition; +import emu.grasscutter.database.DatabaseHelper; +import emu.grasscutter.game.player.Player; +import emu.grasscutter.game.quest.enums.QuestTrigger; +import emu.grasscutter.game.quest.enums.LogicType; +import emu.grasscutter.game.quest.enums.QuestState; +import emu.grasscutter.server.packet.send.PacketFinishedParentQuestUpdateNotify; +import emu.grasscutter.server.packet.send.PacketQuestListUpdateNotify; +import emu.grasscutter.server.packet.send.PacketQuestProgressUpdateNotify; +import emu.grasscutter.server.packet.send.PacketServerCondMeetQuestListUpdateNotify; +import it.unimi.dsi.fastutil.ints.Int2ObjectMap; +import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap; + +public class QuestManager { + private final Player player; + private final Int2ObjectMap quests; + + public QuestManager(Player player) { + this.player = player; + this.quests = new Int2ObjectOpenHashMap<>(); + } + + public Player getPlayer() { + return player; + } + + public Int2ObjectMap getQuests() { + return quests; + } + + public GameMainQuest getMainQuestById(int mainQuestId) { + return getQuests().get(mainQuestId); + } + + public GameQuest getQuestById(int questId) { + QuestData questConfig = GameData.getQuestDataMap().get(questId); + if (questConfig == null) { + return null; + } + + GameMainQuest mainQuest = getQuests().get(questConfig.getMainId()); + + if (mainQuest == null) { + return null; + } + + return mainQuest.getChildQuests().get(questId); + } + + public void forEachQuest(Consumer callback) { + for (GameMainQuest mainQuest : getQuests().values()) { + for (GameQuest quest : mainQuest.getChildQuests().values()) { + callback.accept(quest); + } + } + } + + public void forEachMainQuest(Consumer callback) { + for (GameMainQuest mainQuest : getQuests().values()) { + callback.accept(mainQuest); + } + } + + // TODO + public void forEachActiveQuest(Consumer callback) { + for (GameMainQuest mainQuest : getQuests().values()) { + for (GameQuest quest : mainQuest.getChildQuests().values()) { + if (quest.getState() != QuestState.QUEST_STATE_FINISHED) { + callback.accept(quest); + } + } + } + } + + public GameMainQuest addMainQuest(QuestData questConfig) { + GameMainQuest mainQuest = new GameMainQuest(getPlayer(), questConfig.getMainId()); + getQuests().put(mainQuest.getParentQuestId(), mainQuest); + + getPlayer().sendPacket(new PacketFinishedParentQuestUpdateNotify(mainQuest)); + + return mainQuest; + } + + public GameQuest addQuest(int questId) { + QuestData questConfig = GameData.getQuestDataMap().get(questId); + if (questConfig == null) { + return null; + } + + // Main quest + GameMainQuest mainQuest = this.getMainQuestById(questConfig.getMainId()); + + // Create main quest if it doesnt exist + if (mainQuest == null) { + mainQuest = addMainQuest(questConfig); + } + + // Sub quest + GameQuest quest = mainQuest.getChildQuestById(questId); + + if (quest != null) { + return null; + } + + // Create + quest = new GameQuest(mainQuest, questConfig); + + // Save main quest + mainQuest.save(); + + // Send packet + getPlayer().sendPacket(new PacketQuestListUpdateNotify(quest)); + + return quest; + } + + public void triggerEvent(QuestTrigger condType, int... params) { + Set changedQuests = new HashSet<>(); + + this.forEachActiveQuest(quest -> { + QuestData data = quest.getData(); + + for (int i = 0; i < data.getFinishCond().length; i++) { + if (quest.getFinishProgressList() == null + || quest.getFinishProgressList().length == 0 + || quest.getFinishProgressList()[i] == 1) { + continue; + } + + QuestCondition condition = data.getFinishCond()[i]; + + if (condition.getType() != condType) { + continue; + } + + boolean result = getPlayer().getServer().getQuestHandler().triggerContent(quest, condition, params); + + if (result) { + quest.getFinishProgressList()[i] = 1; + + changedQuests.add(quest); + } + } + }); + + for (GameQuest quest : changedQuests) { + LogicType logicType = quest.getData().getFailCondComb(); + int[] progress = quest.getFinishProgressList(); + + // Handle logical comb + boolean finish = LogicType.calculate(logicType, progress); + + // Finish + if (finish) { + quest.finish(); + } else { + getPlayer().sendPacket(new PacketQuestProgressUpdateNotify(quest)); + quest.save(); + } + } + } + + public void loadFromDatabase() { + List quests = DatabaseHelper.getAllQuests(getPlayer()); + + for (GameMainQuest mainQuest : quests) { + mainQuest.setOwner(this.getPlayer()); + + for (GameQuest quest : mainQuest.getChildQuests().values()) { + quest.setMainQuest(mainQuest); + quest.setConfig(GameData.getQuestDataMap().get(quest.getQuestId())); + } + + this.getQuests().put(mainQuest.getParentQuestId(), mainQuest); + } + } +} diff --git a/src/main/java/emu/grasscutter/game/quest/QuestValue.java b/src/main/java/emu/grasscutter/game/quest/QuestValue.java new file mode 100644 index 000000000..42b868fc8 --- /dev/null +++ b/src/main/java/emu/grasscutter/game/quest/QuestValue.java @@ -0,0 +1,11 @@ +package emu.grasscutter.game.quest; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; + +import emu.grasscutter.game.quest.enums.QuestTrigger; + +@Retention(RetentionPolicy.RUNTIME) +public @interface QuestValue { + QuestTrigger value(); +} diff --git a/src/main/java/emu/grasscutter/game/quest/ServerQuestHandler.java b/src/main/java/emu/grasscutter/game/quest/ServerQuestHandler.java new file mode 100644 index 000000000..b48194dd9 --- /dev/null +++ b/src/main/java/emu/grasscutter/game/quest/ServerQuestHandler.java @@ -0,0 +1,89 @@ +package emu.grasscutter.game.quest; + +import java.util.Set; + +import org.reflections.Reflections; + +import emu.grasscutter.Grasscutter; +import emu.grasscutter.data.excels.QuestData.QuestCondition; +import emu.grasscutter.game.quest.handlers.QuestBaseHandler; +import it.unimi.dsi.fastutil.ints.Int2ObjectMap; +import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap; + +@SuppressWarnings("unchecked") +public class ServerQuestHandler { + private final Int2ObjectMap condHandlers; + private final Int2ObjectMap contHandlers; + private final Int2ObjectMap execHandlers; + + public ServerQuestHandler() { + this.condHandlers = new Int2ObjectOpenHashMap<>(); + this.contHandlers = new Int2ObjectOpenHashMap<>(); + this.execHandlers = new Int2ObjectOpenHashMap<>(); + + this.registerHandlers(); + } + + public void registerHandlers() { + this.registerHandlers(this.condHandlers, "emu.grasscutter.game.quest.conditions"); + this.registerHandlers(this.contHandlers, "emu.grasscutter.game.quest.content"); + this.registerHandlers(this.execHandlers, "emu.grasscutter.game.quest.exec"); + } + + public void registerHandlers(Int2ObjectMap map, String packageName) { + Reflections reflections = new Reflections(packageName); + Set handlerClasses = reflections.getSubTypesOf(QuestBaseHandler.class); + + for (Object obj : handlerClasses) { + this.registerPacketHandler(map, (Class) obj); + } + } + + public void registerPacketHandler(Int2ObjectMap map, Class handlerClass) { + try { + QuestValue opcode = handlerClass.getAnnotation(QuestValue.class); + + if (opcode == null || opcode.value().getValue() <= 0) { + return; + } + + QuestBaseHandler packetHandler = (QuestBaseHandler) handlerClass.newInstance(); + + map.put(opcode.value().getValue(), packetHandler); + } catch (Exception e) { + e.printStackTrace(); + } + } + + // TODO make cleaner + + public boolean triggerCondition(GameQuest quest, QuestCondition condition, int... params) { + QuestBaseHandler handler = condHandlers.get(condition.getType().getValue()); + + if (handler == null || quest.getData() == null) { + return false; + } + + return handler.execute(quest, condition, params); + } + + public boolean triggerContent(GameQuest quest, QuestCondition condition, int... params) { + QuestBaseHandler handler = contHandlers.get(condition.getType().getValue()); + + if (handler == null || quest.getData() == null) { + return false; + } + + return handler.execute(quest, condition, params); + } + + public boolean triggerExec(GameQuest quest, QuestCondition condition, int... params) { + QuestBaseHandler handler = execHandlers.get(condition.getType().getValue()); + + if (handler == null || quest.getData() == null) { + return false; + } + + return handler.execute(quest, condition, params); + } +} diff --git a/src/main/java/emu/grasscutter/game/quest/conditions/BaseCondition.java b/src/main/java/emu/grasscutter/game/quest/conditions/BaseCondition.java new file mode 100644 index 000000000..86df72b34 --- /dev/null +++ b/src/main/java/emu/grasscutter/game/quest/conditions/BaseCondition.java @@ -0,0 +1,18 @@ +package emu.grasscutter.game.quest.conditions; + +import emu.grasscutter.game.quest.QuestValue; +import emu.grasscutter.data.excels.QuestData.QuestCondition; +import emu.grasscutter.game.quest.GameQuest; +import emu.grasscutter.game.quest.enums.QuestTrigger; +import emu.grasscutter.game.quest.handlers.QuestBaseHandler; + +@QuestValue(QuestTrigger.QUEST_CONTENT_NONE) +public class BaseCondition extends QuestBaseHandler { + + @Override + public boolean execute(GameQuest quest, QuestCondition condition, int... params) { + // TODO Auto-generated method stub + return false; + } + +} diff --git a/src/main/java/emu/grasscutter/game/quest/conditions/ConditionPlayerLevelEqualGreater.java b/src/main/java/emu/grasscutter/game/quest/conditions/ConditionPlayerLevelEqualGreater.java new file mode 100644 index 000000000..e15ce8ed7 --- /dev/null +++ b/src/main/java/emu/grasscutter/game/quest/conditions/ConditionPlayerLevelEqualGreater.java @@ -0,0 +1,17 @@ +package emu.grasscutter.game.quest.conditions; + +import emu.grasscutter.game.quest.QuestValue; +import emu.grasscutter.data.excels.QuestData.QuestCondition; +import emu.grasscutter.game.quest.GameQuest; +import emu.grasscutter.game.quest.enums.QuestTrigger; +import emu.grasscutter.game.quest.handlers.QuestBaseHandler; + +@QuestValue(QuestTrigger.QUEST_COND_PLAYER_LEVEL_EQUAL_GREATER) +public class ConditionPlayerLevelEqualGreater extends QuestBaseHandler { + + @Override + public boolean execute(GameQuest quest, QuestCondition condition, int... params) { + return quest.getOwner().getLevel() >= params[0]; + } + +} diff --git a/src/main/java/emu/grasscutter/game/quest/conditions/ConditionStateEqual.java b/src/main/java/emu/grasscutter/game/quest/conditions/ConditionStateEqual.java new file mode 100644 index 000000000..bcf90145b --- /dev/null +++ b/src/main/java/emu/grasscutter/game/quest/conditions/ConditionStateEqual.java @@ -0,0 +1,23 @@ +package emu.grasscutter.game.quest.conditions; + +import emu.grasscutter.game.quest.QuestValue; +import emu.grasscutter.data.excels.QuestData.QuestCondition; +import emu.grasscutter.game.quest.GameQuest; +import emu.grasscutter.game.quest.enums.QuestTrigger; +import emu.grasscutter.game.quest.handlers.QuestBaseHandler; + +@QuestValue(QuestTrigger.QUEST_COND_STATE_EQUAL) +public class ConditionStateEqual extends QuestBaseHandler { + + @Override + public boolean execute(GameQuest quest, QuestCondition condition, int... params) { + GameQuest checkQuest = quest.getOwner().getQuestManager().getQuestById(params[0]); + + if (checkQuest != null) { + return checkQuest.getState().getValue() == params[1]; + } + + return false; + } + +} diff --git a/src/main/java/emu/grasscutter/game/quest/content/BaseContent.java b/src/main/java/emu/grasscutter/game/quest/content/BaseContent.java new file mode 100644 index 000000000..e2c7abdc8 --- /dev/null +++ b/src/main/java/emu/grasscutter/game/quest/content/BaseContent.java @@ -0,0 +1,18 @@ +package emu.grasscutter.game.quest.content; + +import emu.grasscutter.game.quest.QuestValue; +import emu.grasscutter.data.excels.QuestData.QuestCondition; +import emu.grasscutter.game.quest.GameQuest; +import emu.grasscutter.game.quest.enums.QuestTrigger; +import emu.grasscutter.game.quest.handlers.QuestBaseHandler; + +@QuestValue(QuestTrigger.QUEST_CONTENT_NONE) +public class BaseContent extends QuestBaseHandler { + + @Override + public boolean execute(GameQuest quest, QuestCondition condition, int... params) { + // TODO Auto-generated method stub + return false; + } + +} diff --git a/src/main/java/emu/grasscutter/game/quest/content/ContentCompleteTalk.java b/src/main/java/emu/grasscutter/game/quest/content/ContentCompleteTalk.java new file mode 100644 index 000000000..3c17bccde --- /dev/null +++ b/src/main/java/emu/grasscutter/game/quest/content/ContentCompleteTalk.java @@ -0,0 +1,17 @@ +package emu.grasscutter.game.quest.content; + +import emu.grasscutter.game.quest.QuestValue; +import emu.grasscutter.data.excels.QuestData.QuestCondition; +import emu.grasscutter.game.quest.GameQuest; +import emu.grasscutter.game.quest.enums.QuestTrigger; +import emu.grasscutter.game.quest.handlers.QuestBaseHandler; + +@QuestValue(QuestTrigger.QUEST_CONTENT_COMPLETE_TALK) +public class ContentCompleteTalk extends QuestBaseHandler { + + @Override + public boolean execute(GameQuest quest, QuestCondition condition, int... params) { + return condition.getParam()[0] == params[0]; + } + +} diff --git a/src/main/java/emu/grasscutter/game/quest/content/ContentEnterDungeon.java b/src/main/java/emu/grasscutter/game/quest/content/ContentEnterDungeon.java new file mode 100644 index 000000000..e331f48e0 --- /dev/null +++ b/src/main/java/emu/grasscutter/game/quest/content/ContentEnterDungeon.java @@ -0,0 +1,17 @@ +package emu.grasscutter.game.quest.content; + +import emu.grasscutter.game.quest.QuestValue; +import emu.grasscutter.data.excels.QuestData.QuestCondition; +import emu.grasscutter.game.quest.GameQuest; +import emu.grasscutter.game.quest.enums.QuestTrigger; +import emu.grasscutter.game.quest.handlers.QuestBaseHandler; + +@QuestValue(QuestTrigger.QUEST_CONTENT_ENTER_DUNGEON) +public class ContentEnterDungeon extends QuestBaseHandler { + + @Override + public boolean execute(GameQuest quest, QuestCondition condition, int... params) { + return condition.getParam()[0] == params[0]; + } + +} diff --git a/src/main/java/emu/grasscutter/game/quest/content/ContentFinishPlot.java b/src/main/java/emu/grasscutter/game/quest/content/ContentFinishPlot.java new file mode 100644 index 000000000..305808112 --- /dev/null +++ b/src/main/java/emu/grasscutter/game/quest/content/ContentFinishPlot.java @@ -0,0 +1,17 @@ +package emu.grasscutter.game.quest.content; + +import emu.grasscutter.game.quest.QuestValue; +import emu.grasscutter.data.excels.QuestData.QuestCondition; +import emu.grasscutter.game.quest.GameQuest; +import emu.grasscutter.game.quest.enums.QuestTrigger; +import emu.grasscutter.game.quest.handlers.QuestBaseHandler; + +@QuestValue(QuestTrigger.QUEST_CONTENT_FINISH_PLOT) +public class ContentFinishPlot extends QuestBaseHandler { + + @Override + public boolean execute(GameQuest quest, QuestCondition condition, int... params) { + return condition.getParam()[0] == params[0]; + } + +} diff --git a/src/main/java/emu/grasscutter/game/quest/enums/LogicType.java b/src/main/java/emu/grasscutter/game/quest/enums/LogicType.java new file mode 100644 index 000000000..12677ee0d --- /dev/null +++ b/src/main/java/emu/grasscutter/game/quest/enums/LogicType.java @@ -0,0 +1,43 @@ +package emu.grasscutter.game.quest.enums; + +import java.util.Arrays; + +public enum LogicType { + LOGIC_NONE (0), + LOGIC_AND (1), + LOGIC_OR (2), + LOGIC_NOT (3), + LOGIC_A_AND_ETCOR (4), + LOGIC_A_AND_B_AND_ETCOR (5), + LOGIC_A_OR_ETCAND (6), + LOGIC_A_OR_B_OR_ETCAND (7), + LOGIC_A_AND_B_OR_ETCAND (8); + + private final int value; + + LogicType(int id) { + this.value = id; + } + + public int getValue() { + return value; + } + + public static boolean calculate(LogicType logicType, int[] progress) { + if (logicType == null) { + return progress[0] == 1; + } + + switch (logicType) { + case LOGIC_AND -> { + return Arrays.stream(progress).allMatch(i -> i == 1); + } + case LOGIC_OR -> { + return Arrays.stream(progress).anyMatch(i -> i == 1); + } + default -> { + return Arrays.stream(progress).anyMatch(i -> i == 1); + } + } + } +} diff --git a/src/main/java/emu/grasscutter/game/quest/enums/ParentQuestState.java b/src/main/java/emu/grasscutter/game/quest/enums/ParentQuestState.java new file mode 100644 index 000000000..6c7805f8d --- /dev/null +++ b/src/main/java/emu/grasscutter/game/quest/enums/ParentQuestState.java @@ -0,0 +1,18 @@ +package emu.grasscutter.game.quest.enums; + +public enum ParentQuestState { + PARENT_QUEST_STATE_NONE (0), + PARENT_QUEST_STATE_FINISHED (1), + PARENT_QUEST_STATE_FAILED (2), + PARENT_QUEST_STATE_CANCELED (3); + + private final int value; + + ParentQuestState(int id) { + this.value = id; + } + + public int getValue() { + return value; + } +} diff --git a/src/main/java/emu/grasscutter/game/quest/enums/QuestGuideType.java b/src/main/java/emu/grasscutter/game/quest/enums/QuestGuideType.java new file mode 100644 index 000000000..45915c6b7 --- /dev/null +++ b/src/main/java/emu/grasscutter/game/quest/enums/QuestGuideType.java @@ -0,0 +1,17 @@ +package emu.grasscutter.game.quest.enums; + +public enum QuestGuideType { + QUEST_GUIDE_NONE (0), + QUEST_GUIDE_LOCATION (1), + QUEST_GUIDE_NPC (2); + + private final int value; + + QuestGuideType(int id) { + this.value = id; + } + + public int getValue() { + return value; + } +} diff --git a/src/main/java/emu/grasscutter/game/quest/enums/QuestShowType.java b/src/main/java/emu/grasscutter/game/quest/enums/QuestShowType.java new file mode 100644 index 000000000..014c1ee06 --- /dev/null +++ b/src/main/java/emu/grasscutter/game/quest/enums/QuestShowType.java @@ -0,0 +1,16 @@ +package emu.grasscutter.game.quest.enums; + +public enum QuestShowType { + QUEST_SHOW (0), + QUEST_HIDDEN (1); + + private final int value; + + QuestShowType(int id) { + this.value = id; + } + + public int getValue() { + return value; + } +} diff --git a/src/main/java/emu/grasscutter/game/quest/enums/QuestState.java b/src/main/java/emu/grasscutter/game/quest/enums/QuestState.java new file mode 100644 index 000000000..d258a2582 --- /dev/null +++ b/src/main/java/emu/grasscutter/game/quest/enums/QuestState.java @@ -0,0 +1,19 @@ +package emu.grasscutter.game.quest.enums; + +public enum QuestState { + QUEST_STATE_NONE (0), + QUEST_STATE_UNSTARTED (1), + QUEST_STATE_UNFINISHED (2), + QUEST_STATE_FINISHED (3), + QUEST_STATE_FAILED (4); + + private final int value; + + QuestState(int id) { + this.value = id; + } + + public int getValue() { + return value; + } +} diff --git a/src/main/java/emu/grasscutter/game/quest/enums/QuestTrigger.java b/src/main/java/emu/grasscutter/game/quest/enums/QuestTrigger.java new file mode 100644 index 000000000..def3a399d --- /dev/null +++ b/src/main/java/emu/grasscutter/game/quest/enums/QuestTrigger.java @@ -0,0 +1,235 @@ +package emu.grasscutter.game.quest.enums; + +public enum QuestTrigger { + QUEST_COND_NONE (0), + QUEST_COND_STATE_EQUAL (1), + QUEST_COND_STATE_NOT_EQUAL (2), + QUEST_COND_PACK_HAVE_ITEM (3), + QUEST_COND_AVATAR_ELEMENT_EQUAL (4), + QUEST_COND_AVATAR_ELEMENT_NOT_EQUAL (5), + QUEST_COND_AVATAR_CAN_CHANGE_ELEMENT (6), + QUEST_COND_CITY_LEVEL_EQUAL_GREATER (7), + QUEST_COND_ITEM_NUM_LESS_THAN (8), + QUEST_COND_DAILY_TASK_START (9), + QUEST_COND_OPEN_STATE_EQUAL (10), + QUEST_COND_DAILY_TASK_OPEN (11), + QUEST_COND_DAILY_TASK_REWARD_CAN_GET (12), + QUEST_COND_DAILY_TASK_REWARD_RECEIVED (13), + QUEST_COND_PLAYER_LEVEL_REWARD_CAN_GET (14), + QUEST_COND_EXPLORATION_REWARD_CAN_GET (15), + QUEST_COND_IS_WORLD_OWNER (16), + QUEST_COND_PLAYER_LEVEL_EQUAL_GREATER (17), + QUEST_COND_SCENE_AREA_UNLOCKED (18), + QUEST_COND_ITEM_GIVING_ACTIVED (19), + QUEST_COND_ITEM_GIVING_FINISHED (20), + QUEST_COND_IS_DAYTIME (21), + QUEST_COND_CURRENT_AVATAR (22), + QUEST_COND_CURRENT_AREA (23), + QUEST_COND_QUEST_VAR_EQUAL (24), + QUEST_COND_QUEST_VAR_GREATER (25), + QUEST_COND_QUEST_VAR_LESS (26), + QUEST_COND_FORGE_HAVE_FINISH (27), + QUEST_COND_DAILY_TASK_IN_PROGRESS (28), + QUEST_COND_DAILY_TASK_FINISHED (29), + QUEST_COND_ACTIVITY_COND (30), + QUEST_COND_ACTIVITY_OPEN (31), + QUEST_COND_DAILY_TASK_VAR_GT (32), + QUEST_COND_DAILY_TASK_VAR_EQ (33), + QUEST_COND_DAILY_TASK_VAR_LT (34), + QUEST_COND_BARGAIN_ITEM_GT (35), + QUEST_COND_BARGAIN_ITEM_EQ (36), + QUEST_COND_BARGAIN_ITEM_LT (37), + QUEST_COND_COMPLETE_TALK (38), + QUEST_COND_NOT_HAVE_BLOSSOM_TALK (39), + QUEST_COND_IS_CUR_BLOSSOM_TALK (40), + QUEST_COND_QUEST_NOT_RECEIVE (41), + QUEST_COND_QUEST_SERVER_COND_VALID (42), + QUEST_COND_ACTIVITY_CLIENT_COND (43), + QUEST_COND_QUEST_GLOBAL_VAR_EQUAL (44), + QUEST_COND_QUEST_GLOBAL_VAR_GREATER (45), + QUEST_COND_QUEST_GLOBAL_VAR_LESS (46), + QUEST_COND_PERSONAL_LINE_UNLOCK (47), + QUEST_COND_CITY_REPUTATION_REQUEST (48), + QUEST_COND_MAIN_COOP_START (49), + QUEST_COND_MAIN_COOP_ENTER_SAVE_POINT (50), + QUEST_COND_CITY_REPUTATION_LEVEL (51), + QUEST_COND_CITY_REPUTATION_UNLOCK (52), + QUEST_COND_LUA_NOTIFY (53), + QUEST_COND_CUR_CLIMATE (54), + QUEST_COND_ACTIVITY_END (55), + QUEST_COND_COOP_POINT_RUNNING (56), + QUEST_COND_GADGET_TALK_STATE_EQUAL (57), + QUEST_COND_AVATAR_FETTER_GT (58), + QUEST_COND_AVATAR_FETTER_EQ (59), + QUEST_COND_AVATAR_FETTER_LT (60), + QUEST_COND_NEW_HOMEWORLD_MOUDLE_UNLOCK (61), + QUEST_COND_NEW_HOMEWORLD_LEVEL_REWARD (62), + QUEST_COND_NEW_HOMEWORLD_MAKE_FINISH (63), + QUEST_COND_HOMEWORLD_NPC_EVENT (64), + QUEST_COND_TIME_VAR_GT_EQ (65), + QUEST_COND_TIME_VAR_PASS_DAY (66), + QUEST_COND_HOMEWORLD_NPC_NEW_TALK (67), + QUEST_COND_PLAYER_CHOOSE_MALE (68), + QUEST_COND_HISTORY_GOT_ANY_ITEM (69), + QUEST_COND_LEARNED_RECIPE (70), + QUEST_COND_LUNARITE_REGION_UNLOCKED (71), + QUEST_COND_LUNARITE_HAS_REGION_HINT_COUNT (72), + QUEST_COND_LUNARITE_COLLECT_FINISH (73), + QUEST_COND_LUNARITE_MARK_ALL_FINISH (74), + QUEST_COND_NEW_HOMEWORLD_SHOP_ITEM (75), + QUEST_COND_SCENE_POINT_UNLOCK (76), + QUEST_COND_SCENE_LEVEL_TAG_EQ (77), + + QUEST_CONTENT_NONE (0), + QUEST_CONTENT_KILL_MONSTER (1), + QUEST_CONTENT_COMPLETE_TALK (2), + QUEST_CONTENT_MONSTER_DIE (3), + QUEST_CONTENT_FINISH_PLOT (4), + QUEST_CONTENT_OBTAIN_ITEM (5), + QUEST_CONTENT_TRIGGER_FIRE (6), + QUEST_CONTENT_CLEAR_GROUP_MONSTER (7), + QUEST_CONTENT_NOT_FINISH_PLOT (8), + QUEST_CONTENT_ENTER_DUNGEON (9), + QUEST_CONTENT_ENTER_MY_WORLD (10), + QUEST_CONTENT_FINISH_DUNGEON (11), + QUEST_CONTENT_DESTROY_GADGET (12), + QUEST_CONTENT_OBTAIN_MATERIAL_WITH_SUBTYPE (13), + QUEST_CONTENT_NICK_NAME (14), + QUEST_CONTENT_WORKTOP_SELECT (15), + QUEST_CONTENT_SEAL_BATTLE_RESULT (16), + QUEST_CONTENT_ENTER_ROOM (17), + QUEST_CONTENT_GAME_TIME_TICK (18), + QUEST_CONTENT_FAIL_DUNGEON (19), + QUEST_CONTENT_LUA_NOTIFY (20), + QUEST_CONTENT_TEAM_DEAD (21), + QUEST_CONTENT_COMPLETE_ANY_TALK (22), + QUEST_CONTENT_UNLOCK_TRANS_POINT (23), + QUEST_CONTENT_ADD_QUEST_PROGRESS (24), + QUEST_CONTENT_INTERACT_GADGET (25), + QUEST_CONTENT_DAILY_TASK_COMP_FINISH (26), + QUEST_CONTENT_FINISH_ITEM_GIVING (27), + QUEST_CONTENT_SKILL (107), + QUEST_CONTENT_CITY_LEVEL_UP (109), + QUEST_CONTENT_PATTERN_GROUP_CLEAR_MONSTER (110), + QUEST_CONTENT_ITEM_LESS_THAN (111), + QUEST_CONTENT_PLAYER_LEVEL_UP (112), + QUEST_CONTENT_DUNGEON_OPEN_STATUE (113), + QUEST_CONTENT_UNLOCK_AREA (114), + QUEST_CONTENT_OPEN_CHEST_WITH_GADGET_ID (115), + QUEST_CONTENT_UNLOCK_TRANS_POINT_WITH_TYPE (116), + QUEST_CONTENT_FINISH_DAILY_DUNGEON (117), + QUEST_CONTENT_FINISH_WEEKLY_DUNGEON (118), + QUEST_CONTENT_QUEST_VAR_EQUAL (119), + QUEST_CONTENT_QUEST_VAR_GREATER (120), + QUEST_CONTENT_QUEST_VAR_LESS (121), + QUEST_CONTENT_OBTAIN_VARIOUS_ITEM (122), + QUEST_CONTENT_FINISH_TOWER_LEVEL (123), + QUEST_CONTENT_BARGAIN_SUCC (124), + QUEST_CONTENT_BARGAIN_FAIL (125), + QUEST_CONTENT_ITEM_LESS_THAN_BARGAIN (126), + QUEST_CONTENT_ACTIVITY_TRIGGER_FAILED (127), + QUEST_CONTENT_MAIN_COOP_ENTER_SAVE_POINT (128), + QUEST_CONTENT_ANY_MANUAL_TRANSPORT (129), + QUEST_CONTENT_USE_ITEM (130), + QUEST_CONTENT_MAIN_COOP_ENTER_ANY_SAVE_POINT (131), + QUEST_CONTENT_ENTER_MY_HOME_WORLD (132), + QUEST_CONTENT_ENTER_MY_WORLD_SCENE (133), + QUEST_CONTENT_TIME_VAR_GT_EQ (134), + QUEST_CONTENT_TIME_VAR_PASS_DAY (135), + QUEST_CONTENT_QUEST_STATE_EQUAL (136), + QUEST_CONTENT_QUEST_STATE_NOT_EQUAL (137), + QUEST_CONTENT_UNLOCKED_RECIPE (138), + QUEST_CONTENT_NOT_UNLOCKED_RECIPE (139), + QUEST_CONTENT_FISHING_SUCC (140), + QUEST_CONTENT_ENTER_ROGUE_DUNGEON (141), + QUEST_CONTENT_USE_WIDGET (142), + QUEST_CONTENT_CAPTURE_SUCC (143), + QUEST_CONTENT_CAPTURE_USE_CAPTURETAG_LIST (144), + QUEST_CONTENT_CAPTURE_USE_MATERIAL_LIST (145), + QUEST_CONTENT_ENTER_VEHICLE (147), + QUEST_CONTENT_SCENE_LEVEL_TAG_EQ (148), + QUEST_CONTENT_LEAVE_SCENE (149), + QUEST_CONTENT_LEAVE_SCENE_RANGE (150), + QUEST_CONTENT_IRODORI_FINISH_FLOWER_COMBINATION (151), + QUEST_CONTENT_IRODORI_POETRY_REACH_MIN_PROGRESS (152), + QUEST_CONTENT_IRODORI_POETRY_FINISH_FILL_POETRY (153), + + QUEST_EXEC_NONE (0), + QUEST_EXEC_DEL_PACK_ITEM (1), + QUEST_EXEC_UNLOCK_POINT (2), + QUEST_EXEC_UNLOCK_AREA (3), + QUEST_EXEC_UNLOCK_FORCE (4), + QUEST_EXEC_LOCK_FORCE (5), + QUEST_EXEC_CHANGE_AVATAR_ELEMET (6), + QUEST_EXEC_REFRESH_GROUP_MONSTER (7), + QUEST_EXEC_SET_IS_FLYABLE (8), + QUEST_EXEC_SET_IS_WEATHER_LOCKED (9), + QUEST_EXEC_SET_IS_GAME_TIME_LOCKED (10), + QUEST_EXEC_SET_IS_TRANSFERABLE (11), + QUEST_EXEC_GRANT_TRIAL_AVATAR (12), + QUEST_EXEC_OPEN_BORED (13), + QUEST_EXEC_ROLLBACK_QUEST (14), + QUEST_EXEC_NOTIFY_GROUP_LUA (15), + QUEST_EXEC_SET_OPEN_STATE (16), + QUEST_EXEC_LOCK_POINT (17), + QUEST_EXEC_DEL_PACK_ITEM_BATCH (18), + QUEST_EXEC_REFRESH_GROUP_SUITE (19), + QUEST_EXEC_REMOVE_TRIAL_AVATAR (20), + QUEST_EXEC_SET_GAME_TIME (21), + QUEST_EXEC_SET_WEATHER_GADGET (22), + QUEST_EXEC_ADD_QUEST_PROGRESS (23), + QUEST_EXEC_NOTIFY_DAILY_TASK (24), + QUEST_EXEC_CREATE_PATTERN_GROUP (25), + QUEST_EXEC_REMOVE_PATTERN_GROUP (26), + QUEST_EXEC_REFRESH_GROUP_SUITE_RANDOM (27), + QUEST_EXEC_ACTIVE_ITEM_GIVING (28), + QUEST_EXEC_DEL_ALL_SPECIFIC_PACK_ITEM (29), + QUEST_EXEC_ROLLBACK_PARENT_QUEST (30), + QUEST_EXEC_LOCK_AVATAR_TEAM (31), + QUEST_EXEC_UNLOCK_AVATAR_TEAM (32), + QUEST_EXEC_UPDATE_PARENT_QUEST_REWARD_INDEX (33), + QUEST_EXEC_SET_DAILY_TASK_VAR (34), + QUEST_EXEC_INC_DAILY_TASK_VAR (35), + QUEST_EXEC_DEC_DAILY_TASK_VAR (36), + QUEST_EXEC_ACTIVE_ACTIVITY_COND_STATE (37), + QUEST_EXEC_INACTIVE_ACTIVITY_COND_STATE (38), + QUEST_EXEC_ADD_CUR_AVATAR_ENERGY (39), + QUEST_EXEC_START_BARGAIN (41), + QUEST_EXEC_STOP_BARGAIN (42), + QUEST_EXEC_SET_QUEST_GLOBAL_VAR (43), + QUEST_EXEC_INC_QUEST_GLOBAL_VAR (44), + QUEST_EXEC_DEC_QUEST_GLOBAL_VAR (45), + QUEST_EXEC_REGISTER_DYNAMIC_GROUP (46), + QUEST_EXEC_UNREGISTER_DYNAMIC_GROUP (47), + QUEST_EXEC_SET_QUEST_VAR (48), + QUEST_EXEC_INC_QUEST_VAR (49), + QUEST_EXEC_DEC_QUEST_VAR (50), + QUEST_EXEC_RANDOM_QUEST_VAR (51), + QUEST_EXEC_ACTIVATE_SCANNING_PIC (52), + QUEST_EXEC_RELOAD_SCENE_TAG (53), + QUEST_EXEC_REGISTER_DYNAMIC_GROUP_ONLY (54), + QUEST_EXEC_CHANGE_SKILL_DEPOT (55), + QUEST_EXEC_ADD_SCENE_TAG (56), + QUEST_EXEC_DEL_SCENE_TAG (57), + QUEST_EXEC_INIT_TIME_VAR (58), + QUEST_EXEC_CLEAR_TIME_VAR (59), + QUEST_EXEC_MODIFY_CLIMATE_AREA (60), + QUEST_EXEC_GRANT_TRIAL_AVATAR_AND_LOCK_TEAM (61), + QUEST_EXEC_CHANGE_MAP_AREA_STATE (62), + QUEST_EXEC_DEACTIVE_ITEM_GIVING (63), + QUEST_EXEC_CHANGE_SCENE_LEVEL_TAG (64), + QUEST_EXEC_UNLOCK_PLAYER_WORLD_SCENE (65), + QUEST_EXEC_LOCK_PLAYER_WORLD_SCENE (66), + QUEST_EXEC_FAIL_MAINCOOP (67), + QUEST_EXEC_MODIFY_WEATHER_AREA (68); + + private final int value; + + QuestTrigger(int id) { + this.value = id; + } + + public int getValue() { + return value; + } +} diff --git a/src/main/java/emu/grasscutter/game/quest/enums/QuestType.java b/src/main/java/emu/grasscutter/game/quest/enums/QuestType.java new file mode 100644 index 000000000..fbbac2ae0 --- /dev/null +++ b/src/main/java/emu/grasscutter/game/quest/enums/QuestType.java @@ -0,0 +1,22 @@ +package emu.grasscutter.game.quest.enums; + +public enum QuestType { + AQ (0), + FQ (1), + LQ (2), + EQ (3), + DQ (4), + IQ (5), + VQ (6), + WQ (7); + + private final int value; + + QuestType(int id) { + this.value = id; + } + + public int getValue() { + return value; + } +} diff --git a/src/main/java/emu/grasscutter/game/quest/enums/ShowQuestGuideType.java b/src/main/java/emu/grasscutter/game/quest/enums/ShowQuestGuideType.java new file mode 100644 index 000000000..d4e985592 --- /dev/null +++ b/src/main/java/emu/grasscutter/game/quest/enums/ShowQuestGuideType.java @@ -0,0 +1,17 @@ +package emu.grasscutter.game.quest.enums; + +public enum ShowQuestGuideType { + QUEST_GUIDE_ITEM_ENABLE (0), + QUEST_GUIDE_ITEM_DISABLE (1), + QUEST_GUIDE_ITEM_MOVE_HIDE (2); + + private final int value; + + ShowQuestGuideType(int id) { + this.value = id; + } + + public int getValue() { + return value; + } +} diff --git a/src/main/java/emu/grasscutter/game/quest/handlers/QuestBaseHandler.java b/src/main/java/emu/grasscutter/game/quest/handlers/QuestBaseHandler.java new file mode 100644 index 000000000..815906f81 --- /dev/null +++ b/src/main/java/emu/grasscutter/game/quest/handlers/QuestBaseHandler.java @@ -0,0 +1,10 @@ +package emu.grasscutter.game.quest.handlers; + +import emu.grasscutter.data.excels.QuestData.QuestCondition; +import emu.grasscutter.game.quest.GameQuest; + +public abstract class QuestBaseHandler { + + public abstract boolean execute(GameQuest quest, QuestCondition condition, int... params); + +} diff --git a/src/main/java/emu/grasscutter/game/shop/ShopInfo.java b/src/main/java/emu/grasscutter/game/shop/ShopInfo.java index ebbeea2cd..4902dbb65 100644 --- a/src/main/java/emu/grasscutter/game/shop/ShopInfo.java +++ b/src/main/java/emu/grasscutter/game/shop/ShopInfo.java @@ -1,7 +1,7 @@ package emu.grasscutter.game.shop; import emu.grasscutter.data.common.ItemParamData; -import emu.grasscutter.data.def.ShopGoodsData; +import emu.grasscutter.data.excels.ShopGoodsData; import java.util.ArrayList; import java.util.List; diff --git a/src/main/java/emu/grasscutter/game/shop/ShopManager.java b/src/main/java/emu/grasscutter/game/shop/ShopManager.java index 2c5d014f5..1010e14a8 100644 --- a/src/main/java/emu/grasscutter/game/shop/ShopManager.java +++ b/src/main/java/emu/grasscutter/game/shop/ShopManager.java @@ -2,20 +2,25 @@ package emu.grasscutter.game.shop; import com.google.gson.reflect.TypeToken; import emu.grasscutter.Grasscutter; +import emu.grasscutter.data.DataLoader; import emu.grasscutter.data.GameData; import emu.grasscutter.data.common.ItemParamData; -import emu.grasscutter.data.def.ShopGoodsData; +import emu.grasscutter.data.excels.ShopGoodsData; import emu.grasscutter.server.game.GameServer; import emu.grasscutter.utils.Utils; import it.unimi.dsi.fastutil.ints.Int2ObjectMap; import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap; import java.io.FileReader; +import java.io.InputStreamReader; +import java.io.Reader; import java.util.ArrayList; import java.util.Collection; import java.util.Iterator; import java.util.List; +import static emu.grasscutter.Configuration.*; + public class ShopManager { private final GameServer server; @@ -56,7 +61,7 @@ public class ShopManager { } private void loadShop() { - try (FileReader fileReader = new FileReader(Grasscutter.getConfig().DATA_FOLDER + "Shop.json")) { + try (Reader fileReader = new InputStreamReader(DataLoader.load("Shop.json"))) { getShopData().clear(); List banners = Grasscutter.getGsonFactory().fromJson(fileReader, TypeToken.getParameterized(Collection.class, ShopTable.class).getType()); if(banners.size() > 0) { @@ -84,7 +89,7 @@ public class ShopManager { Grasscutter.getLogger().error("Unable to load shop data. Shop data size is 0."); } - if (Grasscutter.getConfig().getGameServerOptions().EnableOfficialShop) { + if (GAME_OPTIONS.enableShopItems) { GameData.getShopGoodsDataEntries().forEach((k, v) -> { if (!getShopData().containsKey(k.intValue())) getShopData().put(k.intValue(), new ArrayList<>()); @@ -100,7 +105,7 @@ public class ShopManager { } private void loadShopChest() { - try (FileReader fileReader = new FileReader(Grasscutter.getConfig().DATA_FOLDER + "ShopChest.json")) { + try (Reader fileReader = new InputStreamReader(DataLoader.load("ShopChest.json"))) { getShopChestData().clear(); List shopChestTableList = Grasscutter.getGsonFactory().fromJson(fileReader, TypeToken.getParameterized(Collection.class, ShopChestTable.class).getType()); if (shopChestTableList.size() > 0) { @@ -115,7 +120,7 @@ public class ShopManager { } private void loadShopChestBatchUse() { - try (FileReader fileReader = new FileReader(Grasscutter.getConfig().DATA_FOLDER + "ShopChestBatchUse.json")) { + try (Reader fileReader = new InputStreamReader(DataLoader.load("ShopChestBatchUse.json"))) { getShopChestBatchUseData().clear(); List shopChestBatchUseTableList = Grasscutter.getGsonFactory().fromJson(fileReader, TypeToken.getParameterized(Collection.class, ShopChestBatchUseTable.class).getType()); if (shopChestBatchUseTableList.size() > 0) { diff --git a/src/main/java/emu/grasscutter/game/tower/TowerLevelRecord.java b/src/main/java/emu/grasscutter/game/tower/TowerLevelRecord.java new file mode 100644 index 000000000..5a65f63ed --- /dev/null +++ b/src/main/java/emu/grasscutter/game/tower/TowerLevelRecord.java @@ -0,0 +1,64 @@ +package emu.grasscutter.game.tower; + +import dev.morphia.annotations.Entity; + +import java.util.HashMap; +import java.util.Map; + +@Entity +public class TowerLevelRecord { + /** + * floorId in config + */ + private int floorId; + /** + * LevelId - Stars + */ + private Map passedLevelMap; + + private int floorStarRewardProgress; + + public TowerLevelRecord setLevelStars(int levelId, int stars){ + passedLevelMap.put(levelId, stars); + return this; + } + + public int getStarCount() { + return passedLevelMap.values().stream().mapToInt(Integer::intValue).sum(); + } + + public TowerLevelRecord(){ + + } + + public TowerLevelRecord(int floorId){ + this.floorId = floorId; + this.passedLevelMap = new HashMap<>(); + this.floorStarRewardProgress = 0; + } + + public int getFloorId() { + return floorId; + } + + public void setFloorId(int floorId) { + this.floorId = floorId; + } + + public Map getPassedLevelMap() { + return passedLevelMap; + } + + public void setPassedLevelMap(Map passedLevelMap) { + this.passedLevelMap = passedLevelMap; + } + + public int getFloorStarRewardProgress() { + return floorStarRewardProgress; + } + + public void setFloorStarRewardProgress(int floorStarRewardProgress) { + this.floorStarRewardProgress = floorStarRewardProgress; + } + +} diff --git a/src/main/java/emu/grasscutter/game/tower/TowerManager.java b/src/main/java/emu/grasscutter/game/tower/TowerManager.java index 51f840663..ab5e1cf0c 100644 --- a/src/main/java/emu/grasscutter/game/tower/TowerManager.java +++ b/src/main/java/emu/grasscutter/game/tower/TowerManager.java @@ -3,15 +3,15 @@ package emu.grasscutter.game.tower; import dev.morphia.annotations.Entity; import dev.morphia.annotations.Transient; import emu.grasscutter.data.GameData; -import emu.grasscutter.data.def.TowerLevelData; +import emu.grasscutter.data.excels.TowerLevelData; import emu.grasscutter.game.dungeons.DungeonSettleListener; import emu.grasscutter.game.dungeons.TowerDungeonSettleListener; import emu.grasscutter.game.player.Player; -import emu.grasscutter.server.packet.send.PacketTowerCurLevelRecordChangeNotify; - -import emu.grasscutter.server.packet.send.PacketTowerEnterLevelRsp; +import emu.grasscutter.server.packet.send.*; +import java.util.HashMap; import java.util.List; +import java.util.Map; @Entity public class TowerManager { @@ -25,11 +25,19 @@ public class TowerManager { this.player = player; } + /** + * the floor players chose + */ private int currentFloorId; private int currentLevel; @Transient private int currentLevelId; + /** + * floorId - Record + */ + private Map recordMap; + @Transient private int entryScene; @@ -37,66 +45,114 @@ public class TowerManager { return currentFloorId; } + public int getCurrentLevelId(){ + return this.currentLevelId + currentLevel; + } + + /** + * form 1-3 + */ + public int getCurrentLevel(){ + return currentLevel + 1; + } private static final List towerDungeonSettleListener = List.of(new TowerDungeonSettleListener()); + + public Map getRecordMap() { + if(recordMap == null){ + recordMap = new HashMap<>(); + recordMap.put(1001, new TowerLevelRecord(1001)); + } + return recordMap; + } + public void teamSelect(int floor, List> towerTeams) { var floorData = GameData.getTowerFloorDataMap().get(floor); this.currentFloorId = floorData.getFloorId(); this.currentLevel = 0; this.currentLevelId = GameData.getTowerLevelDataMap().values().stream() - .filter(x -> x.getLevelId() == floorData.getLevelId() && x.getLevelIndex() == 1) + .filter(x -> x.getLevelGroupId() == floorData.getLevelGroupId() && x.getLevelIndex() == 1) .findFirst() - .map(TowerLevelData::getID) + .map(TowerLevelData::getId) .orElse(0); if (entryScene == 0){ entryScene = player.getSceneId(); } - player.getTeamManager().setupTemporaryTeam(towerTeams); } public void enterLevel(int enterPointId) { - var levelData = GameData.getTowerLevelDataMap().get(currentLevelId + currentLevel); + var levelData = GameData.getTowerLevelDataMap().get(getCurrentLevelId()); - this.currentLevel++; - var id = levelData.getDungeonId(); + var dungeonId = levelData.getDungeonId(); notifyCurLevelRecordChange(); // use team user choose player.getTeamManager().useTemporaryTeam(0); - player.getServer().getDungeonManager().handoffDungeon(player, id, + player.getServer().getDungeonManager().handoffDungeon(player, dungeonId, towerDungeonSettleListener); // make sure user can exit dungeon correctly player.getScene().setPrevScene(entryScene); player.getScene().setPrevScenePoint(enterPointId); - player.getSession().send(new PacketTowerEnterLevelRsp(currentFloorId, currentLevel)); - + player.getSession().send(new PacketTowerEnterLevelRsp(currentFloorId, getCurrentLevel())); + // stop using skill + player.getSession().send(new PacketCanUseSkillNotify(false)); + // notify the cond of stars + player.getSession().send(new PacketTowerLevelStarCondNotify(currentFloorId, getCurrentLevel())); } public void notifyCurLevelRecordChange(){ - player.getSession().send(new PacketTowerCurLevelRecordChangeNotify(currentFloorId, currentLevel)); + player.getSession().send(new PacketTowerCurLevelRecordChangeNotify(currentFloorId, getCurrentLevel())); } - public void notifyCurLevelRecordChangeWhenDone(){ - player.getSession().send(new PacketTowerCurLevelRecordChangeNotify(currentFloorId, currentLevel + 1)); + public void notifyCurLevelRecordChangeWhenDone(int stars){ + if(!recordMap.containsKey(currentFloorId)){ + recordMap.put(currentFloorId, + new TowerLevelRecord(currentFloorId).setLevelStars(getCurrentLevelId(),stars)); + }else{ + recordMap.put(currentFloorId, + recordMap.get(currentFloorId).setLevelStars(getCurrentLevelId(),stars)); + } + + this.currentLevel++; + + if(!hasNextLevel()){ + // set up the next floor + recordMap.putIfAbsent(getNextFloorId(), new TowerLevelRecord(getNextFloorId())); + player.getSession().send(new PacketTowerCurLevelRecordChangeNotify(getNextFloorId(), 1)); + }else{ + player.getSession().send(new PacketTowerCurLevelRecordChangeNotify(currentFloorId, getCurrentLevel())); + } } public boolean hasNextLevel(){ return this.currentLevel < 3; } - public int getNextFloorId() { - if(hasNextLevel()){ - return 0; - } - this.currentFloorId++; - return this.currentFloorId; + return player.getServer().getTowerScheduleManager().getNextFloorId(this.currentFloorId); + } + public boolean hasNextFloor(){ + return player.getServer().getTowerScheduleManager().getNextFloorId(this.currentFloorId) > 0; } public void clearEntry() { this.entryScene = 0; } + + public boolean canEnterScheduleFloor(){ + if(!recordMap.containsKey(player.getServer().getTowerScheduleManager().getLastEntranceFloor())){ + return false; + } + return recordMap.get(player.getServer().getTowerScheduleManager().getLastEntranceFloor()) + .getStarCount() >= 6; + } + + public void mirrorTeamSetUp(int teamId) { + // use team user choose + player.getTeamManager().useTemporaryTeam(teamId); + player.sendPacket(new PacketTowerMiddleLevelChangeTeamNotify()); + } } diff --git a/src/main/java/emu/grasscutter/game/tower/TowerScheduleConfig.java b/src/main/java/emu/grasscutter/game/tower/TowerScheduleConfig.java new file mode 100644 index 000000000..35afbc7ba --- /dev/null +++ b/src/main/java/emu/grasscutter/game/tower/TowerScheduleConfig.java @@ -0,0 +1,35 @@ +package emu.grasscutter.game.tower; + +import java.util.Date; + +public class TowerScheduleConfig { + private int scheduleId; + + private Date scheduleStartTime; + private Date nextScheduleChangeTime; + + + public int getScheduleId() { + return scheduleId; + } + + public void setScheduleId(int scheduleId) { + this.scheduleId = scheduleId; + } + + public Date getScheduleStartTime() { + return scheduleStartTime; + } + + public void setScheduleStartTime(Date scheduleStartTime) { + this.scheduleStartTime = scheduleStartTime; + } + + public Date getNextScheduleChangeTime() { + return nextScheduleChangeTime; + } + + public void setNextScheduleChangeTime(Date nextScheduleChangeTime) { + this.nextScheduleChangeTime = nextScheduleChangeTime; + } +} diff --git a/src/main/java/emu/grasscutter/game/tower/TowerScheduleManager.java b/src/main/java/emu/grasscutter/game/tower/TowerScheduleManager.java new file mode 100644 index 000000000..a2b8c0396 --- /dev/null +++ b/src/main/java/emu/grasscutter/game/tower/TowerScheduleManager.java @@ -0,0 +1,87 @@ +package emu.grasscutter.game.tower; + +import emu.grasscutter.Grasscutter; +import emu.grasscutter.data.DataLoader; +import emu.grasscutter.data.GameData; +import emu.grasscutter.data.excels.TowerScheduleData; +import emu.grasscutter.server.game.GameServer; + +import java.io.FileReader; +import java.io.InputStreamReader; +import java.io.Reader; +import java.util.List; + +import static emu.grasscutter.Configuration.*; + +public class TowerScheduleManager { + private final GameServer gameServer; + + public GameServer getGameServer() { + return gameServer; + } + + public TowerScheduleManager(GameServer gameServer) { + this.gameServer = gameServer; + this.load(); + } + + private TowerScheduleConfig towerScheduleConfig; + + public synchronized void load(){ + try (Reader fileReader = new InputStreamReader(DataLoader.load("TowerSchedule.json"))) { + towerScheduleConfig = Grasscutter.getGsonFactory().fromJson(fileReader, TowerScheduleConfig.class); + } catch (Exception e) { + Grasscutter.getLogger().error("Unable to load tower schedule config.", e); + } + } + + public TowerScheduleConfig getTowerScheduleConfig() { + return towerScheduleConfig; + } + + public TowerScheduleData getCurrentTowerScheduleData(){ + var data = GameData.getTowerScheduleDataMap().get(towerScheduleConfig.getScheduleId()); + if(data == null){ + Grasscutter.getLogger().error("Could not get current tower schedule data by schedule id {}, please check your resource files", + towerScheduleConfig.getScheduleId()); + } + + return data; + } + + public List getScheduleFloors() { + return getCurrentTowerScheduleData().getSchedules().get(0).getFloorList(); + } + + public int getNextFloorId(int floorId){ + var entranceFloors = getCurrentTowerScheduleData().getEntranceFloorId(); + var scheduleFloors = getScheduleFloors(); + var nextId = 0; + + // find in entrance floors first + for(int i=0;i x.getConfigId() == configId) + .findFirst() + .orElse(null); + } /** * @return the autoCloseTime */ @@ -281,7 +284,7 @@ public class Scene { private void removePlayerAvatars(Player player) { Iterator it = player.getTeamManager().getActiveTeam().iterator(); while (it.hasNext()) { - this.removeEntity(it.next(), VisionType.VISION_REMOVE); + this.removeEntity(it.next(), VisionType.VISION_TYPE_REMOVE); it.remove(); } } @@ -324,7 +327,7 @@ public class Scene { this.addEntityDirectly(entity); } - this.broadcastPacket(new PacketSceneEntityAppearNotify(entities, VisionType.VISION_BORN)); + this.broadcastPacket(new PacketSceneEntityAppearNotify(entities, VisionType.VISION_TYPE_BORN)); } private GameEntity removeEntityDirectly(GameEntity entity) { @@ -332,7 +335,7 @@ public class Scene { } public void removeEntity(GameEntity entity) { - this.removeEntity(entity, VisionType.VISION_DIE); + this.removeEntity(entity, VisionType.VISION_TYPE_DIE); } public synchronized void removeEntity(GameEntity entity, VisionType visionType) { @@ -345,8 +348,8 @@ public class Scene { public synchronized void replaceEntity(EntityAvatar oldEntity, EntityAvatar newEntity) { this.removeEntityDirectly(oldEntity); this.addEntityDirectly(newEntity); - this.broadcastPacket(new PacketSceneEntityDisappearNotify(oldEntity, VisionType.VISION_REPLACE)); - this.broadcastPacket(new PacketSceneEntityAppearNotify(newEntity, VisionType.VISION_REPLACE, oldEntity.getId())); + this.broadcastPacket(new PacketSceneEntityDisappearNotify(oldEntity, VisionType.VISION_TYPE_REPLACE)); + this.broadcastPacket(new PacketSceneEntityAppearNotify(newEntity, VisionType.VISION_TYPE_REPLACE, oldEntity.getId())); } public void showOtherEntities(Player player) { @@ -360,7 +363,7 @@ public class Scene { entities.add(entity); } - player.sendPacket(new PacketSceneEntityAppearNotify(entities, VisionType.VISION_MEET)); + player.sendPacket(new PacketSceneEntityAppearNotify(entities, VisionType.VISION_TYPE_MEET)); } public void handleAttack(AttackResult result) { @@ -379,30 +382,23 @@ public class Scene { } // Sanity check - if (target.getFightProperties() == null) { - return; - } - - // Lose hp - target.addFightProperty(FightProperty.FIGHT_PROP_CUR_HP, -result.getDamage()); - - // Check if dead - boolean isDead = false; - if (target.getFightProperty(FightProperty.FIGHT_PROP_CUR_HP) <= 0f) { - target.setFightProperty(FightProperty.FIGHT_PROP_CUR_HP, 0f); - isDead = true; - } - - // Packets - this.broadcastPacket(new PacketEntityFightPropUpdateNotify(target, FightProperty.FIGHT_PROP_CUR_HP)); - - // Check if dead - if (isDead) { - this.killEntity(target, result.getAttackerId()); - } + target.damage(result.getDamage(), result.getAttackerId()); } public void killEntity(GameEntity target, int attackerId) { + GameEntity attacker = getEntityById(attackerId); + + //Check codex + if (attacker instanceof EntityClientGadget) { + var clientGadgetOwner = getEntityById(((EntityClientGadget) attacker).getOwnerEntityId()); + if(clientGadgetOwner instanceof EntityAvatar) { + ((EntityClientGadget) attacker).getOwner().getCodex().checkAnimal(target, CodexAnimalData.CodexAnimalUnlockCondition.CODEX_COUNT_TYPE_KILL); + } + } + else if (attacker instanceof EntityAvatar) { + ((EntityAvatar) attacker).getPlayer().getCodex().checkAnimal(target, CodexAnimalData.CodexAnimalUnlockCondition.CODEX_COUNT_TYPE_KILL); + } + // Packet this.broadcastPacket(new PacketLifeStateChangeNotify(attackerId, target, LifeState.LIFE_DEAD)); @@ -469,7 +465,11 @@ public class Scene { continue; } - EntityMonster entity = new EntityMonster(this, data, entry.getPos(), worldLevelOverride > 0 ? worldLevelOverride : entry.getLevel()); + int level = worldLevelOverride > 0 ? worldLevelOverride + entry.getLevel() - 22 : entry.getLevel(); + level = level >= 100 ? 100 : level; + level = level <= 0 ? 1 : level; + + EntityMonster entity = new EntityMonster(this, data, entry.getPos(), level); entity.getRotation().set(entry.getRot()); entity.setGroupId(entry.getGroup().getGroupId()); entity.setPoseId(entry.getPoseId()); @@ -491,11 +491,11 @@ public class Scene { if (toAdd.size() > 0) { toAdd.stream().forEach(this::addEntityDirectly); - this.broadcastPacket(new PacketSceneEntityAppearNotify(toAdd, VisionType.VISION_BORN)); + this.broadcastPacket(new PacketSceneEntityAppearNotify(toAdd, VisionType.VISION_TYPE_BORN)); } if (toRemove.size() > 0) { toRemove.stream().forEach(this::removeEntityDirectly); - this.broadcastPacket(new PacketSceneEntityDisappearNotify(toRemove, VisionType.VISION_REMOVE)); + this.broadcastPacket(new PacketSceneEntityDisappearNotify(toRemove, VisionType.VISION_TYPE_REMOVE)); } } @@ -568,7 +568,7 @@ public class Scene { if (toRemove.size() > 0) { toRemove.stream().forEach(this::removeEntityDirectly); - this.broadcastPacket(new PacketSceneEntityDisappearNotify(toRemove, VisionType.VISION_REMOVE)); + this.broadcastPacket(new PacketSceneEntityDisappearNotify(toRemove, VisionType.VISION_TYPE_REMOVE)); } for (SceneGroup group : block.groups) { @@ -613,7 +613,7 @@ public class Scene { return; } - this.broadcastPacketToOthers(gadget.getOwner(), new PacketSceneEntityDisappearNotify(gadget, VisionType.VISION_DIE)); + this.broadcastPacketToOthers(gadget.getOwner(), new PacketSceneEntityDisappearNotify(gadget, VisionType.VISION_TYPE_DIE)); } // Broadcasting diff --git a/src/main/java/emu/grasscutter/game/world/World.java b/src/main/java/emu/grasscutter/game/world/World.java index 46c80cd25..ac6ab4a72 100644 --- a/src/main/java/emu/grasscutter/game/world/World.java +++ b/src/main/java/emu/grasscutter/game/world/World.java @@ -1,39 +1,25 @@ package emu.grasscutter.game.world; import java.util.ArrayList; -import java.util.Collection; import java.util.Collections; import java.util.Iterator; -import java.util.LinkedList; import java.util.List; import java.util.stream.Collectors; -import emu.grasscutter.game.entity.GameEntity; import emu.grasscutter.game.player.Player; import emu.grasscutter.game.player.Player.SceneLoadState; -import emu.grasscutter.game.props.ClimateType; import emu.grasscutter.game.props.EnterReason; import emu.grasscutter.game.props.EntityIdType; -import emu.grasscutter.game.props.FightProperty; -import emu.grasscutter.game.props.LifeState; +import emu.grasscutter.game.props.SceneType; import emu.grasscutter.data.GameData; -import emu.grasscutter.data.def.DungeonData; -import emu.grasscutter.data.def.SceneData; -import emu.grasscutter.game.entity.EntityAvatar; -import emu.grasscutter.game.entity.EntityClientGadget; -import emu.grasscutter.game.entity.EntityBaseGadget; +import emu.grasscutter.data.excels.DungeonData; +import emu.grasscutter.data.excels.SceneData; import emu.grasscutter.net.packet.BasePacket; -import emu.grasscutter.net.proto.AttackResultOuterClass.AttackResult; import emu.grasscutter.net.proto.EnterTypeOuterClass.EnterType; -import emu.grasscutter.net.proto.VisionTypeOuterClass.VisionType; import emu.grasscutter.scripts.data.SceneConfig; import emu.grasscutter.server.game.GameServer; import emu.grasscutter.server.packet.send.PacketDelTeamEntityNotify; -import emu.grasscutter.server.packet.send.PacketEntityFightPropUpdateNotify; -import emu.grasscutter.server.packet.send.PacketLifeStateChangeNotify; import emu.grasscutter.server.packet.send.PacketPlayerEnterSceneNotify; -import emu.grasscutter.server.packet.send.PacketSceneEntityAppearNotify; -import emu.grasscutter.server.packet.send.PacketSceneEntityDisappearNotify; import emu.grasscutter.server.packet.send.PacketScenePlayerInfoNotify; import emu.grasscutter.server.packet.send.PacketSyncScenePlayTeamEntityNotify; import emu.grasscutter.server.packet.send.PacketSyncTeamEntityNotify; @@ -207,7 +193,7 @@ public class World implements Iterable { World world = new World(victim); world.addPlayer(victim); - victim.sendPacket(new PacketPlayerEnterSceneNotify(victim, EnterType.ENTER_SELF, EnterReason.TeamKick, victim.getSceneId(), victim.getPos())); + victim.sendPacket(new PacketPlayerEnterSceneNotify(victim, EnterType.ENTER_TYPE_SELF, EnterReason.TeamKick, victim.getSceneId(), victim.getPos())); } } } @@ -274,14 +260,17 @@ public class World implements Iterable { } // Get enter types - EnterType enterType = EnterType.ENTER_JUMP; + EnterType enterType = EnterType.ENTER_TYPE_JUMP; EnterReason enterReason = EnterReason.TransPoint; if (dungeonData != null) { - enterType = EnterType.ENTER_DUNGEON; + enterType = EnterType.ENTER_TYPE_DUNGEON; enterReason = EnterReason.DungeonEnter; } else if (oldScene == newScene) { - enterType = EnterType.ENTER_GOTO; + enterType = EnterType.ENTER_TYPE_GOTO; + } else if (newScene.getSceneType() == SceneType.SCENE_HOME_WORLD) { + // Home + enterType = EnterType.ENTER_TYPE_SELF_HOME; } // Teleport packet diff --git a/src/main/java/emu/grasscutter/net/packet/PacketOpcodes.java b/src/main/java/emu/grasscutter/net/packet/PacketOpcodes.java index 8e77504d6..ec737b22f 100644 --- a/src/main/java/emu/grasscutter/net/packet/PacketOpcodes.java +++ b/src/main/java/emu/grasscutter/net/packet/PacketOpcodes.java @@ -1,1574 +1,1522 @@ package emu.grasscutter.net.packet; -import java.util.Arrays; -import java.util.List; +import java.util.HashSet; public class PacketOpcodes { // Empty public static final int NONE = 0; // Opcodes - public static final int AbilityChangeNotify = 1179; - public static final int AbilityInvocationFailNotify = 1137; - public static final int AbilityInvocationFixedNotify = 1160; - public static final int AbilityInvocationsNotify = 1133; - public static final int AcceptCityReputationRequestReq = 2845; - public static final int AcceptCityReputationRequestRsp = 2875; - public static final int AchievementAllDataNotify = 1155; - public static final int AchievementUpdateNotify = 1146; - public static final int ActivityCoinInfoNotify = 2056; - public static final int ActivityCondStateChangeNotify = 2162; - public static final int ActivityDisableTransferPointInteractionNotify = 8295; - public static final int ActivityInfoNotify = 2023; - public static final int ActivityPlayOpenAnimNotify = 2164; - public static final int ActivitySaleChangeNotify = 2043; - public static final int ActivityScheduleInfoNotify = 2180; - public static final int ActivityScheduleInfoNotify_1 = 2187; - public static final int ActivitySelectAvatarCardReq = 2153; - public static final int ActivitySelectAvatarCardRsp = 2069; - public static final int ActivityTakeAllScoreRewardReq = 8708; - public static final int ActivityTakeAllScoreRewardRsp = 8836; - public static final int ActivityTakeScoreRewardReq = 8767; - public static final int ActivityTakeScoreRewardRsp = 8445; - public static final int ActivityTakeWatcherRewardBatchReq = 2027; - public static final int ActivityTakeWatcherRewardBatchRsp = 2036; - public static final int ActivityTakeWatcherRewardReq = 2074; - public static final int ActivityTakeWatcherRewardRsp = 2180; - public static final int ActivityTakeWatcherRewardRsp_1 = 2187; - public static final int ActivityUpdateWatcherNotify = 2101; - public static final int AddBlacklistReq = 4067; + public static final int AbilityChangeNotify = 1155; + public static final int AbilityInvocationFailNotify = 1132; + public static final int AbilityInvocationFixedNotify = 1109; + public static final int AbilityInvocationsNotify = 1131; + public static final int AcceptCityReputationRequestReq = 2835; + public static final int AcceptCityReputationRequestRsp = 2849; + public static final int AchievementAllDataNotify = 2668; + public static final int AchievementUpdateNotify = 2659; + public static final int ActivityCoinInfoNotify = 2094; + public static final int ActivityCondStateChangeNotify = 2120; + public static final int ActivityDisableTransferPointInteractionNotify = 8686; + public static final int ActivityInfoNotify = 2125; + public static final int ActivityPlayOpenAnimNotify = 2146; + public static final int ActivitySaleChangeNotify = 2109; + public static final int ActivityScheduleInfoNotify = 2033; + public static final int ActivitySelectAvatarCardReq = 2030; + public static final int ActivitySelectAvatarCardRsp = 2127; + public static final int ActivityTakeAllScoreRewardReq = 8424; + public static final int ActivityTakeAllScoreRewardRsp = 8692; + public static final int ActivityTakeScoreRewardReq = 8321; + public static final int ActivityTakeScoreRewardRsp = 8218; + public static final int ActivityTakeWatcherRewardBatchReq = 2079; + public static final int ActivityTakeWatcherRewardBatchRsp = 2150; + public static final int ActivityTakeWatcherRewardReq = 2090; + public static final int ActivityTakeWatcherRewardRsp = 2025; + public static final int ActivityUpdateWatcherNotify = 2172; + public static final int AddBlacklistReq = 4050; public static final int AddBlacklistRsp = 4020; - public static final int AddFriendNotify = 4026; - public static final int AddNoGachaAvatarCardNotify = 1740; - public static final int AddQuestContentProgressReq = 493; - public static final int AddQuestContentProgressRsp = 444; - public static final int AddRandTaskInfoNotify = 147; - public static final int AddSeenMonsterNotify = 242; - public static final int AdjustWorldLevelReq = 104; - public static final int AdjustWorldLevelRsp = 106; - public static final int AllCoopInfoNotify = 1985; - public static final int AllMarkPointNotify = 3462; - public static final int AllSeenMonsterNotify = 276; - public static final int AllWidgetDataNotify = 4284; - public static final int AnchorPointDataNotify = 4285; - public static final int AnchorPointOpReq = 4298; - public static final int AnchorPointOpRsp = 4263; - public static final int AnimatorForceSetAirMoveNotify = 308; - public static final int AntiAddictNotify = 177; - public static final int ArenaChallengeFinishNotify = 2083; - public static final int AskAddFriendNotify = 4062; - public static final int AskAddFriendReq = 4037; - public static final int AskAddFriendRsp = 4093; - public static final int AsterLargeInfoNotify = 2133; - public static final int AsterLittleInfoNotify = 2058; - public static final int AsterMidCampInfoNotify = 2115; - public static final int AsterMidInfoNotify = 2151; - public static final int AsterMiscInfoNotify = 2098; - public static final int AsterProgressInfoNotify = 2065; - public static final int AvatarAddNotify = 1759; - public static final int AvatarBuffAddNotify = 367; + public static final int AddFriendNotify = 4024; + public static final int AddNoGachaAvatarCardNotify = 1648; + public static final int AddQuestContentProgressReq = 417; + public static final int AddQuestContentProgressRsp = 485; + public static final int AddRandTaskInfoNotify = 116; + public static final int AddSeenMonsterNotify = 251; + public static final int AdjustWorldLevelReq = 195; + public static final int AdjustWorldLevelRsp = 115; + public static final int AllCoopInfoNotify = 1968; + public static final int AllMarkPointNotify = 3195; + public static final int AllSeenMonsterNotify = 226; + public static final int AllWidgetDataNotify = 4273; + public static final int AnchorPointDataNotify = 4268; + public static final int AnchorPointOpReq = 4254; + public static final int AnchorPointOpRsp = 4278; + public static final int AnimatorForceSetAirMoveNotify = 346; + public static final int AntiAddictNotify = 148; + public static final int ArenaChallengeFinishNotify = 2082; + public static final int AskAddFriendNotify = 4038; + public static final int AskAddFriendReq = 4032; + public static final int AskAddFriendRsp = 4017; + public static final int AsterLargeInfoNotify = 2192; + public static final int AsterLittleInfoNotify = 2188; + public static final int AsterMidCampInfoNotify = 2020; + public static final int AsterMidInfoNotify = 2121; + public static final int AsterMiscInfoNotify = 2187; + public static final int AsterProgressInfoNotify = 2193; + public static final int AvatarAddNotify = 1757; + public static final int AvatarBuffAddNotify = 350; public static final int AvatarBuffDelNotify = 320; - public static final int AvatarCardChangeReq = 667; + public static final int AvatarCardChangeReq = 650; public static final int AvatarCardChangeRsp = 620; - public static final int AvatarChangeAnimHashReq = 1630; - public static final int AvatarChangeAnimHashRsp = 1747; - public static final int AvatarChangeCostumeNotify = 1748; - public static final int AvatarChangeCostumeReq = 1650; - public static final int AvatarChangeCostumeRsp = 1632; - public static final int AvatarChangeElementTypeReq = 1741; - public static final int AvatarChangeElementTypeRsp = 1626; - public static final int AvatarDataNotify = 1757; - public static final int AvatarDelNotify = 1624; - public static final int AvatarDieAnimationEndReq = 1635; - public static final int AvatarDieAnimationEndRsp = 1638; - public static final int AvatarEnterElementViewNotify = 366; - public static final int AvatarEquipAffixStartNotify = 1734; - public static final int AvatarEquipChangeNotify = 674; - public static final int AvatarExpeditionAllDataReq = 1721; - public static final int AvatarExpeditionAllDataRsp = 1800; - public static final int AvatarExpeditionCallBackReq = 1607; - public static final int AvatarExpeditionCallBackRsp = 1783; - public static final int AvatarExpeditionDataNotify = 1777; - public static final int AvatarExpeditionGetRewardReq = 1604; - public static final int AvatarExpeditionGetRewardRsp = 1731; - public static final int AvatarExpeditionStartReq = 1788; - public static final int AvatarExpeditionStartRsp = 1786; - public static final int AvatarFetterDataNotify = 1718; - public static final int AvatarFetterLevelRewardReq = 1717; - public static final int AvatarFetterLevelRewardRsp = 1690; - public static final int AvatarFightPropNotify = 1237; - public static final int AvatarFightPropUpdateNotify = 1293; - public static final int AvatarFlycloakChangeNotify = 1761; - public static final int AvatarFollowRouteNotify = 3210; - public static final int AvatarGainCostumeNotify = 1778; - public static final int AvatarGainFlycloakNotify = 1676; - public static final int AvatarLifeStateChangeNotify = 1245; - public static final int AvatarPromoteGetRewardReq = 1784; - public static final int AvatarPromoteGetRewardRsp = 1776; - public static final int AvatarPromoteReq = 1661; - public static final int AvatarPromoteRsp = 1712; - public static final int AvatarPropChangeReasonNotify = 1275; - public static final int AvatarPropNotify = 1279; - public static final int AvatarSatiationDataNotify = 1639; - public static final int AvatarSkillChangeNotify = 1088; - public static final int AvatarSkillDepotChangeNotify = 1015; - public static final int AvatarSkillInfoNotify = 1045; - public static final int AvatarSkillMaxChargeCountNotify = 1044; - public static final int AvatarSkillUpgradeReq = 1091; - public static final int AvatarSkillUpgradeRsp = 1097; - public static final int AvatarTeamUpdateNotify = 1649; - public static final int AvatarUnlockTalentNotify = 1010; - public static final int AvatarUpgradeReq = 1660; - public static final int AvatarUpgradeRsp = 1735; - public static final int AvatarWearFlycloakReq = 1677; - public static final int AvatarWearFlycloakRsp = 1782; - public static final int BackMyWorldReq = 219; - public static final int BackMyWorldRsp = 269; - public static final int BargainOfferPriceReq = 409; - public static final int BargainOfferPriceRsp = 465; - public static final int BargainStartNotify = 489; - public static final int BargainTerminateNotify = 403; - public static final int BattlePassAllDataNotify = 2635; - public static final int BattlePassBuySuccNotify = 2612; - public static final int BattlePassCurScheduleUpdateNotify = 2648; - public static final int BattlePassMissionDelNotify = 2645; - public static final int BattlePassMissionUpdateNotify = 2625; - public static final int BeginCameraSceneLookNotify = 261; - public static final int BigTalentPointConvertReq = 1037; - public static final int BigTalentPointConvertRsp = 1093; - public static final int BlessingAcceptAllGivePicReq = 2176; - public static final int BlessingAcceptAllGivePicRsp = 2050; - public static final int BlessingAcceptGivePicReq = 2134; - public static final int BlessingAcceptGivePicRsp = 2117; - public static final int BlessingGetAllRecvPicRecordListReq = 2090; - public static final int BlessingGetAllRecvPicRecordListRsp = 2140; - public static final int BlessingGetFriendPicListReq = 2077; - public static final int BlessingGetFriendPicListRsp = 2182; - public static final int BlessingGiveFriendPicReq = 2161; - public static final int BlessingGiveFriendPicRsp = 2076; - public static final int BlessingRecvFriendPicNotify = 2184; - public static final int BlessingRedeemRewardReq = 2172; - public static final int BlessingRedeemRewardRsp = 2039; - public static final int BlessingScanReq = 2186; - public static final int BlessingScanRsp = 2007; - public static final int BlitzRushParkourRestartReq = 8212; - public static final int BlitzRushParkourRestartRsp = 8962; - public static final int BlossomBriefInfoNotify = 2710; - public static final int BlossomChestCreateNotify = 2793; - public static final int BlossomChestInfoNotify = 845; - public static final int BonusActivityInfoReq = 2597; - public static final int BonusActivityInfoRsp = 2588; - public static final int BonusActivityUpdateNotify = 2591; - public static final int BossChestActivateNotify = 844; - public static final int BounceConjuringSettleNotify = 8339; - public static final int BuoyantCombatSettleNotify = 9000; - public static final int BuyBattlePassLevelReq = 2639; - public static final int BuyBattlePassLevelRsp = 2621; - public static final int BuyGoodsReq = 710; - public static final int BuyGoodsRsp = 715; - public static final int BuyResinReq = 630; - public static final int BuyResinRsp = 647; - public static final int CalcWeaponUpgradeReturnItemsReq = 643; - public static final int CalcWeaponUpgradeReturnItemsRsp = 686; - public static final int CancelCityReputationRequestReq = 2834; - public static final int CancelCityReputationRequestRsp = 2879; - public static final int CancelCoopTaskReq = 1989; - public static final int CancelCoopTaskRsp = 1971; - public static final int CancelFinishParentQuestNotify = 492; - public static final int CanUseSkillNotify = 1055; - public static final int CardProductRewardNotify = 4148; - public static final int ChallengeDataNotify = 983; - public static final int ChallengeRecordNotify = 909; - public static final int ChangeAvatarReq = 1743; - public static final int ChangeAvatarRsp = 1672; - public static final int ChangeGameTimeReq = 175; - public static final int ChangeGameTimeRsp = 134; - public static final int ChangeMailStarNotify = 1497; - public static final int ChangeMpTeamAvatarReq = 1794; - public static final int ChangeMpTeamAvatarRsp = 1629; - public static final int ChangeServerGlobalValueNotify = 65; - public static final int ChangeTeamNameReq = 1793; - public static final int ChangeTeamNameRsp = 1707; - public static final int ChangeWorldToSingleModeNotify = 3293; - public static final int ChangeWorldToSingleModeReq = 3174; - public static final int ChangeWorldToSingleModeRsp = 3308; - public static final int ChannelerSlabStageActiveChallengeIndexNotify = 8578; - public static final int ChannelerSlabStageOneoffDungeonNotify = 8137; - public static final int ChannellerSlabCheckEnterLoopDungeonReq = 8684; - public static final int ChannellerSlabCheckEnterLoopDungeonRsp = 8376; - public static final int ChannellerSlabEnterLoopDungeonReq = 8090; - public static final int ChannellerSlabEnterLoopDungeonRsp = 8120; - public static final int ChannellerSlabLoopDungeonChallengeInfoNotify = 8263; - public static final int ChannellerSlabLoopDungeonSelectConditionReq = 8741; - public static final int ChannellerSlabLoopDungeonSelectConditionRsp = 8341; - public static final int ChannellerSlabLoopDungeonTakeFirstPassRewardReq = 8914; - public static final int ChannellerSlabLoopDungeonTakeFirstPassRewardRsp = 8503; - public static final int ChannellerSlabLoopDungeonTakeScoreRewardReq = 8012; - public static final int ChannellerSlabLoopDungeonTakeScoreRewardRsp = 8123; - public static final int ChannellerSlabOneOffDungeonInfoNotify = 8795; - public static final int ChannellerSlabOneOffDungeonInfoReq = 8772; - public static final int ChannellerSlabOneOffDungeonInfoRsp = 8715; - public static final int ChannellerSlabSaveAssistInfoReq = 8518; - public static final int ChannellerSlabSaveAssistInfoRsp = 8773; - public static final int ChannellerSlabTakeoffBuffReq = 8407; - public static final int ChannellerSlabTakeoffBuffRsp = 8260; - public static final int ChannellerSlabWearBuffReq = 8850; - public static final int ChannellerSlabWearBuffRsp = 8203; - public static final int ChapterStateNotify = 455; - public static final int ChatChannelDataNotify = 5047; - public static final int ChatChannelUpdateNotify = 5041; - public static final int ChatHistoryNotify = 3265; - public static final int CheckAddItemExceedLimitNotify = 639; - public static final int CheckSegmentCRCNotify = 56; - public static final int CheckSegmentCRCReq = 83; - public static final int ChessEscapedMonstersNotify = 5394; - public static final int ChessLeftMonstersNotify = 5378; - public static final int ChessManualRefreshCardsReq = 5325; - public static final int ChessManualRefreshCardsRsp = 5349; - public static final int ChessPickCardNotify = 5377; - public static final int ChessPickCardReq = 5343; - public static final int ChessPickCardRsp = 5386; - public static final int ChessPlayerInfoNotify = 5385; - public static final int ChessSelectedCardsNotify = 5339; - public static final int ChooseCurAvatarTeamReq = 1713; - public static final int ChooseCurAvatarTeamRsp = 1608; - public static final int CityReputationDataNotify = 2855; - public static final int CityReputationLevelupNotify = 2837; - public static final int ClearRoguelikeCurseNotify = 8997; - public static final int ClientAbilitiesInitFinishCombineNotify = 1144; - public static final int ClientAbilityChangeNotify = 1191; - public static final int ClientAbilityInitBeginNotify = 1110; - public static final int ClientAbilityInitFinishNotify = 1115; - public static final int ClientAIStateNotify = 1128; - public static final int ClientBulletCreateNotify = 89; - public static final int ClientCollectorDataNotify = 4262; - public static final int ClientHashDebugNotify = 3163; - public static final int ClientLoadingCostumeVerificationNotify = 3133; - public static final int ClientLockGameTimeNotify = 194; - public static final int ClientNewMailNotify = 1434; - public static final int ClientPauseNotify = 278; - public static final int ClientReconnectNotify = 91; - public static final int ClientReportNotify = 28; - public static final int ClientScriptEventNotify = 218; - public static final int ClientTransmitReq = 252; - public static final int ClientTransmitRsp = 292; - public static final int ClientTriggerEventNotify = 197; - public static final int CloseCommonTipsNotify = 3187; - public static final int ClosedItemNotify = 694; - public static final int CodexDataFullNotify = 4204; - public static final int CodexDataUpdateNotify = 4205; - public static final int CombatInvocationsNotify = 347; - public static final int CombineDataNotify = 649; - public static final int CombineFormulaDataNotify = 685; - public static final int CombineReq = 663; - public static final int CombineRsp = 608; - public static final int CommonPlayerTipsNotify = 8928; - public static final int CompoundDataNotify = 181; - public static final int CompoundUnlockNotify = 199; - public static final int CookDataNotify = 164; - public static final int CookGradeDataNotify = 166; + public static final int AvatarChangeAnimHashReq = 1798; + public static final int AvatarChangeAnimHashRsp = 1622; + public static final int AvatarChangeCostumeNotify = 1780; + public static final int AvatarChangeCostumeReq = 1734; + public static final int AvatarChangeCostumeRsp = 1723; + public static final int AvatarChangeElementTypeReq = 1706; + public static final int AvatarChangeElementTypeRsp = 1708; + public static final int AvatarDataNotify = 1695; + public static final int AvatarDelNotify = 1608; + public static final int AvatarDieAnimationEndReq = 1616; + public static final int AvatarDieAnimationEndRsp = 1765; + public static final int AvatarEnterElementViewNotify = 311; + public static final int AvatarEquipAffixStartNotify = 1609; + public static final int AvatarEquipChangeNotify = 690; + public static final int AvatarExpeditionAllDataReq = 1659; + public static final int AvatarExpeditionAllDataRsp = 1636; + public static final int AvatarExpeditionCallBackReq = 1655; + public static final int AvatarExpeditionCallBackRsp = 1642; + public static final int AvatarExpeditionDataNotify = 1698; + public static final int AvatarExpeditionGetRewardReq = 1603; + public static final int AvatarExpeditionGetRewardRsp = 1646; + public static final int AvatarExpeditionStartReq = 1675; + public static final int AvatarExpeditionStartRsp = 1688; + public static final int AvatarFetterDataNotify = 1777; + public static final int AvatarFetterLevelRewardReq = 1704; + public static final int AvatarFetterLevelRewardRsp = 1606; + public static final int AvatarFightPropNotify = 1232; + public static final int AvatarFightPropUpdateNotify = 1217; + public static final int AvatarFlycloakChangeNotify = 1681; + public static final int AvatarFollowRouteNotify = 3353; + public static final int AvatarGainCostumeNotify = 1670; + public static final int AvatarGainFlycloakNotify = 1615; + public static final int AvatarLifeStateChangeNotify = 1235; + public static final int AvatarPromoteGetRewardReq = 1676; + public static final int AvatarPromoteGetRewardRsp = 1613; + public static final int AvatarPromoteReq = 1711; + public static final int AvatarPromoteRsp = 1668; + public static final int AvatarPropChangeReasonNotify = 1249; + public static final int AvatarPropNotify = 1255; + public static final int AvatarSatiationDataNotify = 1632; + public static final int AvatarSkillChangeNotify = 1030; + public static final int AvatarSkillDepotChangeNotify = 1037; + public static final int AvatarSkillInfoNotify = 1035; + public static final int AvatarSkillMaxChargeCountNotify = 1085; + public static final int AvatarSkillUpgradeReq = 1027; + public static final int AvatarSkillUpgradeRsp = 1007; + public static final int AvatarTeamUpdateNotify = 1713; + public static final int AvatarUnlockTalentNotify = 1041; + public static final int AvatarUpgradeReq = 1680; + public static final int AvatarUpgradeRsp = 1779; + public static final int AvatarWearFlycloakReq = 1728; + public static final int AvatarWearFlycloakRsp = 1770; + public static final int BackMyWorldReq = 221; + public static final int BackMyWorldRsp = 223; + public static final int BargainOfferPriceReq = 419; + public static final int BargainOfferPriceRsp = 412; + public static final int BargainStartNotify = 406; + public static final int BargainTerminateNotify = 458; + public static final int BattlePassAllDataNotify = 2618; + public static final int BattlePassBuySuccNotify = 2614; + public static final int BattlePassCurScheduleUpdateNotify = 2604; + public static final int BattlePassMissionDelNotify = 2643; + public static final int BattlePassMissionUpdateNotify = 2609; + public static final int BeginCameraSceneLookNotify = 203; + public static final int BigTalentPointConvertReq = 1032; + public static final int BigTalentPointConvertRsp = 1017; + public static final int BlessingAcceptAllGivePicReq = 2123; + public static final int BlessingAcceptAllGivePicRsp = 2180; + public static final int BlessingAcceptGivePicReq = 2006; + public static final int BlessingAcceptGivePicRsp = 2048; + public static final int BlessingGetAllRecvPicRecordListReq = 2076; + public static final int BlessingGetAllRecvPicRecordListRsp = 2013; + public static final int BlessingGetFriendPicListReq = 2081; + public static final int BlessingGetFriendPicListRsp = 2015; + public static final int BlessingGiveFriendPicReq = 2009; + public static final int BlessingGiveFriendPicRsp = 2104; + public static final int BlessingRecvFriendPicNotify = 2134; + public static final int BlessingRedeemRewardReq = 2128; + public static final int BlessingRedeemRewardRsp = 2170; + public static final int BlessingScanReq = 2012; + public static final int BlessingScanRsp = 2032; + public static final int BlitzRushParkourRestartReq = 8355; + public static final int BlitzRushParkourRestartRsp = 8943; + public static final int BlossomBriefInfoNotify = 2741; + public static final int BlossomChestCreateNotify = 2717; + public static final int BlossomChestInfoNotify = 835; + public static final int BonusActivityInfoReq = 2507; + public static final int BonusActivityInfoRsp = 2530; + public static final int BonusActivityUpdateNotify = 2527; + public static final int BossChestActivateNotify = 885; + public static final int BounceConjuringSettleNotify = 8313; + public static final int BuoyantCombatSettleNotify = 8347; + public static final int BuyBattlePassLevelReq = 2612; + public static final int BuyBattlePassLevelRsp = 2646; + public static final int BuyGoodsReq = 741; + public static final int BuyGoodsRsp = 737; + public static final int BuyResinReq = 602; + public static final int BuyResinRsp = 616; + public static final int CalcWeaponUpgradeReturnItemsReq = 610; + public static final int CalcWeaponUpgradeReturnItemsRsp = 666; + public static final int CancelCityReputationRequestReq = 2882; + public static final int CancelCityReputationRequestRsp = 2855; + public static final int CancelCoopTaskReq = 1962; + public static final int CancelCoopTaskRsp = 1996; + public static final int CancelFinishParentQuestNotify = 459; + public static final int CanUseSkillNotify = 1098; + public static final int CardProductRewardNotify = 4104; + public static final int ChallengeDataNotify = 963; + public static final int ChallengeRecordNotify = 919; + public static final int ChangeAvatarReq = 1662; + public static final int ChangeAvatarRsp = 1710; + public static final int ChangeGameTimeReq = 149; + public static final int ChangeGameTimeRsp = 182; + public static final int ChangeMailStarNotify = 1407; + public static final int ChangeMpTeamAvatarReq = 1689; + public static final int ChangeMpTeamAvatarRsp = 1617; + public static final int ChangeServerGlobalValueNotify = 12; + public static final int ChangeTeamNameReq = 1762; + public static final int ChangeTeamNameRsp = 1789; + public static final int ChangeWorldToSingleModeNotify = 3260; + public static final int ChangeWorldToSingleModeReq = 3140; + public static final int ChangeWorldToSingleModeRsp = 3083; + public static final int ChannelerSlabStageActiveChallengeIndexNotify = 8864; + public static final int ChannelerSlabStageOneoffDungeonNotify = 8522; + public static final int ChannellerSlabCheckEnterLoopDungeonReq = 8304; + public static final int ChannellerSlabCheckEnterLoopDungeonRsp = 8928; + public static final int ChannellerSlabEnterLoopDungeonReq = 8687; + public static final int ChannellerSlabEnterLoopDungeonRsp = 8639; + public static final int ChannellerSlabLoopDungeonChallengeInfoNotify = 8555; + public static final int ChannellerSlabLoopDungeonSelectConditionReq = 8383; + public static final int ChannellerSlabLoopDungeonSelectConditionRsp = 8979; + public static final int ChannellerSlabLoopDungeonTakeFirstPassRewardReq = 8910; + public static final int ChannellerSlabLoopDungeonTakeFirstPassRewardRsp = 8482; + public static final int ChannellerSlabLoopDungeonTakeScoreRewardReq = 8305; + public static final int ChannellerSlabLoopDungeonTakeScoreRewardRsp = 8552; + public static final int ChannellerSlabOneOffDungeonInfoNotify = 8008; + public static final int ChannellerSlabOneOffDungeonInfoReq = 8295; + public static final int ChannellerSlabOneOffDungeonInfoRsp = 8716; + public static final int ChannellerSlabSaveAssistInfoReq = 8860; + public static final int ChannellerSlabSaveAssistInfoRsp = 8320; + public static final int ChannellerSlabTakeoffBuffReq = 8577; + public static final int ChannellerSlabTakeoffBuffRsp = 8689; + public static final int ChannellerSlabWearBuffReq = 8132; + public static final int ChannellerSlabWearBuffRsp = 8315; + public static final int ChapterStateNotify = 498; + public static final int ChatChannelDataNotify = 4957; + public static final int ChatChannelUpdateNotify = 4977; + public static final int ChatHistoryNotify = 3213; + public static final int CheckAddItemExceedLimitNotify = 653; + public static final int CheckSegmentCRCNotify = 86; + public static final int CheckSegmentCRCReq = 63; + public static final int ChessEscapedMonstersNotify = 5374; + public static final int ChessLeftMonstersNotify = 5391; + public static final int ChessManualRefreshCardsReq = 5373; + public static final int ChessManualRefreshCardsRsp = 5361; + public static final int ChessPickCardNotify = 5348; + public static final int ChessPickCardReq = 5310; + public static final int ChessPickCardRsp = 5366; + public static final int ChessPlayerInfoNotify = 5388; + public static final int ChessSelectedCardsNotify = 5353; + public static final int ChooseCurAvatarTeamReq = 1800; + public static final int ChooseCurAvatarTeamRsp = 1673; + public static final int CityReputationDataNotify = 2898; + public static final int CityReputationLevelupNotify = 2832; + public static final int ClearRoguelikeCurseNotify = 8291; + public static final int ClientAbilitiesInitFinishCombineNotify = 1185; + public static final int ClientAbilityChangeNotify = 1127; + public static final int ClientAbilityInitBeginNotify = 1141; + public static final int ClientAbilityInitFinishNotify = 1137; + public static final int ClientAIStateNotify = 1197; + public static final int ClientBulletCreateNotify = 6; + public static final int ClientCollectorDataNotify = 4264; + public static final int ClientHashDebugNotify = 3192; + public static final int ClientLoadingCostumeVerificationNotify = 3488; + public static final int ClientLockGameTimeNotify = 174; + public static final int ClientNewMailNotify = 1482; + public static final int ClientPauseNotify = 291; + public static final int ClientReconnectNotify = 27; + public static final int ClientReportNotify = 97; + public static final int ClientScriptEventNotify = 214; + public static final int ClientTransmitReq = 272; + public static final int ClientTransmitRsp = 259; + public static final int ClientTriggerEventNotify = 107; + public static final int CloseCommonTipsNotify = 3496; + public static final int ClosedItemNotify = 674; + public static final int CodexDataFullNotify = 4201; + public static final int CodexDataUpdateNotify = 4210; + public static final int CombatInvocationsNotify = 316; + public static final int CombineDataNotify = 661; + public static final int CombineFormulaDataNotify = 688; + public static final int CombineReq = 654; + public static final int CombineRsp = 646; + public static final int CommonPlayerTipsNotify = 8490; + public static final int CompoundDataNotify = 136; + public static final int CompoundUnlockNotify = 181; + public static final int CookDataNotify = 142; + public static final int CookGradeDataNotify = 111; public static final int CookRecipeDataNotify = 101; - public static final int CoopCgShowNotify = 1983; - public static final int CoopCgUpdateNotify = 1993; - public static final int CoopChapterUpdateNotify = 1986; - public static final int CoopDataNotify = 1967; + public static final int CoopCgShowNotify = 1989; + public static final int CoopCgUpdateNotify = 1966; + public static final int CoopChapterUpdateNotify = 1997; + public static final int CoopDataNotify = 1976; public static final int CoopPointUpdateNotify = 1987; - public static final int CoopProgressUpdateNotify = 2000; - public static final int CoopRewardUpdateNotify = 1976; - public static final int CreateMassiveEntityNotify = 336; - public static final int CreateMassiveEntityReq = 323; - public static final int CreateMassiveEntityRsp = 313; - public static final int CreateVehicleReq = 809; - public static final int CreateVehicleRsp = 865; - public static final int CutSceneBeginNotify = 241; - public static final int CutSceneEndNotify = 214; - public static final int CutSceneFinishNotify = 248; - public static final int DailyTaskDataNotify = 124; - public static final int DailyTaskFilterCityReq = 159; - public static final int DailyTaskFilterCityRsp = 150; - public static final int DailyTaskProgressNotify = 161; - public static final int DailyTaskScoreRewardNotify = 138; - public static final int DailyTaskUnlockedCitiesNotify = 119; - public static final int DataResVersionNotify = 136; - public static final int DealAddFriendReq = 4044; - public static final int DealAddFriendRsp = 4045; - public static final int DeleteFriendNotify = 4083; - public static final int DeleteFriendReq = 4079; - public static final int DeleteFriendRsp = 4091; - public static final int DelMailReq = 1493; - public static final int DelMailRsp = 1444; - public static final int DelScenePlayTeamEntityNotify = 3117; - public static final int DelTeamEntityNotify = 330; - public static final int DestroyMassiveEntityNotify = 324; - public static final int DestroyMaterialReq = 670; - public static final int DestroyMaterialRsp = 654; - public static final int DigActivityChangeGadgetStateReq = 8283; - public static final int DigActivityChangeGadgetStateRsp = 8609; - public static final int DigActivityMarkPointChangeNotify = 8143; - public static final int DisableRoguelikeTrapNotify = 8029; - public static final int DoGachaReq = 1510; - public static final int DoGachaRsp = 1515; - public static final int DoRoguelikeDungeonCardGachaReq = 8897; - public static final int DoRoguelikeDungeonCardGachaRsp = 8973; - public static final int DoSetPlayerBornDataNotify = 174; - public static final int DraftGuestReplyInviteNotify = 5445; - public static final int DraftGuestReplyInviteReq = 5493; - public static final int DraftGuestReplyInviteRsp = 5444; - public static final int DraftGuestReplyTwiceConfirmNotify = 5488; - public static final int DraftGuestReplyTwiceConfirmReq = 5479; - public static final int DraftGuestReplyTwiceConfirmRsp = 5491; - public static final int DraftInviteResultNotify = 5475; - public static final int DraftOwnerInviteNotify = 5437; - public static final int DraftOwnerStartInviteReq = 5410; - public static final int DraftOwnerStartInviteRsp = 5415; - public static final int DraftOwnerTwiceConfirmNotify = 5434; - public static final int DraftTwiceConfirmResultNotify = 5497; - public static final int DragonSpineChapterFinishNotify = 2196; - public static final int DragonSpineChapterOpenNotify = 2070; - public static final int DragonSpineChapterProgressChangeNotify = 2001; - public static final int DragonSpineCoinChangeNotify = 2189; - public static final int DropHintNotify = 673; - public static final int DropItemReq = 634; - public static final int DropItemRsp = 679; - public static final int DropSubfieldReq = 232; - public static final int DropSubfieldRsp = 251; - public static final int DungeonCandidateTeamChangeAvatarReq = 958; - public static final int DungeonCandidateTeamChangeAvatarRsp = 923; - public static final int DungeonCandidateTeamCreateReq = 964; + public static final int CoopProgressUpdateNotify = 1961; + public static final int CoopRewardUpdateNotify = 1951; + public static final int CreateMassiveEntityNotify = 325; + public static final int CreateMassiveEntityReq = 370; + public static final int CreateMassiveEntityRsp = 329; + public static final int CreateVehicleReq = 819; + public static final int CreateVehicleRsp = 812; + public static final int CutSceneBeginNotify = 300; + public static final int CutSceneEndNotify = 299; + public static final int CutSceneFinishNotify = 294; + public static final int DailyTaskDataNotify = 183; + public static final int DailyTaskFilterCityReq = 192; + public static final int DailyTaskFilterCityRsp = 118; + public static final int DailyTaskProgressNotify = 103; + public static final int DailyTaskScoreRewardNotify = 134; + public static final int DailyTaskUnlockedCitiesNotify = 121; + public static final int DataResVersionNotify = 125; + public static final int DealAddFriendReq = 4085; + public static final int DealAddFriendRsp = 4035; + public static final int DebugNotify = 101; + public static final int DeleteFriendNotify = 4063; + public static final int DeleteFriendReq = 4055; + public static final int DeleteFriendRsp = 4027; + public static final int DelMailReq = 1417; + public static final int DelMailRsp = 1485; + public static final int DelScenePlayTeamEntityNotify = 3087; + public static final int DelTeamEntityNotify = 302; + public static final int DestroyMassiveEntityNotify = 383; + public static final int DestroyMaterialReq = 678; + public static final int DestroyMaterialRsp = 608; + public static final int DigActivityChangeGadgetStateReq = 8155; + public static final int DigActivityChangeGadgetStateRsp = 8851; + public static final int DigActivityMarkPointChangeNotify = 8679; + public static final int DisableRoguelikeTrapNotify = 8250; + public static final int DoGachaReq = 1541; + public static final int DoGachaRsp = 1537; + public static final int DoRoguelikeDungeonCardGachaReq = 8764; + public static final int DoRoguelikeDungeonCardGachaRsp = 8641; + public static final int DoSetPlayerBornDataNotify = 190; + public static final int DraftGuestReplyInviteNotify = 5435; + public static final int DraftGuestReplyInviteReq = 5417; + public static final int DraftGuestReplyInviteRsp = 5485; + public static final int DraftGuestReplyTwiceConfirmNotify = 5430; + public static final int DraftGuestReplyTwiceConfirmReq = 5455; + public static final int DraftGuestReplyTwiceConfirmRsp = 5427; + public static final int DraftInviteResultNotify = 5449; + public static final int DraftOwnerInviteNotify = 5432; + public static final int DraftOwnerStartInviteReq = 5441; + public static final int DraftOwnerStartInviteRsp = 5437; + public static final int DraftOwnerTwiceConfirmNotify = 5482; + public static final int DraftTwiceConfirmResultNotify = 5407; + public static final int DragonSpineChapterFinishNotify = 2135; + public static final int DragonSpineChapterOpenNotify = 2119; + public static final int DragonSpineChapterProgressChangeNotify = 2102; + public static final int DragonSpineCoinChangeNotify = 2031; + public static final int DropHintNotify = 622; + public static final int DropItemReq = 682; + public static final int DropItemRsp = 655; + public static final int DungeonCandidateTeamChangeAvatarReq = 984; + public static final int DungeonCandidateTeamChangeAvatarRsp = 970; + public static final int DungeonCandidateTeamCreateReq = 942; public static final int DungeonCandidateTeamCreateRsp = 901; - public static final int DungeonCandidateTeamDismissNotify = 980; - public static final int DungeonCandidateTeamInfoNotify = 965; - public static final int DungeonCandidateTeamInviteNotify = 903; - public static final int DungeonCandidateTeamInviteReq = 966; - public static final int DungeonCandidateTeamInviteRsp = 973; - public static final int DungeonCandidateTeamKickReq = 963; - public static final int DungeonCandidateTeamKickRsp = 908; - public static final int DungeonCandidateTeamLeaveReq = 917; - public static final int DungeonCandidateTeamLeaveRsp = 981; + public static final int DungeonCandidateTeamDismissNotify = 913; + public static final int DungeonCandidateTeamInfoNotify = 912; + public static final int DungeonCandidateTeamInviteNotify = 958; + public static final int DungeonCandidateTeamInviteReq = 911; + public static final int DungeonCandidateTeamInviteRsp = 922; + public static final int DungeonCandidateTeamKickReq = 954; + public static final int DungeonCandidateTeamKickRsp = 946; + public static final int DungeonCandidateTeamLeaveReq = 933; + public static final int DungeonCandidateTeamLeaveRsp = 936; public static final int DungeonCandidateTeamPlayerLeaveNotify = 920; - public static final int DungeonCandidateTeamRefuseNotify = 967; - public static final int DungeonCandidateTeamReplyInviteReq = 927; - public static final int DungeonCandidateTeamReplyInviteRsp = 902; - public static final int DungeonCandidateTeamSetChangingAvatarReq = 954; - public static final int DungeonCandidateTeamSetChangingAvatarRsp = 935; - public static final int DungeonCandidateTeamSetReadyReq = 952; - public static final int DungeonCandidateTeamSetReadyRsp = 992; - public static final int DungeonChallengeBeginNotify = 974; - public static final int DungeonChallengeFinishNotify = 956; - public static final int DungeonDataNotify = 946; - public static final int DungeonDieOptionReq = 991; - public static final int DungeonDieOptionRsp = 997; - public static final int DungeonEntryInfoReq = 960; - public static final int DungeonEntryInfoRsp = 933; - public static final int DungeonEntryToBeExploreNotify = 3067; - public static final int DungeonFollowNotify = 926; - public static final int DungeonGetStatueDropReq = 962; - public static final int DungeonGetStatueDropRsp = 989; - public static final int DungeonInterruptChallengeReq = 938; - public static final int DungeonInterruptChallengeRsp = 930; - public static final int DungeonPlayerDieNotify = 979; - public static final int DungeonPlayerDieReq = 928; - public static final int DungeonPlayerDieRsp = 955; - public static final int DungeonRestartInviteNotify = 984; - public static final int DungeonRestartInviteReplyNotify = 972; - public static final int DungeonRestartInviteReplyReq = 912; - public static final int DungeonRestartInviteReplyRsp = 953; - public static final int DungeonRestartReq = 932; - public static final int DungeonRestartResultNotify = 970; - public static final int DungeonRestartRsp = 951; - public static final int DungeonReviseLevelNotify = 957; - public static final int DungeonSettleNotify = 934; - public static final int DungeonShowReminderNotify = 988; - public static final int DungeonSlipRevivePointActivateReq = 924; - public static final int DungeonSlipRevivePointActivateRsp = 961; - public static final int DungeonWayPointActivateReq = 945; - public static final int DungeonWayPointActivateRsp = 975; - public static final int DungeonWayPointNotify = 944; - public static final int EchoNotify = 62; - public static final int EchoShellTakeRewardReq = 8801; - public static final int EchoShellTakeRewardRsp = 8888; - public static final int EchoShellUpdateNotify = 8730; - public static final int EffigyChallengeInfoNotify = 2159; - public static final int EffigyChallengeResultNotify = 2024; - public static final int EndCameraSceneLookNotify = 238; - public static final int EnterChessDungeonReq = 8336; - public static final int EnterChessDungeonRsp = 8581; - public static final int EnterFishingReq = 5835; - public static final int EnterFishingRsp = 5825; - public static final int EnterMechanicusDungeonReq = 3979; - public static final int EnterMechanicusDungeonRsp = 3991; - public static final int EnterRoguelikeDungeonNotify = 8330; - public static final int EnterSceneDoneReq = 268; - public static final int EnterSceneDoneRsp = 290; - public static final int EnterScenePeerNotify = 282; - public static final int EnterSceneReadyReq = 298; - public static final int EnterSceneReadyRsp = 296; - public static final int EnterSceneWeatherAreaNotify = 258; - public static final int EnterTransPointRegionNotify = 255; - public static final int EnterTrialAvatarActivityDungeonReq = 2031; - public static final int EnterTrialAvatarActivityDungeonRsp = 2175; - public static final int EnterWorldAreaReq = 273; - public static final int EnterWorldAreaRsp = 263; - public static final int EntityAiKillSelfNotify = 370; - public static final int EntityAiSyncNotify = 312; - public static final int EntityAuthorityChangeNotify = 303; - public static final int EntityConfigHashNotify = 3458; - public static final int EntityFightPropChangeReasonNotify = 1244; - public static final int EntityFightPropNotify = 1210; - public static final int EntityFightPropUpdateNotify = 1215; - public static final int EntityForceSyncReq = 208; - public static final int EntityForceSyncRsp = 217; - public static final int EntityJumpNotify = 226; - public static final int EntityMoveRoomNotify = 3135; - public static final int EntityPropNotify = 1260; - public static final int EntityTagChangeNotify = 3262; - public static final int EquipRoguelikeRuneReq = 8579; - public static final int EquipRoguelikeRuneRsp = 8412; - public static final int EvtAiSyncCombatThreatInfoNotify = 351; - public static final int EvtAiSyncSkillCdNotify = 317; - public static final int EvtAnimatorParameterNotify = 333; - public static final int EvtAnimatorStateChangedNotify = 379; - public static final int EvtAvatarEnterFocusNotify = 389; - public static final int EvtAvatarExitFocusNotify = 309; - public static final int EvtAvatarLockChairReq = 354; - public static final int EvtAvatarLockChairRsp = 335; - public static final int EvtAvatarSitDownNotify = 392; - public static final int EvtAvatarStandUpNotify = 358; - public static final int EvtAvatarUpdateFocusNotify = 365; - public static final int EvtBeingHitNotify = 360; - public static final int EvtBeingHitsCombineNotify = 381; - public static final int EvtBulletDeactiveNotify = 388; - public static final int EvtBulletHitNotify = 397; - public static final int EvtBulletMoveNotify = 362; - public static final int EvtCostStaminaNotify = 375; - public static final int EvtCreateGadgetNotify = 337; - public static final int EvtDestroyGadgetNotify = 393; - public static final int EvtDestroyServerGadgetNotify = 372; - public static final int EvtDoSkillSuccNotify = 315; - public static final int EvtEntityRenderersChangedNotify = 363; - public static final int EvtEntityStartDieEndNotify = 328; - public static final int EvtFaceToDirNotify = 345; - public static final int EvtFaceToEntityNotify = 344; - public static final int EvtRushMoveNotify = 391; - public static final int EvtSetAttackTargetNotify = 334; - public static final int ExclusiveRuleNotify = 169; - public static final int ExecuteGadgetLuaReq = 231; - public static final int ExecuteGadgetLuaRsp = 240; - public static final int ExecuteGroupTriggerReq = 284; - public static final int ExecuteGroupTriggerRsp = 212; - public static final int ExitFishingReq = 5812; - public static final int ExitFishingRsp = 5839; - public static final int ExitSceneWeatherAreaNotify = 223; - public static final int ExitTransPointRegionNotify = 246; - public static final int ExpeditionChallengeEnterRegionNotify = 2095; - public static final int ExpeditionChallengeFinishedNotify = 2197; - public static final int ExpeditionRecallReq = 2114; - public static final int ExpeditionRecallRsp = 2108; - public static final int ExpeditionStartReq = 2032; - public static final int ExpeditionStartRsp = 2148; - public static final int ExpeditionTakeRewardReq = 2053; - public static final int ExpeditionTakeRewardRsp = 2181; - public static final int FindHilichurlAcceptQuestNotify = 8292; - public static final int FindHilichurlFinishSecondQuestNotify = 8799; - public static final int FinishDeliveryNotify = 2126; - public static final int FinishedParentQuestNotify = 415; - public static final int FinishedParentQuestUpdateNotify = 437; - public static final int FinishMainCoopReq = 1963; - public static final int FinishMainCoopRsp = 1951; - public static final int FishAttractNotify = 5821; - public static final int FishBaitGoneNotify = 5846; - public static final int FishBattleBeginReq = 5815; - public static final int FishBattleBeginRsp = 5827; + public static final int DungeonCandidateTeamRefuseNotify = 950; + public static final int DungeonCandidateTeamReplyInviteReq = 928; + public static final int DungeonCandidateTeamReplyInviteRsp = 976; + public static final int DungeonCandidateTeamSetChangingAvatarReq = 908; + public static final int DungeonCandidateTeamSetChangingAvatarRsp = 904; + public static final int DungeonCandidateTeamSetReadyReq = 972; + public static final int DungeonCandidateTeamSetReadyRsp = 959; + public static final int DungeonChallengeBeginNotify = 990; + public static final int DungeonChallengeFinishNotify = 986; + public static final int DungeonDataNotify = 945; + public static final int DungeonDieOptionReq = 927; + public static final int DungeonDieOptionRsp = 907; + public static final int DungeonEntryInfoReq = 909; + public static final int DungeonEntryInfoRsp = 931; + public static final int DungeonEntryToBeExploreNotify = 3414; + public static final int DungeonFollowNotify = 924; + public static final int DungeonGetStatueDropReq = 938; + public static final int DungeonGetStatueDropRsp = 906; + public static final int DungeonInterruptChallengeReq = 934; + public static final int DungeonInterruptChallengeRsp = 902; + public static final int DungeonPlayerDieNotify = 955; + public static final int DungeonPlayerDieReq = 997; + public static final int DungeonPlayerDieRsp = 998; + public static final int DungeonRestartInviteNotify = 977; + public static final int DungeonRestartInviteReplyNotify = 989; + public static final int DungeonRestartInviteReplyReq = 943; + public static final int DungeonRestartInviteReplyRsp = 987; + public static final int DungeonRestartReq = 993; + public static final int DungeonRestartResultNotify = 978; + public static final int DungeonRestartRsp = 960; + public static final int DungeonReviseLevelNotify = 905; + public static final int DungeonSettleNotify = 982; + public static final int DungeonShowReminderNotify = 930; + public static final int DungeonSlipRevivePointActivateReq = 983; + public static final int DungeonSlipRevivePointActivateRsp = 903; + public static final int DungeonWayPointActivateReq = 935; + public static final int DungeonWayPointActivateRsp = 949; + public static final int DungeonWayPointNotify = 985; + public static final int EchoNotify = 38; + public static final int EchoShellTakeRewardReq = 8074; + public static final int EchoShellTakeRewardRsp = 8446; + public static final int EchoShellUpdateNotify = 8077; + public static final int EffigyChallengeInfoNotify = 2153; + public static final int EffigyChallengeResultNotify = 2064; + public static final int EndCameraSceneLookNotify = 234; + public static final int EnterChessDungeonReq = 8571; + public static final int EnterChessDungeonRsp = 8819; + public static final int EnterFishingReq = 5818; + public static final int EnterFishingRsp = 5809; + public static final int EnterMechanicusDungeonReq = 3955; + public static final int EnterMechanicusDungeonRsp = 3927; + public static final int EnterRoguelikeDungeonNotify = 8646; + public static final int EnterSceneDoneReq = 279; + public static final int EnterSceneDoneRsp = 269; + public static final int EnterScenePeerNotify = 257; + public static final int EnterSceneReadyReq = 244; + public static final int EnterSceneReadyRsp = 252; + public static final int EnterSceneWeatherAreaNotify = 284; + public static final int EnterTransPointRegionNotify = 298; + public static final int EnterTrialAvatarActivityDungeonReq = 2057; + public static final int EnterTrialAvatarActivityDungeonRsp = 2021; + public static final int EnterWorldAreaReq = 222; + public static final int EnterWorldAreaRsp = 254; + public static final int EntityAiKillSelfNotify = 378; + public static final int EntityAiSyncNotify = 343; + public static final int EntityAuthorityChangeNotify = 358; + public static final int EntityConfigHashNotify = 3271; + public static final int EntityFightPropChangeReasonNotify = 1285; + public static final int EntityFightPropNotify = 1241; + public static final int EntityFightPropUpdateNotify = 1237; + public static final int EntityForceSyncReq = 246; + public static final int EntityForceSyncRsp = 233; + public static final int EntityJumpNotify = 224; + public static final int EntityMoveRoomNotify = 3209; + public static final int EntityPropNotify = 1209; + public static final int EntityTagChangeNotify = 3273; + public static final int EquipRoguelikeRuneReq = 8510; + public static final int EquipRoguelikeRuneRsp = 8516; + public static final int EvtAiSyncCombatThreatInfoNotify = 360; + public static final int EvtAiSyncSkillCdNotify = 333; + public static final int EvtAnimatorParameterNotify = 331; + public static final int EvtAnimatorStateChangedNotify = 355; + public static final int EvtAvatarEnterFocusNotify = 306; + public static final int EvtAvatarExitFocusNotify = 319; + public static final int EvtAvatarLockChairReq = 308; + public static final int EvtAvatarLockChairRsp = 304; + public static final int EvtAvatarSitDownNotify = 359; + public static final int EvtAvatarStandUpNotify = 384; + public static final int EvtAvatarUpdateFocusNotify = 312; + public static final int EvtBeingHitNotify = 309; + public static final int EvtBeingHitsCombineNotify = 336; + public static final int EvtBulletDeactiveNotify = 330; + public static final int EvtBulletHitNotify = 307; + public static final int EvtBulletMoveNotify = 338; + public static final int EvtCostStaminaNotify = 349; + public static final int EvtCreateGadgetNotify = 332; + public static final int EvtDestroyGadgetNotify = 317; + public static final int EvtDestroyServerGadgetNotify = 389; + public static final int EvtDoSkillSuccNotify = 337; + public static final int EvtEntityRenderersChangedNotify = 354; + public static final int EvtEntityStartDieEndNotify = 397; + public static final int EvtFaceToDirNotify = 335; + public static final int EvtFaceToEntityNotify = 385; + public static final int EvtRushMoveNotify = 327; + public static final int EvtSetAttackTargetNotify = 382; + public static final int ExclusiveRuleNotify = 123; + public static final int ExecuteGadgetLuaReq = 267; + public static final int ExecuteGadgetLuaRsp = 271; + public static final int ExecuteGroupTriggerReq = 277; + public static final int ExecuteGroupTriggerRsp = 243; + public static final int ExitFishingReq = 5814; + public static final int ExitFishingRsp = 5812; + public static final int ExitSceneWeatherAreaNotify = 270; + public static final int ExitTransPointRegionNotify = 245; + public static final int ExpeditionChallengeEnterRegionNotify = 2050; + public static final int ExpeditionChallengeFinishedNotify = 2034; + public static final int ExpeditionRecallReq = 2122; + public static final int ExpeditionRecallRsp = 2156; + public static final int ExpeditionStartReq = 2026; + public static final int ExpeditionStartRsp = 2163; + public static final int ExpeditionTakeRewardReq = 2173; + public static final int ExpeditionTakeRewardRsp = 2196; + public static final int FindHilichurlAcceptQuestNotify = 8465; + public static final int FindHilichurlFinishSecondQuestNotify = 8981; + public static final int FinishDeliveryNotify = 2158; + public static final int FinishedParentQuestNotify = 437; + public static final int FinishedParentQuestUpdateNotify = 432; + public static final int FinishMainCoopReq = 1978; + public static final int FinishMainCoopRsp = 1953; + public static final int FishAttractNotify = 5846; + public static final int FishBaitGoneNotify = 5827; + public static final int FishBattleBeginReq = 5802; + public static final int FishBattleBeginRsp = 5813; public static final int FishBattleEndReq = 5837; - public static final int FishBattleEndRsp = 5806; - public static final int FishBiteReq = 5843; - public static final int FishBiteRsp = 5826; - public static final int FishCastRodReq = 5813; - public static final int FishCastRodRsp = 5801; - public static final int FishChosenNotify = 5817; - public static final int FishEscapeNotify = 5836; - public static final int FishingGallerySettleNotify = 8870; - public static final int FishPoolDataNotify = 5850; - public static final int FleurFairBalloonSettleNotify = 2192; - public static final int FleurFairBuffEnergyNotify = 5392; - public static final int FleurFairFallSettleNotify = 2015; - public static final int FleurFairFinishGalleryStageNotify = 5323; - public static final int FleurFairMusicGameSettleReq = 2064; - public static final int FleurFairMusicGameSettleRsp = 2040; - public static final int FleurFairMusicGameStartReq = 2105; - public static final int FleurFairMusicGameStartRsp = 2179; - public static final int FleurFairReplayMiniGameReq = 2146; - public static final int FleurFairReplayMiniGameRsp = 2089; - public static final int FleurFairStageSettleNotify = 5358; - public static final int FlightActivityRestartReq = 2073; - public static final int FlightActivityRestartRsp = 2045; - public static final int FlightActivitySettleNotify = 2195; - public static final int FocusAvatarReq = 1710; - public static final int FocusAvatarRsp = 1772; - public static final int ForceAddPlayerFriendReq = 4084; - public static final int ForceAddPlayerFriendRsp = 4012; - public static final int ForceDragAvatarNotify = 3056; - public static final int ForceDragBackTransferNotify = 3171; - public static final int ForgeDataNotify = 677; - public static final int ForgeFormulaDataNotify = 625; - public static final int ForgeGetQueueDataReq = 681; - public static final int ForgeGetQueueDataRsp = 627; - public static final int ForgeQueueDataNotify = 617; - public static final int ForgeQueueManipulateReq = 692; - public static final int ForgeQueueManipulateRsp = 658; - public static final int ForgeStartReq = 602; - public static final int ForgeStartRsp = 652; - public static final int FoundationNotify = 874; - public static final int FoundationReq = 855; - public static final int FoundationRsp = 846; - public static final int FriendInfoChangeNotify = 4085; - public static final int FunitureMakeMakeInfoChangeNotify = 4523; - public static final int FurnitureCurModuleArrangeCountNotify = 4770; - public static final int FurnitureMakeBeHelpedNotify = 4825; - public static final int FurnitureMakeCancelReq = 4495; - public static final int FurnitureMakeCancelRsp = 4814; - public static final int FurnitureMakeFinishNotify = 4707; - public static final int FurnitureMakeHelpReq = 4779; - public static final int FurnitureMakeHelpRsp = 4455; - public static final int FurnitureMakeReq = 4885; - public static final int FurnitureMakeRsp = 4819; - public static final int FurnitureMakeStartReq = 4518; - public static final int FurnitureMakeStartRsp = 4521; - public static final int GachaOpenWishNotify = 1544; - public static final int GachaSimpleInfoNotify = 1545; - public static final int GachaWishReq = 1537; - public static final int GachaWishRsp = 1593; - public static final int GadgetAutoPickDropInfoNotify = 888; - public static final int GadgetChainLevelChangeNotify = 826; - public static final int GadgetChainLevelUpdateNotify = 883; - public static final int GadgetCustomTreeInfoNotify = 873; - public static final int GadgetGeneralRewardInfoNotify = 897; - public static final int GadgetInteractReq = 860; - public static final int GadgetInteractRsp = 833; - public static final int GadgetPlayDataNotify = 879; - public static final int GadgetPlayStartNotify = 875; - public static final int GadgetPlayStopNotify = 834; - public static final int GadgetPlayUidOpNotify = 891; - public static final int GadgetStateNotify = 810; - public static final int GadgetTalkChangeNotify = 856; - public static final int GalleryBalloonScoreNotify = 5510; - public static final int GalleryBalloonShootNotify = 5533; - public static final int GalleryBounceConjuringHitNotify = 5555; - public static final int GalleryBrokenFloorFallNotify = 5591; - public static final int GalleryBulletHitNotify = 5579; - public static final int GalleryFallCatchNotify = 5537; - public static final int GalleryFallScoreNotify = 5593; - public static final int GalleryFlowerCatchNotify = 5575; - public static final int GalleryPreStartNotify = 5534; - public static final int GalleryStartNotify = 5560; - public static final int GalleryStopNotify = 5515; - public static final int GallerySumoKillMonsterNotify = 5546; - public static final int GetActivityInfoReq = 2011; - public static final int GetActivityInfoRsp = 2170; - public static final int GetActivityScheduleReq = 2663; - public static final int GetActivityScheduleRsp = 2651; - public static final int GetActivityShopSheetInfoReq = 744; - public static final int GetActivityShopSheetInfoRsp = 745; - public static final int GetAllActivatedBargainDataReq = 480; - public static final int GetAllActivatedBargainDataRsp = 464; - public static final int GetAllH5ActivityInfoReq = 5675; - public static final int GetAllH5ActivityInfoRsp = 5685; - public static final int GetAllMailReq = 1479; - public static final int GetAllMailRsp = 1491; - public static final int GetAllSceneGalleryInfoReq = 5544; - public static final int GetAllSceneGalleryInfoRsp = 5545; - public static final int GetAllUnlockNameCardReq = 4065; - public static final int GetAllUnlockNameCardRsp = 4003; - public static final int GetAreaExplorePointReq = 227; - public static final int GetAreaExplorePointRsp = 202; - public static final int GetAuthkeyReq = 1445; - public static final int GetAuthkeyRsp = 1475; - public static final int GetAuthSalesmanInfoReq = 2082; - public static final int GetAuthSalesmanInfoRsp = 2173; - public static final int GetBargainDataReq = 467; + public static final int FishBattleEndRsp = 5832; + public static final int FishBiteReq = 5816; + public static final int FishBiteRsp = 5801; + public static final int FishCastRodReq = 5828; + public static final int FishCastRodRsp = 5803; + public static final int FishChosenNotify = 5826; + public static final int FishEscapeNotify = 5847; + public static final int FishingGallerySettleNotify = 8896; + public static final int FishPoolDataNotify = 5811; + public static final int FleurFairBalloonSettleNotify = 2139; + public static final int FleurFairBuffEnergyNotify = 5359; + public static final int FleurFairFallSettleNotify = 2097; + public static final int FleurFairFinishGalleryStageNotify = 5370; + public static final int FleurFairMusicGameSettleReq = 2115; + public static final int FleurFairMusicGameSettleRsp = 2124; + public static final int FleurFairMusicGameStartReq = 2171; + public static final int FleurFairMusicGameStartRsp = 2149; + public static final int FleurFairReplayMiniGameReq = 2152; + public static final int FleurFairReplayMiniGameRsp = 2101; + public static final int FleurFairStageSettleNotify = 5384; + public static final int FlightActivityRestartReq = 2164; + public static final int FlightActivityRestartRsp = 2002; + public static final int FlightActivitySettleNotify = 2084; + public static final int FocusAvatarReq = 1652; + public static final int FocusAvatarRsp = 1612; + public static final int ForceAddPlayerFriendReq = 4077; + public static final int ForceAddPlayerFriendRsp = 4043; + public static final int ForceDragAvatarNotify = 3425; + public static final int ForceDragBackTransferNotify = 3088; + public static final int ForgeDataNotify = 648; + public static final int ForgeFormulaDataNotify = 673; + public static final int ForgeGetQueueDataReq = 636; + public static final int ForgeGetQueueDataRsp = 628; + public static final int ForgeQueueDataNotify = 633; + public static final int ForgeQueueManipulateReq = 659; + public static final int ForgeQueueManipulateRsp = 684; + public static final int ForgeStartReq = 676; + public static final int ForgeStartRsp = 672; + public static final int FoundationNotify = 890; + public static final int FoundationReq = 898; + public static final int FoundationRsp = 845; + public static final int FriendInfoChangeNotify = 4088; + public static final int FunitureMakeMakeInfoChangeNotify = 4488; + public static final int FurnitureCurModuleArrangeCountNotify = 4681; + public static final int FurnitureMakeBeHelpedNotify = 4626; + public static final int FurnitureMakeCancelReq = 4834; + public static final int FurnitureMakeCancelRsp = 4660; + public static final int FurnitureMakeFinishNotify = 4557; + public static final int FurnitureMakeHelpReq = 4580; + public static final int FurnitureMakeHelpRsp = 4722; + public static final int FurnitureMakeReq = 4551; + public static final int FurnitureMakeRsp = 4530; + public static final int FurnitureMakeStartReq = 4582; + public static final int FurnitureMakeStartRsp = 4463; + public static final int GachaOpenWishNotify = 1585; + public static final int GachaSimpleInfoNotify = 1535; + public static final int GachaWishReq = 1532; + public static final int GachaWishRsp = 1517; + public static final int GadgetAutoPickDropInfoNotify = 830; + public static final int GadgetChainLevelChangeNotify = 824; + public static final int GadgetChainLevelUpdateNotify = 863; + public static final int GadgetCustomTreeInfoNotify = 822; + public static final int GadgetGeneralRewardInfoNotify = 807; + public static final int GadgetInteractReq = 809; + public static final int GadgetInteractRsp = 831; + public static final int GadgetPlayDataNotify = 855; + public static final int GadgetPlayStartNotify = 849; + public static final int GadgetPlayStopNotify = 882; + public static final int GadgetPlayUidOpNotify = 827; + public static final int GadgetStateNotify = 841; + public static final int GadgetTalkChangeNotify = 886; + public static final int GalleryBalloonScoreNotify = 5541; + public static final int GalleryBalloonShootNotify = 5531; + public static final int GalleryBounceConjuringHitNotify = 5598; + public static final int GalleryBrokenFloorFallNotify = 5527; + public static final int GalleryBulletHitNotify = 5555; + public static final int GalleryFallCatchNotify = 5532; + public static final int GalleryFallScoreNotify = 5517; + public static final int GalleryFlowerCatchNotify = 5549; + public static final int GalleryPreStartNotify = 5582; + public static final int GalleryStartNotify = 5509; + public static final int GalleryStopNotify = 5537; + public static final int GallerySumoKillMonsterNotify = 5545; + public static final int GetActivityInfoReq = 2067; + public static final int GetActivityInfoRsp = 2144; + public static final int GetActivityScheduleReq = 2049; + public static final int GetActivityScheduleRsp = 2044; + public static final int GetActivityShopSheetInfoReq = 785; + public static final int GetActivityShopSheetInfoRsp = 735; + public static final int GetAllActivatedBargainDataReq = 413; + public static final int GetAllActivatedBargainDataRsp = 442; + public static final int GetAllH5ActivityInfoReq = 5659; + public static final int GetAllH5ActivityInfoRsp = 5668; + public static final int GetAllMailReq = 1455; + public static final int GetAllMailRsp = 1427; + public static final int GetAllSceneGalleryInfoReq = 5585; + public static final int GetAllSceneGalleryInfoRsp = 5535; + public static final int GetAllUnlockNameCardReq = 4012; + public static final int GetAllUnlockNameCardRsp = 4058; + public static final int GetAreaExplorePointReq = 228; + public static final int GetAreaExplorePointRsp = 276; + public static final int GetAuthkeyReq = 1435; + public static final int GetAuthkeyRsp = 1449; + public static final int GetAuthSalesmanInfoReq = 2161; + public static final int GetAuthSalesmanInfoRsp = 2039; + public static final int GetBargainDataReq = 450; public static final int GetBargainDataRsp = 420; - public static final int GetBattlePassProductReq = 2643; - public static final int GetBattlePassProductRsp = 2626; - public static final int GetBlossomBriefInfoListReq = 2760; - public static final int GetBlossomBriefInfoListRsp = 2733; - public static final int GetBonusActivityRewardReq = 2528; - public static final int GetBonusActivityRewardRsp = 2555; - public static final int GetChatEmojiCollectionReq = 4057; - public static final int GetChatEmojiCollectionRsp = 4043; - public static final int GetCityHuntingOfferReq = 4456; - public static final int GetCityHuntingOfferRsp = 4747; - public static final int GetCityReputationInfoReq = 2860; - public static final int GetCityReputationInfoRsp = 2833; - public static final int GetCityReputationMapInfoReq = 2891; - public static final int GetCityReputationMapInfoRsp = 2897; - public static final int GetCompoundDataReq = 127; - public static final int GetCompoundDataRsp = 102; - public static final int GetDailyDungeonEntryInfoReq = 913; - public static final int GetDailyDungeonEntryInfoRsp = 936; - public static final int GetDungeonEntryExploreConditionReq = 3208; - public static final int GetDungeonEntryExploreConditionRsp = 3391; - public static final int GetExpeditionAssistInfoListReq = 2124; - public static final int GetExpeditionAssistInfoListRsp = 2168; - public static final int GetFriendShowAvatarInfoReq = 4061; - public static final int GetFriendShowAvatarInfoRsp = 4038; - public static final int GetFriendShowNameCardInfoReq = 4032; - public static final int GetFriendShowNameCardInfoRsp = 4051; - public static final int GetFurnitureCurModuleArrangeCountReq = 4618; - public static final int GetGachaInfoReq = 1560; - public static final int GetGachaInfoRsp = 1533; - public static final int GetHomeLevelUpRewardReq = 4508; - public static final int GetHomeLevelUpRewardRsp = 4864; - public static final int GetHuntingOfferRewardReq = 4769; - public static final int GetHuntingOfferRewardRsp = 4860; - public static final int GetInvestigationMonsterReq = 1928; - public static final int GetInvestigationMonsterRsp = 1921; - public static final int GetMailItemReq = 1415; - public static final int GetMailItemRsp = 1437; - public static final int GetMapAreaReq = 3269; - public static final int GetMapAreaRsp = 3366; - public static final int GetMapMarkTipsReq = 3307; - public static final int GetMapMarkTipsRsp = 3040; - public static final int GetMechanicusInfoReq = 3960; - public static final int GetMechanicusInfoRsp = 3933; - public static final int GetNextResourceInfoReq = 139; - public static final int GetNextResourceInfoRsp = 187; - public static final int GetOnlinePlayerInfoReq = 46; - public static final int GetOnlinePlayerInfoRsp = 74; - public static final int GetOnlinePlayerListReq = 45; - public static final int GetOnlinePlayerListRsp = 75; - public static final int GetOpActivityInfoReq = 5160; - public static final int GetOpActivityInfoRsp = 5133; - public static final int GetPlayerAskFriendListReq = 4054; - public static final int GetPlayerAskFriendListRsp = 4035; - public static final int GetPlayerBlacklistReq = 4002; - public static final int GetPlayerBlacklistRsp = 4052; - public static final int GetPlayerFriendListReq = 4060; - public static final int GetPlayerFriendListRsp = 4033; - public static final int GetPlayerHomeCompInfoReq = 4540; - public static final int GetPlayerMpModeAvailabilityReq = 1843; - public static final int GetPlayerMpModeAvailabilityRsp = 1826; - public static final int GetPlayerSocialDetailReq = 4075; - public static final int GetPlayerSocialDetailRsp = 4034; - public static final int GetPlayerTokenReq = 160; - public static final int GetPlayerTokenRsp = 133; - public static final int GetPushTipsRewardReq = 2265; - public static final int GetPushTipsRewardRsp = 2203; - public static final int GetQuestTalkHistoryReq = 445; - public static final int GetQuestTalkHistoryRsp = 475; - public static final int GetRecentMpPlayerListReq = 4066; - public static final int GetRecentMpPlayerListRsp = 4073; - public static final int GetRegionSearchReq = 5613; - public static final int GetReunionMissionInfoReq = 5093; - public static final int GetReunionMissionInfoRsp = 5076; - public static final int GetReunionPrivilegeInfoReq = 5089; - public static final int GetReunionPrivilegeInfoRsp = 5071; - public static final int GetReunionSignInInfoReq = 5063; - public static final int GetReunionSignInInfoRsp = 5051; - public static final int GetSceneAreaReq = 262; - public static final int GetSceneAreaRsp = 289; - public static final int GetSceneNpcPositionReq = 515; - public static final int GetSceneNpcPositionRsp = 537; - public static final int GetScenePerformanceReq = 3217; - public static final int GetScenePerformanceRsp = 3319; - public static final int GetScenePointReq = 288; - public static final int GetScenePointRsp = 228; - public static final int GetShopmallDataReq = 737; - public static final int GetShopmallDataRsp = 793; - public static final int GetShopReq = 760; - public static final int GetShopRsp = 733; - public static final int GetSignInRewardReq = 2537; - public static final int GetSignInRewardRsp = 2593; - public static final int GetWidgetSlotReq = 4258; - public static final int GetWidgetSlotRsp = 4294; - public static final int GetWorldMpInfoReq = 3439; - public static final int GetWorldMpInfoRsp = 3424; - public static final int GiveUpRoguelikeDungeonCardReq = 8425; - public static final int GiveUpRoguelikeDungeonCardRsp = 8976; - public static final int GivingRecordChangeNotify = 172; - public static final int GivingRecordNotify = 153; - public static final int GMShowNavMeshReq = 2384; - public static final int GMShowNavMeshRsp = 2312; - public static final int GMShowObstacleReq = 2332; - public static final int GMShowObstacleRsp = 2351; - public static final int GmTalkNotify = 3; - public static final int GmTalkReq = 33; - public static final int GmTalkRsp = 10; - public static final int GrantRewardNotify = 680; - public static final int GroupLinkAllNotify = 5785; - public static final int GroupLinkChangeNotify = 5775; - public static final int GroupLinkDeleteNotify = 5795; - public static final int GroupSuiteNotify = 3368; - public static final int GroupUnloadNotify = 3019; - public static final int GuestBeginEnterSceneNotify = 3033; - public static final int GuestPostEnterSceneNotify = 3198; - public static final int H5ActivityIdsNotify = 5695; - public static final int HideAndSeekPlayerReadyNotify = 5330; - public static final int HideAndSeekPlayerSetAvatarNotify = 5347; - public static final int HideAndSeekSelectAvatarReq = 5313; - public static final int HideAndSeekSelectAvatarRsp = 5336; - public static final int HideAndSeekSelectSkillReq = 8689; - public static final int HideAndSeekSelectSkillRsp = 8948; - public static final int HideAndSeekSetReadyReq = 5324; - public static final int HideAndSeekSetReadyRsp = 5361; - public static final int HideAndSeekSettleNotify = 5338; - public static final int HitClientTrivialNotify = 250; - public static final int HitTreeNotify = 3432; - public static final int HomeAvatarAllFinishRewardNotify = 4612; - public static final int HomeAvatarCostumeChangeNotify = 4888; - public static final int HomeAvatarRewardEventGetReq = 4568; - public static final int HomeAvatarRewardEventGetRsp = 4453; - public static final int HomeAvatarRewardEventNotify = 4801; - public static final int HomeAvatarSummonAllEventNotify = 4575; - public static final int HomeAvatarSummonEventReq = 4871; - public static final int HomeAvatarSummonEventRsp = 4895; - public static final int HomeAvatarSummonFinishReq = 4654; - public static final int HomeAvatarSummonFinishRsp = 4503; - public static final int HomeAvatarTalkFinishInfoNotify = 4800; - public static final int HomeAvatarTalkReq = 4610; - public static final int HomeAvatarTalkRsp = 4507; - public static final int HomeAvtarAllFinishRewardNotify = 4546; - public static final int HomeBasicInfoNotify = 4869; - public static final int HomeBlockNotify = 4784; - public static final int HomeChangeEditModeReq = 4483; - public static final int HomeChangeEditModeRsp = 4880; - public static final int HomeChangeModuleReq = 4604; - public static final int HomeChangeModuleRsp = 4631; - public static final int HomeChooseModuleReq = 4567; - public static final int HomeChooseModuleRsp = 4633; + public static final int GetBattlePassProductReq = 2616; + public static final int GetBattlePassProductRsp = 2601; + public static final int GetBlossomBriefInfoListReq = 2709; + public static final int GetBlossomBriefInfoListRsp = 2731; + public static final int GetBonusActivityRewardReq = 2597; + public static final int GetBonusActivityRewardRsp = 2598; + public static final int GetChatEmojiCollectionReq = 4005; + public static final int GetChatEmojiCollectionRsp = 4010; + public static final int GetCityHuntingOfferReq = 4343; + public static final int GetCityHuntingOfferRsp = 4304; + public static final int GetCityReputationInfoReq = 2809; + public static final int GetCityReputationInfoRsp = 2831; + public static final int GetCityReputationMapInfoReq = 2827; + public static final int GetCityReputationMapInfoRsp = 2807; + public static final int GetCompoundDataReq = 128; + public static final int GetCompoundDataRsp = 176; + public static final int GetDailyDungeonEntryInfoReq = 929; + public static final int GetDailyDungeonEntryInfoRsp = 925; + public static final int GetDungeonEntryExploreConditionReq = 3136; + public static final int GetDungeonEntryExploreConditionRsp = 3099; + public static final int GetExpeditionAssistInfoListReq = 2181; + public static final int GetExpeditionAssistInfoListRsp = 2045; + public static final int GetFriendShowAvatarInfoReq = 4003; + public static final int GetFriendShowAvatarInfoRsp = 4034; + public static final int GetFriendShowNameCardInfoReq = 4093; + public static final int GetFriendShowNameCardInfoRsp = 4060; + public static final int GetFurnitureCurModuleArrangeCountReq = 4603; + public static final int GetGachaInfoReq = 1509; + public static final int GetGachaInfoRsp = 1531; + public static final int GetHomeLevelUpRewardReq = 4747; + public static final int GetHomeLevelUpRewardRsp = 4517; + public static final int GetHuntingOfferRewardReq = 4328; + public static final int GetHuntingOfferRewardRsp = 4303; + public static final int GetInvestigationMonsterReq = 1902; + public static final int GetInvestigationMonsterRsp = 1911; + public static final int GetMailItemReq = 1437; + public static final int GetMailItemRsp = 1432; + public static final int GetMapAreaReq = 3466; + public static final int GetMapAreaRsp = 3012; + public static final int GetMapMarkTipsReq = 3329; + public static final int GetMapMarkTipsRsp = 3118; + public static final int GetMechanicusInfoReq = 3909; + public static final int GetMechanicusInfoRsp = 3931; + public static final int GetNextResourceInfoReq = 153; + public static final int GetNextResourceInfoRsp = 139; + public static final int GetOnlinePlayerInfoReq = 45; + public static final int GetOnlinePlayerInfoRsp = 90; + public static final int GetOnlinePlayerListReq = 35; + public static final int GetOnlinePlayerListRsp = 49; + public static final int GetOpActivityInfoReq = 5109; + public static final int GetOpActivityInfoRsp = 5131; + public static final int GetPlayerAskFriendListReq = 4008; + public static final int GetPlayerAskFriendListRsp = 4004; + public static final int GetPlayerBlacklistReq = 4076; + public static final int GetPlayerBlacklistRsp = 4072; + public static final int GetPlayerFriendListReq = 4009; + public static final int GetPlayerFriendListRsp = 4031; + public static final int GetPlayerHomeCompInfoReq = 4845; + public static final int GetPlayerMpModeAvailabilityReq = 1816; + public static final int GetPlayerMpModeAvailabilityRsp = 1801; + public static final int GetPlayerSocialDetailReq = 4049; + public static final int GetPlayerSocialDetailRsp = 4082; + public static final int GetPlayerTokenReq = 109; + public static final int GetPlayerTokenRsp = 131; + public static final int GetPushTipsRewardReq = 2212; + public static final int GetPushTipsRewardRsp = 2258; + public static final int GetQuestTalkHistoryReq = 435; + public static final int GetQuestTalkHistoryRsp = 449; + public static final int GetRecentMpPlayerListReq = 4011; + public static final int GetRecentMpPlayerListRsp = 4022; + public static final int GetRegionSearchReq = 5628; + public static final int GetReunionMissionInfoReq = 5066; + public static final int GetReunionMissionInfoRsp = 5051; + public static final int GetReunionPrivilegeInfoReq = 5062; + public static final int GetReunionPrivilegeInfoRsp = 5096; + public static final int GetReunionSignInInfoReq = 5078; + public static final int GetReunionSignInInfoRsp = 5053; + public static final int GetSceneAreaReq = 238; + public static final int GetSceneAreaRsp = 206; + public static final int GetSceneNpcPositionReq = 537; + public static final int GetSceneNpcPositionRsp = 532; + public static final int GetScenePerformanceReq = 3340; + public static final int GetScenePerformanceRsp = 3447; + public static final int GetScenePointReq = 230; + public static final int GetScenePointRsp = 297; + public static final int GetShopmallDataReq = 732; + public static final int GetShopmallDataRsp = 717; + public static final int GetShopReq = 709; + public static final int GetShopRsp = 731; + public static final int GetSignInRewardReq = 2532; + public static final int GetSignInRewardRsp = 2517; + public static final int GetWidgetSlotReq = 4267; + public static final int GetWidgetSlotRsp = 4300; + public static final int GetWorldMpInfoReq = 3264; + public static final int GetWorldMpInfoRsp = 3179; + public static final int GiveUpRoguelikeDungeonCardReq = 8977; + public static final int GiveUpRoguelikeDungeonCardRsp = 8583; + public static final int GivingRecordChangeNotify = 189; + public static final int GivingRecordNotify = 187; + public static final int GMShowNavMeshReq = 2377; + public static final int GMShowNavMeshRsp = 2343; + public static final int GMShowObstacleReq = 2393; + public static final int GMShowObstacleRsp = 2360; + public static final int GmTalkNotify = 58; + public static final int GmTalkReq = 31; + public static final int GmTalkRsp = 41; + public static final int GrantRewardNotify = 613; + public static final int GroupLinkAllNotify = 5768; + public static final int GroupLinkChangeNotify = 5759; + public static final int GroupLinkDeleteNotify = 5793; + public static final int GroupSuiteNotify = 3476; + public static final int GroupUnloadNotify = 3416; + public static final int GuestBeginEnterSceneNotify = 3125; + public static final int GuestPostEnterSceneNotify = 3379; + public static final int H5ActivityIdsNotify = 5693; + public static final int HideAndSeekPlayerReadyNotify = 5302; + public static final int HideAndSeekPlayerSetAvatarNotify = 5316; + public static final int HideAndSeekSelectAvatarReq = 5329; + public static final int HideAndSeekSelectAvatarRsp = 5325; + public static final int HideAndSeekSelectSkillReq = 8307; + public static final int HideAndSeekSelectSkillRsp = 8227; + public static final int HideAndSeekSetReadyReq = 5383; + public static final int HideAndSeekSetReadyRsp = 5303; + public static final int HideAndSeekSettleNotify = 5334; + public static final int HitClientTrivialNotify = 218; + public static final int HitTreeNotify = 3222; + public static final int HomeAvatarAllFinishRewardNotify = 4648; + public static final int HomeAvatarCostumeChangeNotify = 4685; + public static final int HomeAvatarRewardEventGetReq = 4775; + public static final int HomeAvatarRewardEventGetRsp = 4873; + public static final int HomeAvatarRewardEventNotify = 4789; + public static final int HomeAvatarSummonAllEventNotify = 4617; + public static final int HomeAvatarSummonEventReq = 4615; + public static final int HomeAvatarSummonEventRsp = 4498; + public static final int HomeAvatarSummonFinishReq = 4748; + public static final int HomeAvatarSummonFinishRsp = 4782; + public static final int HomeAvatarTalkFinishInfoNotify = 4502; + public static final int HomeAvatarTalkReq = 4712; + public static final int HomeAvatarTalkRsp = 4700; + public static final int HomeAvtarAllFinishRewardNotify = 4849; + public static final int HomeBasicInfoNotify = 4872; + public static final int HomeBlockNotify = 4542; + public static final int HomeChangeEditModeReq = 4625; + public static final int HomeChangeEditModeRsp = 4885; + public static final int HomeChangeModuleReq = 4688; + public static final int HomeChangeModuleRsp = 4821; + public static final int HomeChooseModuleReq = 4479; + public static final int HomeChooseModuleRsp = 4745; public static final int HomeComfortInfoNotify = 4649; - public static final int HomeCustomFurnitureInfoNotify = 4636; - public static final int HomeEditCustomFurnitureReq = 4621; - public static final int HomeEditCustomFurnitureRsp = 4804; - public static final int HomeFishFarmingInfoNotify = 4893; - public static final int HomeGetArrangementInfoReq = 4480; - public static final int HomeGetArrangementInfoRsp = 4781; - public static final int HomeGetBasicInfoReq = 4839; - public static final int HomeGetFishFarmingInfoReq = 4848; - public static final int HomeGetFishFarmingInfoRsp = 4451; - public static final int HomeGetOnlineStatusReq = 4673; - public static final int HomeGetOnlineStatusRsp = 4626; - public static final int HomeKickPlayerReq = 4547; - public static final int HomeKickPlayerRsp = 4897; - public static final int HomeLimitedShopBuyGoodsReq = 4620; - public static final int HomeLimitedShopBuyGoodsRsp = 4667; - public static final int HomeLimitedShopGoodsListReq = 4706; - public static final int HomeLimitedShopGoodsListRsp = 4587; - public static final int HomeLimitedShopInfoChangeNotify = 4691; - public static final int HomeLimitedShopInfoNotify = 4679; - public static final int HomeLimitedShopInfoReq = 4715; - public static final int HomeLimitedShopInfoRsp = 4808; - public static final int HomeMarkPointNotify = 4764; - public static final int HomeModuleSeenReq = 4642; - public static final int HomeModuleSeenRsp = 4688; - public static final int HomeModuleUnlockNotify = 4778; - public static final int HomePlantFieldNotify = 4529; - public static final int HomePlantInfoNotify = 4832; - public static final int HomePlantInfoReq = 4771; - public static final int HomePlantInfoRsp = 4806; - public static final int HomePlantSeedReq = 4730; - public static final int HomePlantSeedRsp = 4857; - public static final int HomePlantWeedReq = 4760; - public static final int HomePlantWeedRsp = 4463; - public static final int HomePriorCheckNotify = 4601; - public static final int HomeResourceNotify = 4513; - public static final int HomeResourceTakeFetterExpReq = 4884; - public static final int HomeResourceTakeFetterExpRsp = 4608; - public static final int HomeResourceTakeHomeCoinReq = 4812; - public static final int HomeResourceTakeHomeCoinRsp = 4481; - public static final int HomeSceneInitFinishReq = 4520; - public static final int HomeSceneInitFinishRsp = 4605; - public static final int HomeSceneJumpReq = 4862; - public static final int HomeSceneJumpRsp = 4663; - public static final int HomeTransferReq = 4656; - public static final int HomeTransferRsp = 4669; - public static final int HomeUpdateArrangementInfoReq = 4632; - public static final int HomeUpdateArrangementInfoRsp = 4820; - public static final int HomeUpdateFishFarmingInfoReq = 4810; - public static final int HomeUpdateFishFarmingInfoRsp = 4500; - public static final int HostPlayerNotify = 310; - public static final int HuntingFailNotify = 4345; - public static final int HuntingGiveUpReq = 4313; - public static final int HuntingGiveUpRsp = 4301; - public static final int HuntingOngoingNotify = 4348; - public static final int HuntingRevealClueNotify = 4564; - public static final int HuntingRevealFinalNotify = 4335; - public static final int HuntingStartNotify = 4694; - public static final int HuntingSuccessNotify = 4325; - public static final int InBattleMechanicusBuildingPointsNotify = 5344; - public static final int InBattleMechanicusCardResultNotify = 5388; - public static final int InBattleMechanicusConfirmCardNotify = 5397; - public static final int InBattleMechanicusConfirmCardReq = 5379; - public static final int InBattleMechanicusConfirmCardRsp = 5391; - public static final int InBattleMechanicusExcapeMonsterNotify = 5337; - public static final int InBattleMechanicusLeftMonsterNotify = 5393; - public static final int InBattleMechanicusPickCardNotify = 5334; - public static final int InBattleMechanicusPickCardReq = 5345; - public static final int InBattleMechanicusPickCardRsp = 5375; - public static final int InBattleMechanicusSettleNotify = 5355; - public static final int InteractDailyDungeonInfoNotify = 947; - public static final int InterruptGalleryReq = 5597; - public static final int InterruptGalleryRsp = 5588; - public static final int InvestigationMonsterUpdateNotify = 1918; - public static final int ItemAddHintNotify = 637; - public static final int ItemCdGroupTimeNotify = 666; - public static final int ItemExceedLimitNotify = 639; - public static final int ItemGivingReq = 170; - public static final int ItemGivingRsp = 154; - public static final int JoinHomeWorldFailNotify = 4502; - public static final int JoinPlayerFailNotify = 295; - public static final int JoinPlayerSceneReq = 239; - public static final int JoinPlayerSceneRsp = 287; - public static final int KeepAliveNotify = 60; - public static final int LeaveSceneReq = 233; - public static final int LeaveSceneRsp = 210; - public static final int LeaveWorldNotify = 3203; - public static final int LevelupCityReq = 253; - public static final int LevelupCityRsp = 272; - public static final int LifeStateChangeNotify = 1233; + public static final int HomeCustomFurnitureInfoNotify = 4482; + public static final int HomeEditCustomFurnitureReq = 4516; + public static final int HomeEditCustomFurnitureRsp = 4871; + public static final int HomeFishFarmingInfoNotify = 4682; + public static final int HomeGetArrangementInfoReq = 4848; + public static final int HomeGetArrangementInfoRsp = 4456; + public static final int HomeGetBasicInfoReq = 4535; + public static final int HomeGetFishFarmingInfoReq = 4455; + public static final int HomeGetFishFarmingInfoRsp = 4889; + public static final int HomeGetOnlineStatusReq = 4796; + public static final int HomeGetOnlineStatusRsp = 4554; + public static final int HomeKickPlayerReq = 4809; + public static final int HomeKickPlayerRsp = 4752; + public static final int HomeLimitedShopBuyGoodsReq = 4638; + public static final int HomeLimitedShopBuyGoodsRsp = 4790; + public static final int HomeLimitedShopGoodsListReq = 4579; + public static final int HomeLimitedShopGoodsListRsp = 4846; + public static final int HomeLimitedShopInfoChangeNotify = 4814; + public static final int HomeLimitedShopInfoNotify = 4621; + public static final int HomeLimitedShopInfoReq = 4616; + public static final int HomeLimitedShopInfoRsp = 4585; + public static final int HomeMarkPointNotify = 4746; + public static final int HomeModuleSeenReq = 4546; + public static final int HomeModuleSeenRsp = 4526; + public static final int HomeModuleUnlockNotify = 4781; + public static final int HomePlantFieldNotify = 4466; + public static final int HomePlantInfoNotify = 4835; + public static final int HomePlantInfoReq = 4833; + public static final int HomePlantInfoRsp = 4859; + public static final int HomePlantSeedReq = 4614; + public static final int HomePlantSeedRsp = 4804; + public static final int HomePlantWeedReq = 4877; + public static final int HomePlantWeedRsp = 4575; + public static final int HomePriorCheckNotify = 4690; + public static final int HomeResourceNotify = 4806; + public static final int HomeResourceTakeFetterExpReq = 4840; + public static final int HomeResourceTakeFetterExpRsp = 4500; + public static final int HomeResourceTakeHomeCoinReq = 4763; + public static final int HomeResourceTakeHomeCoinRsp = 4658; + public static final int HomeSceneInitFinishReq = 4552; + public static final int HomeSceneInitFinishRsp = 4592; + public static final int HomeSceneJumpReq = 4659; + public static final int HomeSceneJumpRsp = 4570; + public static final int HomeTransferReq = 4880; + public static final int HomeTransferRsp = 4767; + public static final int HomeUpdateArrangementInfoReq = 4472; + public static final int HomeUpdateArrangementInfoRsp = 4822; + public static final int HomeUpdateFishFarmingInfoReq = 4604; + public static final int HomeUpdateFishFarmingInfoRsp = 4856; + public static final int HostPlayerNotify = 341; + public static final int HuntingFailNotify = 4302; + public static final int HuntingGiveUpReq = 4337; + public static final int HuntingGiveUpRsp = 4332; + public static final int HuntingOngoingNotify = 4313; + public static final int HuntingRevealClueNotify = 4347; + public static final int HuntingRevealFinalNotify = 4316; + public static final int HuntingStartNotify = 4326; + public static final int HuntingSuccessNotify = 4301; + public static final int InBattleMechanicusBuildingPointsNotify = 5385; + public static final int InBattleMechanicusCardResultNotify = 5330; + public static final int InBattleMechanicusConfirmCardNotify = 5307; + public static final int InBattleMechanicusConfirmCardReq = 5355; + public static final int InBattleMechanicusConfirmCardRsp = 5327; + public static final int InBattleMechanicusExcapeMonsterNotify = 5332; + public static final int InBattleMechanicusLeftMonsterNotify = 5317; + public static final int InBattleMechanicusPickCardNotify = 5382; + public static final int InBattleMechanicusPickCardReq = 5335; + public static final int InBattleMechanicusPickCardRsp = 5349; + public static final int InBattleMechanicusSettleNotify = 5398; + public static final int InteractDailyDungeonInfoNotify = 916; + public static final int InterruptGalleryReq = 5507; + public static final int InterruptGalleryRsp = 5530; + public static final int InvestigationMonsterUpdateNotify = 1922; + public static final int ItemAddHintNotify = 632; + public static final int ItemCdGroupTimeNotify = 611; + public static final int ItemGivingReq = 178; + public static final int ItemGivingRsp = 108; + public static final int JoinHomeWorldFailNotify = 4863; + public static final int JoinPlayerFailNotify = 268; + public static final int JoinPlayerSceneReq = 253; + public static final int JoinPlayerSceneRsp = 239; + public static final int KeepAliveNotify = 9; + public static final int LeaveSceneReq = 231; + public static final int LeaveSceneRsp = 241; + public static final int LeaveWorldNotify = 3420; + public static final int LevelupCityReq = 287; + public static final int LevelupCityRsp = 289; + public static final int LifeStateChangeNotify = 1231; public static final int LiveEndNotify = 801; public static final int LiveStartNotify = 820; - public static final int LoadActivityTerrainNotify = 2152; - public static final int LuaEnvironmentEffectNotify = 3175; - public static final int LuaSetOptionNotify = 353; - public static final int LunaRiteAreaFinishNotify = 8822; - public static final int LunaRiteGroupBundleRegisterNotify = 8956; - public static final int LunaRiteHintPointRemoveNotify = 8544; - public static final int LunaRiteHintPointReq = 8173; - public static final int LunaRiteHintPointRsp = 8129; - public static final int LunaRiteSacrificeReq = 8364; - public static final int LunaRiteSacrificeRsp = 8328; - public static final int LunaRiteTakeSacrificeRewardReq = 8987; - public static final int LunaRiteTakeSacrificeRewardRsp = 8352; - public static final int MailChangeNotify = 1433; - public static final int MainCoopUpdateNotify = 1975; - public static final int MapAreaChangeNotify = 3055; - public static final int MarkEntityInMinMapNotify = 230; - public static final int MarkMapReq = 3053; - public static final int MarkMapRsp = 3139; - public static final int MarkNewNotify = 1291; - public static final int MarkTargetInvestigationMonsterNotify = 1924; - public static final int MassiveEntityElementOpBatchNotify = 384; - public static final int MassiveEntityStateChangedNotify = 361; - public static final int MaterialDeleteReturnNotify = 632; - public static final int MaterialDeleteUpdateNotify = 612; - public static final int McoinExchangeHcoinReq = 653; - public static final int McoinExchangeHcoinRsp = 672; - public static final int MechanicusCandidateTeamCreateReq = 3928; - public static final int MechanicusCandidateTeamCreateRsp = 3955; - public static final int MechanicusCloseNotify = 3993; - public static final int MechanicusCoinNotify = 3915; - public static final int MechanicusLevelupGearReq = 3975; - public static final int MechanicusLevelupGearRsp = 3934; - public static final int MechanicusOpenNotify = 3937; - public static final int MechanicusSequenceOpenNotify = 3910; - public static final int MechanicusUnlockGearReq = 3944; - public static final int MechanicusUnlockGearRsp = 3945; - public static final int MeetNpcReq = 544; - public static final int MeetNpcRsp = 545; - public static final int MetNpcIdListNotify = 593; - public static final int MiracleRingDataNotify = 5245; - public static final int MiracleRingDeliverItemReq = 5217; - public static final int MiracleRingDeliverItemRsp = 5236; - public static final int MiracleRingDestroyNotify = 5243; - public static final int MiracleRingDropResultNotify = 5201; - public static final int MiracleRingTakeRewardReq = 5248; - public static final int MiracleRingTakeRewardRsp = 5213; - public static final int MistTrialDunegonFailNotify = 8337; - public static final int MistTrialGetChallengeMissionReq = 8250; - public static final int MistTrialGetChallengeMissionRsp = 8894; - public static final int MistTrialSelectAvatarAndEnterDungeonReq = 8186; - public static final int MistTrialSelectAvatarAndEnterDungeonRsp = 8522; - public static final int MonsterAIConfigHashNotify = 3024; - public static final int MonsterAlertChangeNotify = 380; - public static final int MonsterForceAlertNotify = 364; - public static final int MonsterPointArrayRouteUpdateNotify = 3292; - public static final int MonsterSummonTagNotify = 1360; - public static final int MpBlockNotify = 1824; - public static final int MpPlayGuestReplyInviteReq = 1850; - public static final int MpPlayGuestReplyInviteRsp = 1822; - public static final int MpPlayGuestReplyNotify = 1802; - public static final int MpPlayInviteResultNotify = 1830; - public static final int MpPlayOwnerCheckReq = 1812; - public static final int MpPlayOwnerCheckRsp = 1839; - public static final int MpPlayOwnerInviteNotify = 1831; - public static final int MpPlayOwnerStartInviteReq = 1821; - public static final int MpPlayOwnerStartInviteRsp = 1846; - public static final int MpPlayPrepareInterruptNotify = 1838; - public static final int MpPlayPrepareNotify = 1833; - public static final int MultistagePlayEndNotify = 5307; - public static final int MultistagePlayFinishStageReq = 5333; - public static final int MultistagePlayFinishStageRsp = 5328; - public static final int MultistagePlayInfoNotify = 5360; - public static final int MultistagePlaySettleNotify = 5318; - public static final int MultistagePlayStageEndNotify = 5321; - public static final int MusicGameSettleReq = 8851; - public static final int MusicGameSettleRsp = 8028; - public static final int MusicGameStartReq = 8760; - public static final int MusicGameStartRsp = 8922; - public static final int NavMeshStatsNotify = 2353; - public static final int NormalUidOpNotify = 5735; - public static final int NpcTalkReq = 560; - public static final int NpcTalkRsp = 533; - public static final int ObstacleModifyNotify = 2310; - public static final int OfferingInteractReq = 2916; - public static final int OfferingInteractRsp = 2912; - public static final int OneoffGatherPointDetectorDataNotify = 4289; - public static final int OpActivityDataNotify = 5110; - public static final int OpActivityStateNotify = 2560; - public static final int OpActivityUpdateNotify = 5115; - public static final int OpenBlossomCircleCampGuideNotify = 2744; - public static final int OpenStateChangeNotify = 165; - public static final int OpenStateUpdateNotify = 109; - public static final int OrderDisplayNotify = 4101; - public static final int OrderFinishNotify = 4145; - public static final int OtherPlayerEnterHomeNotify = 4591; - public static final int PacketHead = 13371337; - public static final int PathfindingEnterSceneReq = 2337; - public static final int PathfindingEnterSceneRsp = 2393; - public static final int PathfindingPingNotify = 2315; - public static final int PersonalLineAllDataReq = 408; - public static final int PersonalLineAllDataRsp = 417; - public static final int PersonalLineNewUnlockNotify = 423; - public static final int PersonalSceneJumpReq = 286; - public static final int PersonalSceneJumpRsp = 277; - public static final int PingReq = 37; - public static final int PingRsp = 93; - public static final int PlantFlowerAcceptAllGiveFlowerReq = 8775; - public static final int PlantFlowerAcceptAllGiveFlowerRsp = 8174; - public static final int PlantFlowerAcceptGiveFlowerReq = 8525; - public static final int PlantFlowerAcceptGiveFlowerRsp = 8957; - public static final int PlantFlowerEditFlowerCombinationReq = 8347; - public static final int PlantFlowerEditFlowerCombinationRsp = 8625; - public static final int PlantFlowerGetCanGiveFriendFlowerReq = 8617; - public static final int PlantFlowerGetCanGiveFriendFlowerRsp = 8091; - public static final int PlantFlowerGetFriendFlowerWishListReq = 8893; - public static final int PlantFlowerGetFriendFlowerWishListRsp = 8476; - public static final int PlantFlowerGetRecvFlowerListReq = 8459; - public static final int PlantFlowerGetRecvFlowerListRsp = 8597; - public static final int PlantFlowerGetSeedInfoReq = 8535; - public static final int PlantFlowerGetSeedInfoRsp = 8584; - public static final int PlantFlowerGiveFriendFlowerReq = 8934; - public static final int PlantFlowerGiveFriendFlowerRsp = 8456; - public static final int PlantFlowerHaveRecvFlowerNotify = 8711; - public static final int PlantFlowerSetFlowerWishReq = 8601; - public static final int PlantFlowerSetFlowerWishRsp = 8550; - public static final int PlantFlowerTakeSeedRewardReq = 8317; - public static final int PlantFlowerTakeSeedRewardRsp = 8152; - public static final int PlatformChangeRouteNotify = 257; - public static final int PlatformStartRouteNotify = 254; - public static final int PlatformStopRouteNotify = 235; - public static final int PlayerAllowEnterMpAfterAgreeMatchNotify = 4176; - public static final int PlayerApplyEnterHomeNotify = 4614; - public static final int PlayerApplyEnterHomeResultNotify = 4580; - public static final int PlayerApplyEnterHomeResultReq = 4603; - public static final int PlayerApplyEnterHomeResultRsp = 4849; - public static final int PlayerApplyEnterMpAfterMatchAgreedNotify = 4177; - public static final int PlayerApplyEnterMpNotify = 1835; - public static final int PlayerApplyEnterMpReq = 1825; - public static final int PlayerApplyEnterMpResultNotify = 1848; - public static final int PlayerApplyEnterMpResultReq = 1813; - public static final int PlayerApplyEnterMpResultRsp = 1801; - public static final int PlayerApplyEnterMpRsp = 1845; - public static final int PlayerCancelMatchReq = 4198; - public static final int PlayerCancelMatchRsp = 4163; - public static final int PlayerChatCDNotify = 3173; - public static final int PlayerChatNotify = 3485; - public static final int PlayerChatReq = 3403; - public static final int PlayerChatRsp = 3045; - public static final int PlayerCompoundMaterialReq = 173; - public static final int PlayerCompoundMaterialRsp = 163; - public static final int PlayerConfirmMatchReq = 4186; - public static final int PlayerConfirmMatchRsp = 4193; - public static final int PlayerCookArgsReq = 135; - public static final int PlayerCookArgsRsp = 157; - public static final int PlayerCookReq = 103; - public static final int PlayerCookRsp = 167; - public static final int PlayerDataNotify = 145; - public static final int PlayerEnterDungeonReq = 910; - public static final int PlayerEnterDungeonRsp = 915; - public static final int PlayerEnterSceneInfoNotify = 294; - public static final int PlayerEnterSceneNotify = 260; - public static final int PlayerEyePointStateNotify = 3461; - public static final int PlayerFishingDataNotify = 5831; - public static final int PlayerForceExitReq = 125; - public static final int PlayerForceExitRsp = 149; - public static final int PlayerGameTimeNotify = 179; - public static final int PlayerGeneralMatchConfirmNotify = 4156; + public static final int LoadActivityTerrainNotify = 2175; + public static final int LuaEnvironmentEffectNotify = 3289; + public static final int LuaSetOptionNotify = 387; + public static final int LunaRiteAreaFinishNotify = 8107; + public static final int LunaRiteGroupBundleRegisterNotify = 8327; + public static final int LunaRiteHintPointRemoveNotify = 8420; + public static final int LunaRiteHintPointReq = 8342; + public static final int LunaRiteHintPointRsp = 8481; + public static final int LunaRiteSacrificeReq = 8062; + public static final int LunaRiteSacrificeRsp = 8401; + public static final int LunaRiteTakeSacrificeRewardReq = 8669; + public static final int LunaRiteTakeSacrificeRewardRsp = 8252; + public static final int MailChangeNotify = 1431; + public static final int MainCoopUpdateNotify = 1959; + public static final int MapAreaChangeNotify = 3103; + public static final int MarkEntityInMinMapNotify = 202; + public static final int MarkMapReq = 3208; + public static final int MarkMapRsp = 3421; + public static final int MarkNewNotify = 1227; + public static final int MarkTargetInvestigationMonsterNotify = 1928; + public static final int MassiveEntityElementOpBatchNotify = 377; + public static final int MassiveEntityStateChangedNotify = 303; + public static final int MaterialDeleteReturnNotify = 693; + public static final int MaterialDeleteUpdateNotify = 643; + public static final int McoinExchangeHcoinReq = 687; + public static final int McoinExchangeHcoinRsp = 689; + public static final int MechanicusCandidateTeamCreateReq = 3997; + public static final int MechanicusCandidateTeamCreateRsp = 3998; + public static final int MechanicusCloseNotify = 3917; + public static final int MechanicusCoinNotify = 3937; + public static final int MechanicusLevelupGearReq = 3949; + public static final int MechanicusLevelupGearRsp = 3982; + public static final int MechanicusOpenNotify = 3932; + public static final int MechanicusSequenceOpenNotify = 3941; + public static final int MechanicusUnlockGearReq = 3985; + public static final int MechanicusUnlockGearRsp = 3935; + public static final int MeetNpcReq = 585; + public static final int MeetNpcRsp = 535; + public static final int MetNpcIdListNotify = 517; + public static final int MiracleRingDataNotify = 5243; + public static final int MiracleRingDeliverItemReq = 5226; + public static final int MiracleRingDeliverItemRsp = 5247; + public static final int MiracleRingDestroyNotify = 5216; + public static final int MiracleRingDropResultNotify = 5203; + public static final int MiracleRingTakeRewardReq = 5204; + public static final int MiracleRingTakeRewardRsp = 5228; + public static final int MistTrialDunegonFailNotify = 8580; + public static final int MistTrialGetChallengeMissionReq = 8668; + public static final int MistTrialGetChallengeMissionRsp = 8385; + public static final int MistTrialSelectAvatarAndEnterDungeonReq = 8318; + public static final int MistTrialSelectAvatarAndEnterDungeonRsp = 8403; + public static final int MonsterAIConfigHashNotify = 3121; + public static final int MonsterAlertChangeNotify = 313; + public static final int MonsterForceAlertNotify = 342; + public static final int MonsterPointArrayRouteUpdateNotify = 3138; + public static final int MonsterSummonTagNotify = 1309; + public static final int MpBlockNotify = 1808; + public static final int MpPlayGuestReplyInviteReq = 1811; + public static final int MpPlayGuestReplyInviteRsp = 1841; + public static final int MpPlayGuestReplyNotify = 1805; + public static final int MpPlayInviteResultNotify = 1806; + public static final int MpPlayOwnerCheckReq = 1814; + public static final int MpPlayOwnerCheckRsp = 1812; + public static final int MpPlayOwnerInviteNotify = 1815; + public static final int MpPlayOwnerStartInviteReq = 1846; + public static final int MpPlayOwnerStartInviteRsp = 1827; + public static final int MpPlayPrepareInterruptNotify = 1848; + public static final int MpPlayPrepareNotify = 1839; + public static final int MultistagePlayEndNotify = 5375; + public static final int MultistagePlayFinishStageReq = 5331; + public static final int MultistagePlayFinishStageRsp = 5397; + public static final int MultistagePlayInfoNotify = 5309; + public static final int MultistagePlaySettleNotify = 5314; + public static final int MultistagePlayStageEndNotify = 5340; + public static final int MusicGameSettleReq = 8745; + public static final int MusicGameSettleRsp = 8288; + public static final int MusicGameStartReq = 8927; + public static final int MusicGameStartRsp = 8101; + public static final int NavMeshStatsNotify = 2387; + public static final int NormalUidOpNotify = 5718; + public static final int NpcTalkReq = 509; + public static final int NpcTalkRsp = 531; + public static final int ObstacleModifyNotify = 2341; + public static final int OfferingInteractReq = 2908; + public static final int OfferingInteractRsp = 2923; + public static final int OneoffGatherPointDetectorDataNotify = 4262; + public static final int OpActivityDataNotify = 5141; + public static final int OpActivityStateNotify = 2509; + public static final int OpActivityUpdateNotify = 5137; + public static final int OpenBlossomCircleCampGuideNotify = 2785; + public static final int OpenStateChangeNotify = 112; + public static final int OpenStateUpdateNotify = 119; + public static final int OrderDisplayNotify = 4103; + public static final int OrderFinishNotify = 4143; + public static final int OtherPlayerEnterHomeNotify = 4609; + public static final int PathfindingEnterSceneReq = 2332; + public static final int PathfindingEnterSceneRsp = 2317; + public static final int PathfindingPingNotify = 2337; + public static final int PersonalLineAllDataReq = 446; + public static final int PersonalLineAllDataRsp = 433; + public static final int PersonalLineNewUnlockNotify = 470; + public static final int PersonalSceneJumpReq = 266; + public static final int PersonalSceneJumpRsp = 248; + public static final int PingReq = 32; + public static final int PingRsp = 17; + public static final int PlantFlowerAcceptAllGiveFlowerReq = 8017; + public static final int PlantFlowerAcceptAllGiveFlowerRsp = 8831; + public static final int PlantFlowerAcceptGiveFlowerReq = 8137; + public static final int PlantFlowerAcceptGiveFlowerRsp = 8431; + public static final int PlantFlowerEditFlowerCombinationReq = 8506; + public static final int PlantFlowerEditFlowerCombinationRsp = 8335; + public static final int PlantFlowerGetCanGiveFriendFlowerReq = 8648; + public static final int PlantFlowerGetCanGiveFriendFlowerRsp = 8507; + public static final int PlantFlowerGetFriendFlowerWishListReq = 8840; + public static final int PlantFlowerGetFriendFlowerWishListRsp = 8553; + public static final int PlantFlowerGetRecvFlowerListReq = 8914; + public static final int PlantFlowerGetRecvFlowerListRsp = 8929; + public static final int PlantFlowerGetSeedInfoReq = 8913; + public static final int PlantFlowerGetSeedInfoRsp = 8932; + public static final int PlantFlowerGiveFriendFlowerReq = 8836; + public static final int PlantFlowerGiveFriendFlowerRsp = 8893; + public static final int PlantFlowerHaveRecvFlowerNotify = 8174; + public static final int PlantFlowerSetFlowerWishReq = 8650; + public static final int PlantFlowerSetFlowerWishRsp = 8588; + public static final int PlantFlowerTakeSeedRewardReq = 8182; + public static final int PlantFlowerTakeSeedRewardRsp = 8386; + public static final int PlatformChangeRouteNotify = 205; + public static final int PlatformStartRouteNotify = 208; + public static final int PlatformStopRouteNotify = 204; + public static final int PlayerAllowEnterMpAfterAgreeMatchNotify = 4151; + public static final int PlayerApplyEnterHomeNotify = 4504; + public static final int PlayerApplyEnterHomeResultNotify = 4766; + public static final int PlayerApplyEnterHomeResultReq = 4568; + public static final int PlayerApplyEnterHomeResultRsp = 4697; + public static final int PlayerApplyEnterMpAfterMatchAgreedNotify = 4163; + public static final int PlayerApplyEnterMpNotify = 1818; + public static final int PlayerApplyEnterMpReq = 1809; + public static final int PlayerApplyEnterMpResultNotify = 1804; + public static final int PlayerApplyEnterMpResultReq = 1828; + public static final int PlayerApplyEnterMpResultRsp = 1803; + public static final int PlayerApplyEnterMpRsp = 1843; + public static final int PlayerCancelMatchReq = 4154; + public static final int PlayerCancelMatchRsp = 4178; + public static final int PlayerChatCDNotify = 3464; + public static final int PlayerChatNotify = 3295; + public static final int PlayerChatReq = 3378; + public static final int PlayerChatRsp = 3321; + public static final int PlayerCompoundMaterialReq = 122; + public static final int PlayerCompoundMaterialRsp = 154; + public static final int PlayerConfirmMatchReq = 4197; + public static final int PlayerConfirmMatchRsp = 4166; + public static final int PlayerCookArgsReq = 104; + public static final int PlayerCookArgsRsp = 105; + public static final int PlayerCookReq = 158; + public static final int PlayerCookRsp = 150; + public static final int PlayerDataNotify = 135; + public static final int PlayerEnterDungeonReq = 941; + public static final int PlayerEnterDungeonRsp = 937; + public static final int PlayerEnterSceneInfoNotify = 274; + public static final int PlayerEnterSceneNotify = 209; + public static final int PlayerEyePointStateNotify = 3376; + public static final int PlayerFishingDataNotify = 5815; + public static final int PlayerForceExitReq = 173; + public static final int PlayerForceExitRsp = 161; + public static final int PlayerGameTimeNotify = 155; + public static final int PlayerGeneralMatchConfirmNotify = 4182; public static final int PlayerGeneralMatchDismissNotify = 4187; - public static final int PlayerGetForceQuitBanInfoReq = 4162; - public static final int PlayerGetForceQuitBanInfoRsp = 4189; - public static final int PlayerHomeCompInfoNotify = 4863; - public static final int PlayerInjectFixNotify = 185; - public static final int PlayerInvestigationAllInfoNotify = 1920; - public static final int PlayerInvestigationNotify = 1901; - public static final int PlayerInvestigationTargetNotify = 1919; - public static final int PlayerLevelRewardUpdateNotify = 112; - public static final int PlayerLoginReq = 110; - public static final int PlayerLoginRsp = 115; - public static final int PlayerLogoutNotify = 144; - public static final int PlayerLogoutReq = 137; - public static final int PlayerLogoutRsp = 193; - public static final int PlayerLuaShellNotify = 143; - public static final int PlayerMatchAgreedResultNotify = 4165; - public static final int PlayerMatchInfoNotify = 4195; - public static final int PlayerMatchStopNotify = 4151; - public static final int PlayerMatchSuccNotify = 4167; - public static final int PlayerOfferingDataNotify = 2915; - public static final int PlayerOfferingReq = 2914; - public static final int PlayerOfferingRsp = 2917; - public static final int PlayerPreEnterMpNotify = 1836; - public static final int PlayerPropChangeNotify = 156; - public static final int PlayerPropChangeReasonNotify = 1234; - public static final int PlayerPropNotify = 191; - public static final int PlayerQuitDungeonReq = 937; - public static final int PlayerQuitDungeonRsp = 993; - public static final int PlayerQuitFromHomeNotify = 4757; - public static final int PlayerQuitFromMpNotify = 1817; + public static final int PlayerGetForceQuitBanInfoReq = 4164; + public static final int PlayerGetForceQuitBanInfoRsp = 4162; + public static final int PlayerHomeCompInfoNotify = 4563; + public static final int PlayerInjectFixNotify = 188; + public static final int PlayerInvestigationAllInfoNotify = 1905; + public static final int PlayerInvestigationNotify = 1927; + public static final int PlayerInvestigationTargetNotify = 1906; + public static final int PlayerLevelRewardUpdateNotify = 143; + public static final int PlayerLoginReq = 141; + public static final int PlayerLoginRsp = 137; + public static final int PlayerLogoutNotify = 185; + public static final int PlayerLogoutReq = 132; + public static final int PlayerLogoutRsp = 117; + public static final int PlayerLuaShellNotify = 110; + public static final int PlayerMatchAgreedResultNotify = 4152; + public static final int PlayerMatchInfoNotify = 4193; + public static final int PlayerMatchStopNotify = 4153; + public static final int PlayerMatchSuccNotify = 4176; + public static final int PlayerOfferingDataNotify = 2901; + public static final int PlayerOfferingReq = 2906; + public static final int PlayerOfferingRsp = 2911; + public static final int PlayerPreEnterMpNotify = 1847; + public static final int PlayerPropChangeNotify = 186; + public static final int PlayerPropChangeReasonNotify = 1282; + public static final int PlayerPropNotify = 127; + public static final int PlayerQuitDungeonReq = 932; + public static final int PlayerQuitDungeonRsp = 917; + public static final int PlayerQuitFromHomeNotify = 4776; + public static final int PlayerQuitFromMpNotify = 1826; public static final int PlayerRandomCookReq = 120; - public static final int PlayerRandomCookRsp = 180; - public static final int PlayerRechargeDataNotify = 4113; - public static final int PlayerReportReq = 4092; - public static final int PlayerReportRsp = 4058; - public static final int PlayerRoutineDataNotify = 3535; - public static final int PlayerSetLanguageReq = 123; - public static final int PlayerSetLanguageRsp = 113; - public static final int PlayerSetOnlyMPWithPSPlayerReq = 1815; - public static final int PlayerSetOnlyMPWithPSPlayerRsp = 1827; - public static final int PlayerSetPauseReq = 192; - public static final int PlayerSetPauseRsp = 158; - public static final int PlayerStartMatchReq = 4185; - public static final int PlayerStartMatchRsp = 4175; - public static final int PlayerStoreNotify = 660; - public static final int PlayerTimeNotify = 152; - public static final int PlayerWorldSceneInfoListNotify = 3386; - public static final int PostEnterSceneReq = 3390; - public static final int PostEnterSceneRsp = 3213; - public static final int PrivateChatNotify = 4960; - public static final int PrivateChatReq = 5010; - public static final int PrivateChatRsp = 4983; - public static final int PrivateChatSetSequenceReq = 4965; - public static final int PrivateChatSetSequenceRsp = 4987; - public static final int ProfilePictureChangeNotify = 4053; - public static final int ProjectorOptionReq = 880; - public static final int ProjectorOptionRsp = 864; - public static final int ProudSkillChangeNotify = 1079; - public static final int ProudSkillExtraLevelNotify = 1028; - public static final int ProudSkillUpgradeReq = 1075; - public static final int ProudSkillUpgradeRsp = 1034; - public static final int PSNBlackListNotify = 4070; - public static final int PSNFriendListNotify = 4072; + public static final int PlayerRandomCookRsp = 113; + public static final int PlayerRechargeDataNotify = 4128; + public static final int PlayerReportReq = 4059; + public static final int PlayerReportRsp = 4084; + public static final int PlayerRoutineDataNotify = 3518; + public static final int PlayerSetLanguageReq = 170; + public static final int PlayerSetLanguageRsp = 129; + public static final int PlayerSetOnlyMPWithPSPlayerReq = 1802; + public static final int PlayerSetOnlyMPWithPSPlayerRsp = 1813; + public static final int PlayerSetPauseReq = 159; + public static final int PlayerSetPauseRsp = 184; + public static final int PlayerStartMatchReq = 4168; + public static final int PlayerStartMatchRsp = 4159; + public static final int PlayerStoreNotify = 609; + public static final int PlayerTimeNotify = 172; + public static final int PlayerWorldSceneInfoListNotify = 3172; + public static final int PostEnterSceneReq = 3097; + public static final int PostEnterSceneRsp = 3344; + public static final int PrivateChatNotify = 4991; + public static final int PrivateChatReq = 4959; + public static final int PrivateChatRsp = 4981; + public static final int PrivateChatSetSequenceReq = 4987; + public static final int PrivateChatSetSequenceRsp = 4982; + public static final int ProfilePictureChangeNotify = 4087; + public static final int ProjectorOptionReq = 813; + public static final int ProjectorOptionRsp = 842; + public static final int ProudSkillChangeNotify = 1055; + public static final int ProudSkillExtraLevelNotify = 1097; + public static final int ProudSkillUpgradeReq = 1049; + public static final int ProudSkillUpgradeRsp = 1082; + public static final int PSNBlackListNotify = 4078; + public static final int PSNFriendListNotify = 4089; public static final int PSPlayerApplyEnterMpReq = 1837; - public static final int PSPlayerApplyEnterMpRsp = 1806; - public static final int PullPrivateChatReq = 5043; - public static final int PullPrivateChatRsp = 4994; - public static final int PullRecentChatReq = 4995; - public static final int PullRecentChatRsp = 5025; - public static final int PushTipsAllDataNotify = 2226; - public static final int PushTipsChangeNotify = 2262; - public static final int PushTipsReadFinishReq = 2289; - public static final int PushTipsReadFinishRsp = 2209; + public static final int PSPlayerApplyEnterMpRsp = 1832; + public static final int PullPrivateChatReq = 4967; + public static final int PullPrivateChatRsp = 5035; + public static final int PullRecentChatReq = 4985; + public static final int PullRecentChatRsp = 4999; + public static final int PushTipsAllDataNotify = 2224; + public static final int PushTipsChangeNotify = 2238; + public static final int PushTipsReadFinishReq = 2206; + public static final int PushTipsReadFinishRsp = 2219; public static final int QueryCodexMonsterBeKilledNumReq = 4207; - public static final int QueryCodexMonsterBeKilledNumRsp = 4208; - public static final int QueryPathReq = 2360; - public static final int QueryPathRsp = 2333; - public static final int QuestCreateEntityReq = 434; - public static final int QuestCreateEntityRsp = 479; - public static final int QuestDelNotify = 410; - public static final int QuestDestroyEntityReq = 491; - public static final int QuestDestroyEntityRsp = 497; - public static final int QuestDestroyNpcReq = 426; - public static final int QuestDestroyNpcRsp = 462; - public static final int QuestGlobalVarNotify = 466; - public static final int QuestListNotify = 460; - public static final int QuestListUpdateNotify = 433; - public static final int QuestProgressUpdateNotify = 446; - public static final int QuestTransmitReq = 473; - public static final int QuestTransmitRsp = 463; - public static final int QuestUpdateQuestTimeVarNotify = 458; - public static final int QuestUpdateQuestVarNotify = 483; - public static final int QuestUpdateQuestVarReq = 474; - public static final int QuestUpdateQuestVarRsp = 456; - public static final int QuickUseWidgetReq = 4276; - public static final int QuickUseWidgetRsp = 4265; - public static final int ReadMailNotify = 1410; - public static final int ReadPrivateChatReq = 4984; - public static final int ReadPrivateChatRsp = 5029; - public static final int ReceivedTrialAvatarActivityRewardReq = 2020; - public static final int ReceivedTrialAvatarActivityRewardRsp = 2087; - public static final int RechargeReq = 4135; - public static final int RechargeRsp = 4125; - public static final int RedeemLegendaryKeyReq = 481; - public static final int RedeemLegendaryKeyRsp = 427; - public static final int RefreshBackgroundAvatarReq = 1744; - public static final int RefreshBackgroundAvatarRsp = 1719; - public static final int RefreshRoguelikeDungeonCardReq = 8433; - public static final int RefreshRoguelikeDungeonCardRsp = 8678; - public static final int RegionSearchChangeRegionNotify = 5625; - public static final int RegionSearchNotify = 5635; - public static final int ReliquaryDecomposeReq = 606; - public static final int ReliquaryDecomposeRsp = 659; - public static final int ReliquaryPromoteReq = 665; - public static final int ReliquaryPromoteRsp = 603; - public static final int ReliquaryUpgradeReq = 689; - public static final int ReliquaryUpgradeRsp = 609; - public static final int RemoveBlacklistReq = 4080; - public static final int RemoveBlacklistRsp = 4064; - public static final int RemoveRandTaskInfoNotify = 132; - public static final int ReportFightAntiCheatNotify = 357; - public static final int ReportTrackingIOInfoNotify = 4117; - public static final int RequestLiveInfoReq = 803; - public static final int RequestLiveInfoRsp = 867; - public static final int ResinCardDataUpdateNotify = 4126; - public static final int ResinChangeNotify = 623; - public static final int RestartEffigyChallengeReq = 2061; - public static final int RestartEffigyChallengeRsp = 2112; - public static final int ReunionActivateNotify = 5081; - public static final int ReunionBriefInfoReq = 5085; - public static final int ReunionBriefInfoRsp = 5075; - public static final int ReunionDailyRefreshNotify = 5072; - public static final int ReunionPrivilegeChangeNotify = 5100; - public static final int ReunionSettleNotify = 5096; - public static final int RobotPushPlayerDataNotify = 88; - public static final int RogueCellUpdateNotify = 8074; - public static final int RogueDungeonPlayerCellChangeNotify = 8784; - public static final int RogueHealAvatarsReq = 8959; - public static final int RogueHealAvatarsRsp = 8473; - public static final int RoguelikeCardGachaNotify = 8079; - public static final int RoguelikeEffectDataNotify = 8529; - public static final int RoguelikeEffectViewReq = 8396; - public static final int RoguelikeEffectViewRsp = 8040; - public static final int RoguelikeGiveUpReq = 8716; - public static final int RoguelikeGiveUpRsp = 8728; - public static final int RoguelikeMistClearNotify = 8471; - public static final int RoguelikeRefreshCardCostUpdateNotify = 8304; - public static final int RoguelikeResourceBonusPropUpdateNotify = 8312; - public static final int RoguelikeRuneRecordUpdateNotify = 8985; - public static final int RoguelikeSelectAvatarAndEnterDungeonReq = 8697; - public static final int RoguelikeSelectAvatarAndEnterDungeonRsp = 8377; - public static final int RoguelikeTakeStageFirstPassRewardReq = 8667; - public static final int RoguelikeTakeStageFirstPassRewardRsp = 8101; - public static final int RogueResumeDungeonReq = 8496; - public static final int RogueResumeDungeonRsp = 8759; - public static final int RogueSwitchAvatarReq = 8856; - public static final int RogueSwitchAvatarRsp = 8141; - public static final int SalesmanDeliverItemReq = 2103; - public static final int SalesmanDeliverItemRsp = 2198; - public static final int SalesmanTakeRewardReq = 2091; - public static final int SalesmanTakeRewardRsp = 2171; - public static final int SalesmanTakeSpecialRewardReq = 2156; - public static final int SalesmanTakeSpecialRewardRsp = 2102; - public static final int SaveCoopDialogReq = 1972; - public static final int SaveCoopDialogRsp = 1952; - public static final int SaveMainCoopReq = 1995; - public static final int SaveMainCoopRsp = 1998; - public static final int SceneAreaUnlockNotify = 209; - public static final int SceneAreaWeatherNotify = 213; - public static final int SceneAudioNotify = 3260; - public static final int SceneAvatarStaminaStepReq = 234; - public static final int SceneAvatarStaminaStepRsp = 279; - public static final int SceneCreateEntityReq = 267; + public static final int QueryCodexMonsterBeKilledNumRsp = 4205; + public static final int QueryPathReq = 2309; + public static final int QueryPathRsp = 2331; + public static final int QuestCreateEntityReq = 482; + public static final int QuestCreateEntityRsp = 455; + public static final int QuestDelNotify = 441; + public static final int QuestDestroyEntityReq = 427; + public static final int QuestDestroyEntityRsp = 407; + public static final int QuestDestroyNpcReq = 424; + public static final int QuestDestroyNpcRsp = 438; + public static final int QuestGlobalVarNotify = 411; + public static final int QuestListNotify = 409; + public static final int QuestListUpdateNotify = 431; + public static final int QuestProgressUpdateNotify = 445; + public static final int QuestTransmitReq = 422; + public static final int QuestTransmitRsp = 454; + public static final int QuestUpdateQuestTimeVarNotify = 484; + public static final int QuestUpdateQuestVarNotify = 463; + public static final int QuestUpdateQuestVarReq = 490; + public static final int QuestUpdateQuestVarRsp = 486; + public static final int QuickUseWidgetReq = 4251; + public static final int QuickUseWidgetRsp = 4252; + public static final int ReadMailNotify = 1441; + public static final int ReadPrivateChatReq = 5032; + public static final int ReadPrivateChatRsp = 5005; + public static final int ReceivedTrialAvatarActivityRewardReq = 2054; + public static final int ReceivedTrialAvatarActivityRewardRsp = 2199; + public static final int RechargeReq = 4118; + public static final int RechargeRsp = 4109; + public static final int RedeemLegendaryKeyReq = 436; + public static final int RedeemLegendaryKeyRsp = 428; + public static final int RefreshBackgroundAvatarReq = 1660; + public static final int RefreshBackgroundAvatarRsp = 1751; + public static final int RefreshRoguelikeDungeonCardReq = 8072; + public static final int RefreshRoguelikeDungeonCardRsp = 8644; + public static final int RegionSearchChangeRegionNotify = 5609; + public static final int RegionSearchNotify = 5618; + public static final int ReliquaryDecomposeReq = 615; + public static final int ReliquaryDecomposeRsp = 692; + public static final int ReliquaryPromoteReq = 612; + public static final int ReliquaryPromoteRsp = 658; + public static final int ReliquaryUpgradeReq = 606; + public static final int ReliquaryUpgradeRsp = 619; + public static final int RemoveBlacklistReq = 4013; + public static final int RemoveBlacklistRsp = 4042; + public static final int RemoveRandTaskInfoNotify = 193; + public static final int ReportFightAntiCheatNotify = 305; + public static final int ReportTrackingIOInfoNotify = 4126; + public static final int RequestLiveInfoReq = 858; + public static final int RequestLiveInfoRsp = 850; + public static final int ResinCardDataUpdateNotify = 4101; + public static final int ResinChangeNotify = 670; + public static final int RestartEffigyChallengeReq = 2023; + public static final int RestartEffigyChallengeRsp = 2133; + public static final int ReunionActivateNotify = 5065; + public static final int ReunionBriefInfoReq = 5068; + public static final int ReunionBriefInfoRsp = 5059; + public static final int ReunionDailyRefreshNotify = 5091; + public static final int ReunionPrivilegeChangeNotify = 5061; + public static final int ReunionSettleNotify = 5077; + public static final int RobotPushPlayerDataNotify = 30; + public static final int RogueCellUpdateNotify = 8022; + public static final int RogueDungeonPlayerCellChangeNotify = 8832; + public static final int RogueHealAvatarsReq = 8331; + public static final int RogueHealAvatarsRsp = 8937; + public static final int RoguelikeCardGachaNotify = 8618; + public static final int RoguelikeEffectDataNotify = 8606; + public static final int RoguelikeEffectViewReq = 8377; + public static final int RoguelikeEffectViewRsp = 8770; + public static final int RoguelikeGiveUpReq = 8988; + public static final int RoguelikeGiveUpRsp = 8009; + public static final int RoguelikeMistClearNotify = 8198; + public static final int RoguelikeRefreshCardCostUpdateNotify = 8912; + public static final int RoguelikeResourceBonusPropUpdateNotify = 8823; + public static final int RoguelikeRuneRecordUpdateNotify = 8497; + public static final int RoguelikeSelectAvatarAndEnterDungeonReq = 8975; + public static final int RoguelikeSelectAvatarAndEnterDungeonRsp = 8471; + public static final int RoguelikeTakeStageFirstPassRewardReq = 8205; + public static final int RoguelikeTakeStageFirstPassRewardRsp = 8409; + public static final int RogueResumeDungeonReq = 8417; + public static final int RogueResumeDungeonRsp = 8565; + public static final int RogueSwitchAvatarReq = 8930; + public static final int RogueSwitchAvatarRsp = 8556; + public static final int SalesmanDeliverItemReq = 2118; + public static final int SalesmanDeliverItemRsp = 2038; + public static final int SalesmanTakeRewardReq = 2007; + public static final int SalesmanTakeRewardRsp = 2100; + public static final int SalesmanTakeSpecialRewardReq = 2065; + public static final int SalesmanTakeSpecialRewardRsp = 2107; + public static final int SaveCoopDialogReq = 1991; + public static final int SaveCoopDialogRsp = 1955; + public static final int SaveMainCoopReq = 1993; + public static final int SaveMainCoopRsp = 1954; + public static final int SceneAreaUnlockNotify = 219; + public static final int SceneAreaWeatherNotify = 229; + public static final int SceneAudioNotify = 3430; + public static final int SceneAvatarStaminaStepReq = 282; + public static final int SceneAvatarStaminaStepRsp = 255; + public static final int SceneCreateEntityReq = 250; public static final int SceneCreateEntityRsp = 220; - public static final int SceneDataNotify = 3179; - public static final int SceneDestroyEntityReq = 280; - public static final int SceneDestroyEntityRsp = 264; - public static final int SceneEntitiesMoveCombineNotify = 3312; - public static final int SceneEntitiesMovesReq = 221; - public static final int SceneEntitiesMovesRsp = 207; - public static final int SceneEntityAppearNotify = 293; - public static final int SceneEntityDisappearNotify = 244; - public static final int SceneEntityDrownReq = 265; - public static final int SceneEntityDrownRsp = 203; - public static final int SceneEntityMoveNotify = 291; - public static final int SceneEntityMoveReq = 245; - public static final int SceneEntityMoveRsp = 275; - public static final int SceneEntityUpdateNotify = 3423; - public static final int SceneForceLockNotify = 266; + public static final int SceneDataNotify = 3117; + public static final int SceneDestroyEntityReq = 213; + public static final int SceneDestroyEntityRsp = 242; + public static final int SceneEntitiesMoveCombineNotify = 3298; + public static final int SceneEntitiesMovesReq = 240; + public static final int SceneEntitiesMovesRsp = 275; + public static final int SceneEntityAppearNotify = 217; + public static final int SceneEntityDisappearNotify = 285; + public static final int SceneEntityDrownReq = 212; + public static final int SceneEntityDrownRsp = 258; + public static final int SceneEntityMoveNotify = 227; + public static final int SceneEntityMoveReq = 235; + public static final int SceneEntityMoveRsp = 249; + public static final int SceneEntityUpdateNotify = 3089; + public static final int SceneForceLockNotify = 211; public static final int SceneForceUnlockNotify = 201; - public static final int SceneGalleryInfoNotify = 5528; - public static final int SceneInitFinishReq = 215; - public static final int SceneInitFinishRsp = 237; - public static final int SceneKickPlayerNotify = 259; - public static final int SceneKickPlayerReq = 204; - public static final int SceneKickPlayerRsp = 206; - public static final int ScenePlayBattleInfoListNotify = 4378; - public static final int ScenePlayBattleInfoNotify = 4410; - public static final int ScenePlayBattleInterruptNotify = 4441; - public static final int ScenePlayBattleResultNotify = 4447; - public static final int ScenePlayBattleUidOpNotify = 4438; - public static final int ScenePlayerInfoNotify = 236; - public static final int ScenePlayerLocationNotify = 297; - public static final int ScenePlayerSoundNotify = 243; - public static final int ScenePlayGuestReplyInviteReq = 4394; - public static final int ScenePlayGuestReplyInviteRsp = 4395; - public static final int ScenePlayGuestReplyNotify = 4425; - public static final int ScenePlayInfoListNotify = 4429; - public static final int ScenePlayInviteResultNotify = 4384; - public static final int ScenePlayOutofRegionNotify = 4405; - public static final int ScenePlayOwnerCheckReq = 4383; - public static final int ScenePlayOwnerCheckRsp = 4360; - public static final int ScenePlayOwnerInviteNotify = 4443; - public static final int ScenePlayOwnerStartInviteReq = 4365; - public static final int ScenePlayOwnerStartInviteRsp = 4387; - public static final int ScenePointUnlockNotify = 274; - public static final int SceneRouteChangeNotify = 270; - public static final int SceneTeamUpdateNotify = 1696; - public static final int SceneTimeNotify = 229; - public static final int SceneTransToPointReq = 256; - public static final int SceneTransToPointRsp = 283; - public static final int SceneUnlockInfoNotify = 3386; - public static final int SceneWeatherForcastReq = 3167; - public static final int SceneWeatherForcastRsp = 3023; - public static final int SeaLampCoinNotify = 2028; - public static final int SeaLampContributeItemReq = 2122; - public static final int SeaLampContributeItemRsp = 2084; - public static final int SeaLampFlyLampNotify = 2075; - public static final int SeaLampFlyLampReq = 2174; - public static final int SeaLampFlyLampRsp = 2080; - public static final int SeaLampPopularityNotify = 2062; - public static final int SeaLampTakeContributionRewardReq = 2052; - public static final int SeaLampTakeContributionRewardRsp = 2057; - public static final int SeaLampTakePhaseRewardReq = 2109; - public static final int SeaLampTakePhaseRewardRsp = 2132; - public static final int SealBattleBeginNotify = 225; - public static final int SealBattleEndNotify = 249; - public static final int SealBattleProgressNotify = 285; - public static final int SeeMonsterReq = 299; - public static final int SeeMonsterRsp = 300; - public static final int SelectAsterMidDifficultyReq = 2019; - public static final int SelectAsterMidDifficultyRsp = 2003; - public static final int SelectEffigyChallengeConditionReq = 2143; - public static final int SelectEffigyChallengeConditionRsp = 2072; - public static final int SelectRoguelikeDungeonCardReq = 8964; - public static final int SelectRoguelikeDungeonCardRsp = 8572; - public static final int SelectWorktopOptionReq = 837; - public static final int SelectWorktopOptionRsp = 893; - public static final int ServerAnnounceNotify = 2199; - public static final int ServerAnnounceRevokeNotify = 2129; - public static final int ServerBuffChangeNotify = 332; + public static final int SceneGalleryInfoNotify = 5597; + public static final int SceneInitFinishReq = 237; + public static final int SceneInitFinishRsp = 232; + public static final int SceneKickPlayerNotify = 292; + public static final int SceneKickPlayerReq = 295; + public static final int SceneKickPlayerRsp = 215; + public static final int ScenePlayBattleInfoListNotify = 4447; + public static final int ScenePlayBattleInfoNotify = 4359; + public static final int ScenePlayBattleInterruptNotify = 4377; + public static final int ScenePlayBattleResultNotify = 4357; + public static final int ScenePlayBattleUidOpNotify = 4380; + public static final int ScenePlayerInfoNotify = 225; + public static final int ScenePlayerLocationNotify = 207; + public static final int ScenePlayerSoundNotify = 210; + public static final int ScenePlayGuestReplyInviteReq = 4435; + public static final int ScenePlayGuestReplyInviteRsp = 4385; + public static final int ScenePlayGuestReplyNotify = 4399; + public static final int ScenePlayInfoListNotify = 4405; + public static final int ScenePlayInviteResultNotify = 4432; + public static final int ScenePlayOutofRegionNotify = 4448; + public static final int ScenePlayOwnerCheckReq = 4381; + public static final int ScenePlayOwnerCheckRsp = 4391; + public static final int ScenePlayOwnerInviteNotify = 4367; + public static final int ScenePlayOwnerStartInviteReq = 4387; + public static final int ScenePlayOwnerStartInviteRsp = 4382; + public static final int ScenePointUnlockNotify = 290; + public static final int SceneRouteChangeNotify = 278; + public static final int SceneTeamUpdateNotify = 1699; + public static final int SceneTimeNotify = 262; + public static final int SceneTransToPointReq = 286; + public static final int SceneTransToPointRsp = 263; + public static final int SceneWeatherForcastReq = 3190; + public static final int SceneWeatherForcastRsp = 3063; + public static final int SeaLampCoinNotify = 2096; + public static final int SeaLampContributeItemReq = 2168; + public static final int SeaLampContributeItemRsp = 2159; + public static final int SeaLampFlyLampNotify = 2051; + public static final int SeaLampFlyLampReq = 2071; + public static final int SeaLampFlyLampRsp = 2019; + public static final int SeaLampPopularityNotify = 2112; + public static final int SeaLampTakeContributionRewardReq = 2069; + public static final int SeaLampTakeContributionRewardRsp = 2103; + public static final int SeaLampTakePhaseRewardReq = 2092; + public static final int SeaLampTakePhaseRewardRsp = 2160; + public static final int SealBattleBeginNotify = 273; + public static final int SealBattleEndNotify = 261; + public static final int SealBattleProgressNotify = 288; + public static final int SeeMonsterReq = 281; + public static final int SeeMonsterRsp = 264; + public static final int SelectAsterMidDifficultyReq = 2190; + public static final int SelectAsterMidDifficultyRsp = 2010; + public static final int SelectEffigyChallengeConditionReq = 2111; + public static final int SelectEffigyChallengeConditionRsp = 2068; + public static final int SelectRoguelikeDungeonCardReq = 8070; + public static final int SelectRoguelikeDungeonCardRsp = 8040; + public static final int SelectWorktopOptionReq = 832; + public static final int SelectWorktopOptionRsp = 817; + public static final int ServerAnnounceNotify = 2029; + public static final int ServerAnnounceRevokeNotify = 2141; + public static final int ServerBuffChangeNotify = 393; public static final int ServerCondMeetQuestListUpdateNotify = 401; - public static final int ServerDisconnectClientNotify = 186; - public static final int ServerGlobalValueChangeNotify = 1188; - public static final int ServerLogNotify = 79; - public static final int ServerMessageNotify = 5725; - public static final int ServerTimeNotify = 34; - public static final int ServerUpdateGlobalValueNotify = 1197; + public static final int ServerDisconnectClientNotify = 166; + public static final int ServerGlobalValueChangeNotify = 1130; + public static final int ServerLogNotify = 55; + public static final int ServerMessageNotify = 5709; + public static final int ServerTimeNotify = 82; + public static final int ServerUpdateGlobalValueNotify = 1107; public static final int SetBattlePassViewedReq = 2637; - public static final int SetBattlePassViewedRsp = 2606; - public static final int SetChatEmojiCollectionReq = 4086; - public static final int SetChatEmojiCollectionRsp = 4077; - public static final int SetCoopChapterViewedReq = 1980; - public static final int SetCoopChapterViewedRsp = 1988; - public static final int SetCurExpeditionChallengeIdReq = 2017; - public static final int SetCurExpeditionChallengeIdRsp = 2099; - public static final int SetEntityClientDataNotify = 3303; - public static final int SetEquipLockStateReq = 635; - public static final int SetEquipLockStateRsp = 657; - public static final int SetFriendEnterHomeOptionReq = 4613; - public static final int SetFriendEnterHomeOptionRsp = 4724; - public static final int SetFriendRemarkNameReq = 4023; - public static final int SetFriendRemarkNameRsp = 4013; - public static final int SetH5ActivityRedDotTimestampReq = 5698; - public static final int SetH5ActivityRedDotTimestampRsp = 5663; - public static final int SetIsAutoUnlockSpecificEquipReq = 687; - public static final int SetIsAutoUnlockSpecificEquipRsp = 604; - public static final int SetLimitOptimizationNotify = 8189; - public static final int SetNameCardReq = 4089; - public static final int SetNameCardRsp = 4009; - public static final int SetOpenStateReq = 162; - public static final int SetOpenStateRsp = 189; - public static final int SetPlayerBirthdayReq = 4097; - public static final int SetPlayerBirthdayRsp = 4088; - public static final int SetPlayerBornDataReq = 155; - public static final int SetPlayerBornDataRsp = 146; - public static final int SetPlayerHeadImageReq = 4046; - public static final int SetPlayerHeadImageRsp = 4074; - public static final int SetPlayerNameReq = 183; - public static final int SetPlayerNameRsp = 126; - public static final int SetPlayerPropReq = 188; - public static final int SetPlayerPropRsp = 128; - public static final int SetPlayerSignatureReq = 4028; - public static final int SetPlayerSignatureRsp = 4055; - public static final int SetSceneWeatherAreaReq = 271; - public static final int SetSceneWeatherAreaRsp = 205; - public static final int SetUpAvatarTeamReq = 1671; - public static final int SetUpAvatarTeamRsp = 1634; - public static final int SetUpLunchBoxWidgetReq = 4286; - public static final int SetUpLunchBoxWidgetRsp = 4293; - public static final int SetWidgetSlotReq = 4266; - public static final int SetWidgetSlotRsp = 4279; - public static final int ShowClientGuideNotify = 3190; - public static final int ShowClientTutorialNotify = 3374; - public static final int ShowCommonTipsNotify = 3277; - public static final int ShowMessageNotify = 15; - public static final int ShowTemplateReminderNotify = 3164; - public static final int SignInInfoReq = 2510; - public static final int SignInInfoRsp = 2515; - public static final int SocialDataNotify = 4063; - public static final int SpringUseReq = 1720; - public static final int SpringUseRsp = 1727; - public static final int StartArenaChallengeLevelReq = 2022; - public static final int StartArenaChallengeLevelRsp = 2033; - public static final int StartBuoyantCombatGalleryReq = 8313; - public static final int StartBuoyantCombatGalleryRsp = 8754; - public static final int StartCoopPointReq = 1956; - public static final int StartCoopPointRsp = 1962; - public static final int StartEffigyChallengeReq = 2123; - public static final int StartEffigyChallengeRsp = 2166; - public static final int StartFishingReq = 5845; - public static final int StartFishingRsp = 5848; - public static final int StartRogueEliteCellChallengeReq = 8620; - public static final int StartRogueEliteCellChallengeRsp = 8198; - public static final int StartRogueNormalCellChallengeReq = 8748; - public static final int StartRogueNormalCellChallengeRsp = 8630; - public static final int StoreItemChangeNotify = 610; - public static final int StoreItemDelNotify = 615; - public static final int StoreWeightLimitNotify = 633; - public static final int SummerTimeFloatSignalPositionNotify = 8389; - public static final int SummerTimeFloatSignalUpdateNotify = 8763; - public static final int SummerTimeSprintBoatRestartReq = 8682; - public static final int SummerTimeSprintBoatRestartRsp = 8562; - public static final int SummerTimeSprintBoatSettleNotify = 8150; - public static final int SumoDungeonSettleNotify = 8946; - public static final int SumoEnterDungeonNotify = 8540; - public static final int SumoLeaveDungeonNotify = 8460; - public static final int SumoRestartDungeonReq = 8602; - public static final int SumoRestartDungeonRsp = 8498; - public static final int SumoSaveTeamReq = 8874; - public static final int SumoSaveTeamRsp = 8287; - public static final int SumoSelectTeamAndEnterDungeonReq = 8605; - public static final int SumoSelectTeamAndEnterDungeonRsp = 8998; - public static final int SumoSetNoSwitchPunishTimeNotify = 8804; - public static final int SumoSwitchTeamReq = 8718; - public static final int SumoSwitchTeamRsp = 8356; - public static final int SyncScenePlayTeamEntityNotify = 3296; - public static final int SyncTeamEntityNotify = 338; - public static final int TakeAchievementGoalRewardReq = 2695; - public static final int TakeAchievementGoalRewardRsp = 2698; - public static final int TakeAchievementRewardReq = 2685; - public static final int TakeAchievementRewardRsp = 2675; - public static final int TakeAsterSpecialRewardReq = 2051; - public static final int TakeAsterSpecialRewardRsp = 2041; - public static final int TakeBattlePassMissionPointReq = 2617; - public static final int TakeBattlePassMissionPointRsp = 2636; - public static final int TakeBattlePassRewardReq = 2613; - public static final int TakeBattlePassRewardRsp = 2601; - public static final int TakeCityReputationExploreRewardReq = 2888; - public static final int TakeCityReputationExploreRewardRsp = 2828; - public static final int TakeCityReputationLevelRewardReq = 2810; - public static final int TakeCityReputationLevelRewardRsp = 2815; - public static final int TakeCityReputationParentQuestReq = 2893; - public static final int TakeCityReputationParentQuestRsp = 2844; - public static final int TakeCompoundOutputReq = 108; - public static final int TakeCompoundOutputRsp = 117; - public static final int TakeCoopRewardReq = 1996; - public static final int TakeCoopRewardRsp = 1981; - public static final int TakeDeliveryDailyRewardReq = 2055; - public static final int TakeDeliveryDailyRewardRsp = 2104; - public static final int TakeEffigyFirstPassRewardReq = 2071; - public static final int TakeEffigyFirstPassRewardRsp = 2034; - public static final int TakeEffigyRewardReq = 2113; - public static final int TakeEffigyRewardRsp = 2008; - public static final int TakeFirstShareRewardReq = 4008; - public static final int TakeFirstShareRewardRsp = 4017; - public static final int TakeFurnitureMakeReq = 4751; - public static final int TakeFurnitureMakeRsp = 4457; - public static final int TakeHuntingOfferReq = 4750; - public static final int TakeHuntingOfferRsp = 4782; - public static final int TakeInvestigationRewardReq = 1926; - public static final int TakeInvestigationRewardRsp = 1925; - public static final int TakeInvestigationTargetRewardReq = 1915; - public static final int TakeInvestigationTargetRewardRsp = 1929; - public static final int TakeMaterialDeleteReturnReq = 651; - public static final int TakeMaterialDeleteReturnRsp = 684; - public static final int TakeoffEquipReq = 655; - public static final int TakeoffEquipRsp = 646; - public static final int TakeOfferingLevelRewardReq = 2921; - public static final int TakeOfferingLevelRewardRsp = 2910; - public static final int TakePlayerLevelRewardReq = 151; - public static final int TakePlayerLevelRewardRsp = 184; - public static final int TakeRegionSearchRewardReq = 5645; - public static final int TakeRegionSearchRewardRsp = 5648; - public static final int TakeResinCardDailyRewardReq = 4136; - public static final int TakeResinCardDailyRewardRsp = 4143; - public static final int TakeReunionFirstGiftRewardReq = 5095; - public static final int TakeReunionFirstGiftRewardRsp = 5098; - public static final int TakeReunionMissionRewardReq = 5056; - public static final int TakeReunionMissionRewardRsp = 5062; - public static final int TakeReunionSignInRewardReq = 5067; - public static final int TakeReunionSignInRewardRsp = 5086; - public static final int TakeReunionWatcherRewardReq = 5065; - public static final int TakeReunionWatcherRewardRsp = 5077; - public static final int TaskVarNotify = 178; - public static final int TeamResonanceChangeNotify = 1046; - public static final int TowerAllDataReq = 2445; - public static final int TowerAllDataRsp = 2475; - public static final int TowerBriefDataNotify = 2460; - public static final int TowerBuffSelectReq = 2497; - public static final int TowerBuffSelectRsp = 2488; - public static final int TowerCurLevelRecordChangeNotify = 2410; - public static final int TowerDailyRewardProgressChangeNotify = 2415; - public static final int TowerEnterLevelReq = 2479; - public static final int TowerEnterLevelRsp = 2491; - public static final int TowerFloorRecordChangeNotify = 2433; - public static final int TowerGetFloorStarRewardReq = 2489; - public static final int TowerGetFloorStarRewardRsp = 2409; - public static final int TowerLevelEndNotify = 2464; + public static final int SetBattlePassViewedRsp = 2632; + public static final int SetChatEmojiCollectionReq = 4066; + public static final int SetChatEmojiCollectionRsp = 4048; + public static final int SetCoopChapterViewedReq = 1956; + public static final int SetCoopChapterViewedRsp = 1998; + public static final int SetCurExpeditionChallengeIdReq = 2130; + public static final int SetCurExpeditionChallengeIdRsp = 2093; + public static final int SetEntityClientDataNotify = 3375; + public static final int SetEquipLockStateReq = 604; + public static final int SetEquipLockStateRsp = 605; + public static final int SetFriendEnterHomeOptionReq = 4761; + public static final int SetFriendEnterHomeOptionRsp = 4590; + public static final int SetFriendRemarkNameReq = 4070; + public static final int SetFriendRemarkNameRsp = 4029; + public static final int SetH5ActivityRedDotTimestampReq = 5654; + public static final int SetH5ActivityRedDotTimestampRsp = 5678; + public static final int SetIsAutoUnlockSpecificEquipReq = 639; + public static final int SetIsAutoUnlockSpecificEquipRsp = 695; + public static final int SetLimitOptimizationNotify = 8194; + public static final int SetNameCardReq = 4006; + public static final int SetNameCardRsp = 4019; + public static final int SetOpenStateReq = 138; + public static final int SetOpenStateRsp = 106; + public static final int SetPlayerBirthdayReq = 4007; + public static final int SetPlayerBirthdayRsp = 4030; + public static final int SetPlayerBornDataReq = 198; + public static final int SetPlayerBornDataRsp = 145; + public static final int SetPlayerHeadImageReq = 4045; + public static final int SetPlayerHeadImageRsp = 4090; + public static final int SetPlayerNameReq = 163; + public static final int SetPlayerNameRsp = 124; + public static final int SetPlayerPropReq = 130; + public static final int SetPlayerPropRsp = 197; + public static final int SetPlayerSignatureReq = 4097; + public static final int SetPlayerSignatureRsp = 4098; + public static final int SetSceneWeatherAreaReq = 256; + public static final int SetSceneWeatherAreaRsp = 265; + public static final int SetUpAvatarTeamReq = 1753; + public static final int SetUpAvatarTeamRsp = 1664; + public static final int SetUpLunchBoxWidgetReq = 4297; + public static final int SetUpLunchBoxWidgetRsp = 4266; + public static final int SetWidgetSlotReq = 4269; + public static final int SetWidgetSlotRsp = 4260; + public static final int ShowClientGuideNotify = 3241; + public static final int ShowClientTutorialNotify = 3079; + public static final int ShowCommonTipsNotify = 3357; + public static final int ShowMessageNotify = 37; + public static final int ShowTemplateReminderNotify = 3011; + public static final int SignInInfoReq = 2541; + public static final int SignInInfoRsp = 2537; + public static final int SocialDataNotify = 4054; + public static final int SpringUseReq = 1623; + public static final int SpringUseRsp = 1733; + public static final int StartArenaChallengeLevelReq = 2154; + public static final int StartArenaChallengeLevelRsp = 2191; + public static final int StartBuoyantCombatGalleryReq = 9000; + public static final int StartBuoyantCombatGalleryRsp = 8364; + public static final int StartCoopPointReq = 1982; + public static final int StartCoopPointRsp = 1964; + public static final int StartEffigyChallengeReq = 2157; + public static final int StartEffigyChallengeRsp = 2008; + public static final int StartFishingReq = 5843; + public static final int StartFishingRsp = 5804; + public static final int StartRogueEliteCellChallengeReq = 8722; + public static final int StartRogueEliteCellChallengeRsp = 8876; + public static final int StartRogueNormalCellChallengeReq = 8456; + public static final int StartRogueNormalCellChallengeRsp = 8156; + public static final int StoreItemChangeNotify = 641; + public static final int StoreItemDelNotify = 637; + public static final int StoreWeightLimitNotify = 631; + public static final int SummerTimeFloatSignalPositionNotify = 8622; + public static final int SummerTimeFloatSignalUpdateNotify = 8333; + public static final int SummerTimeSprintBoatRestartReq = 8175; + public static final int SummerTimeSprintBoatRestartRsp = 8563; + public static final int SummerTimeSprintBoatSettleNotify = 8117; + public static final int SumoDungeonSettleNotify = 8426; + public static final int SumoEnterDungeonNotify = 8727; + public static final int SumoLeaveDungeonNotify = 8440; + public static final int SumoRestartDungeonReq = 8585; + public static final int SumoRestartDungeonRsp = 8628; + public static final int SumoSaveTeamReq = 8970; + public static final int SumoSaveTeamRsp = 8890; + public static final int SumoSelectTeamAndEnterDungeonReq = 8030; + public static final int SumoSelectTeamAndEnterDungeonRsp = 8915; + public static final int SumoSetNoSwitchPunishTimeNotify = 8269; + public static final int SumoSwitchTeamReq = 8400; + public static final int SumoSwitchTeamRsp = 8442; + public static final int SyncScenePlayTeamEntityNotify = 3096; + public static final int SyncTeamEntityNotify = 334; + public static final int TakeAchievementGoalRewardReq = 2678; + public static final int TakeAchievementGoalRewardRsp = 2653; + public static final int TakeAchievementRewardReq = 2693; + public static final int TakeAchievementRewardRsp = 2654; + public static final int TakeAsterSpecialRewardReq = 2174; + public static final int TakeAsterSpecialRewardRsp = 2035; + public static final int TakeBattlePassMissionPointReq = 2626; + public static final int TakeBattlePassMissionPointRsp = 2647; + public static final int TakeBattlePassRewardReq = 2628; + public static final int TakeBattlePassRewardRsp = 2603; + public static final int TakeCityReputationExploreRewardReq = 2830; + public static final int TakeCityReputationExploreRewardRsp = 2897; + public static final int TakeCityReputationLevelRewardReq = 2841; + public static final int TakeCityReputationLevelRewardRsp = 2837; + public static final int TakeCityReputationParentQuestReq = 2817; + public static final int TakeCityReputationParentQuestRsp = 2885; + public static final int TakeCompoundOutputReq = 146; + public static final int TakeCompoundOutputRsp = 133; + public static final int TakeCoopRewardReq = 1977; + public static final int TakeCoopRewardRsp = 1965; + public static final int TakeDeliveryDailyRewardReq = 2037; + public static final int TakeDeliveryDailyRewardRsp = 2083; + public static final int TakeEffigyFirstPassRewardReq = 2200; + public static final int TakeEffigyFirstPassRewardRsp = 2073; + public static final int TakeEffigyRewardReq = 2062; + public static final int TakeEffigyRewardRsp = 2110; + public static final int TakeFirstShareRewardReq = 4046; + public static final int TakeFirstShareRewardRsp = 4033; + public static final int TakeFurnitureMakeReq = 4768; + public static final int TakeFurnitureMakeRsp = 4599; + public static final int TakeHuntingOfferReq = 4318; + public static final int TakeHuntingOfferRsp = 4309; + public static final int TakeInvestigationRewardReq = 1925; + public static final int TakeInvestigationRewardRsp = 1903; + public static final int TakeInvestigationTargetRewardReq = 1901; + public static final int TakeInvestigationTargetRewardRsp = 1908; + public static final int TakeMaterialDeleteReturnReq = 660; + public static final int TakeMaterialDeleteReturnRsp = 677; + public static final int TakeoffEquipReq = 698; + public static final int TakeoffEquipRsp = 645; + public static final int TakeOfferingLevelRewardReq = 2903; + public static final int TakeOfferingLevelRewardRsp = 2902; + public static final int TakePlayerLevelRewardReq = 160; + public static final int TakePlayerLevelRewardRsp = 177; + public static final int TakeRegionSearchRewardReq = 5643; + public static final int TakeRegionSearchRewardRsp = 5604; + public static final int TakeResinCardDailyRewardReq = 4147; + public static final int TakeResinCardDailyRewardRsp = 4116; + public static final int TakeReunionFirstGiftRewardReq = 5093; + public static final int TakeReunionFirstGiftRewardRsp = 5054; + public static final int TakeReunionMissionRewardReq = 5082; + public static final int TakeReunionMissionRewardRsp = 5064; + public static final int TakeReunionSignInRewardReq = 5076; + public static final int TakeReunionSignInRewardRsp = 5097; + public static final int TakeReunionWatcherRewardReq = 5052; + public static final int TakeReunionWatcherRewardRsp = 5063; + public static final int TaskVarNotify = 191; + public static final int TeamResonanceChangeNotify = 1045; + public static final int TowerAllDataReq = 2435; + public static final int TowerAllDataRsp = 2449; + public static final int TowerBriefDataNotify = 2409; + public static final int TowerBuffSelectReq = 2407; + public static final int TowerBuffSelectRsp = 2430; + public static final int TowerCurLevelRecordChangeNotify = 2441; + public static final int TowerDailyRewardProgressChangeNotify = 2437; + public static final int TowerEnterLevelReq = 2455; + public static final int TowerEnterLevelRsp = 2427; + public static final int TowerFloorRecordChangeNotify = 2431; + public static final int TowerGetFloorStarRewardReq = 2406; + public static final int TowerGetFloorStarRewardRsp = 2419; + public static final int TowerLevelEndNotify = 2442; public static final int TowerLevelStarCondNotify = 2401; - public static final int TowerMiddleLevelChangeTeamNotify = 2466; - public static final int TowerRecordHandbookReq = 2473; - public static final int TowerRecordHandbookRsp = 2463; - public static final int TowerSurrenderReq = 2426; - public static final int TowerSurrenderRsp = 2462; - public static final int TowerTeamSelectReq = 2493; - public static final int TowerTeamSelectRsp = 2444; - public static final int TreasureMapBonusChallengeNotify = 2121; - public static final int TreasureMapCurrencyNotify = 2127; - public static final int TreasureMapDetectorDataNotify = 4272; - public static final int TreasureMapGuideTaskDoneNotify = 2200; - public static final int TreasureMapHostInfoNotify = 8960; - public static final int TreasureMapMpChallengeNotify = 2177; - public static final int TreasureMapPreTaskDoneNotify = 2188; - public static final int TreasureMapRegionActiveNotify = 2141; - public static final int TreasureMapRegionInfoNotify = 2120; - public static final int TrialAvatarFirstPassDungeonNotify = 2093; - public static final int TrialAvatarInDungeonIndexNotify = 2138; - public static final int TriggerCreateGadgetToEquipPartNotify = 373; - public static final int TriggerRoguelikeCurseNotify = 8878; - public static final int TriggerRoguelikeRuneReq = 8607; - public static final int TriggerRoguelikeRuneRsp = 8246; - public static final int TryEnterHomeReq = 4622; - public static final int TryEnterHomeRsp = 4731; - public static final int UnfreezeGroupLimitNotify = 3401; - public static final int UnionCmdNotify = 55; - public static final int UnlockAvatarTalentReq = 1060; - public static final int UnlockAvatarTalentRsp = 1033; - public static final int UnlockCoopChapterReq = 1965; - public static final int UnlockCoopChapterRsp = 1977; - public static final int UnlockedFurnitureFormulaDataNotify = 4700; - public static final int UnlockedFurnitureSuiteDataNotify = 4788; + public static final int TowerMiddleLevelChangeTeamNotify = 2411; + public static final int TowerRecordHandbookReq = 2422; + public static final int TowerRecordHandbookRsp = 2454; + public static final int TowerSurrenderReq = 2424; + public static final int TowerSurrenderRsp = 2438; + public static final int TowerTeamSelectReq = 2417; + public static final int TowerTeamSelectRsp = 2485; + public static final int TreasureMapBonusChallengeNotify = 2075; + public static final int TreasureMapCurrencyNotify = 2098; + public static final int TreasureMapDetectorDataNotify = 4291; + public static final int TreasureMapGuideTaskDoneNotify = 2088; + public static final int TreasureMapHostInfoNotify = 8206; + public static final int TreasureMapMpChallengeNotify = 2036; + public static final int TreasureMapPreTaskDoneNotify = 2055; + public static final int TreasureMapRegionActiveNotify = 2059; + public static final int TreasureMapRegionInfoNotify = 2106; + public static final int TrialAvatarFirstPassDungeonNotify = 2137; + public static final int TrialAvatarInDungeonIndexNotify = 2024; + public static final int TriggerCreateGadgetToEquipPartNotify = 322; + public static final int TriggerRoguelikeCurseNotify = 8235; + public static final int TriggerRoguelikeRuneReq = 8138; + public static final int TriggerRoguelikeRuneRsp = 8781; + public static final int TryEnterHomeReq = 4553; + public static final int TryEnterHomeRsp = 4610; + public static final int UnfreezeGroupLimitNotify = 3253; + public static final int UnionCmdNotify = 98; + public static final int UnlockAvatarTalentReq = 1009; + public static final int UnlockAvatarTalentRsp = 1031; + public static final int UnlockCoopChapterReq = 1952; + public static final int UnlockCoopChapterRsp = 1963; + public static final int UnlockedFurnitureFormulaDataNotify = 4680; + public static final int UnlockedFurnitureSuiteDataNotify = 4717; public static final int UnlockNameCardNotify = 4001; - public static final int UnlockPersonalLineReq = 402; - public static final int UnlockPersonalLineRsp = 452; - public static final int UnlockTransPointReq = 3421; - public static final int UnlockTransPointRsp = 3073; - public static final int UnmarkEntityInMinMapNotify = 247; - public static final int UpdateAbilityCreatedMovingPlatformNotify = 828; - public static final int UpdatePlayerShowAvatarListReq = 4036; - public static final int UpdatePlayerShowAvatarListRsp = 4024; - public static final int UpdatePlayerShowNameCardListReq = 4030; - public static final int UpdatePlayerShowNameCardListRsp = 4047; - public static final int UpdatePS4BlockListReq = 4081; - public static final int UpdatePS4BlockListRsp = 4027; - public static final int UpdatePS4FriendListNotify = 4056; - public static final int UpdatePS4FriendListReq = 4025; - public static final int UpdatePS4FriendListRsp = 4049; - public static final int UpdateRedPointNotify = 9; + public static final int UnlockPersonalLineReq = 476; + public static final int UnlockPersonalLineRsp = 472; + public static final int UnlockTransPointReq = 3228; + public static final int UnlockTransPointRsp = 3101; + public static final int UnmarkEntityInMinMapNotify = 216; + public static final int UpdateAbilityCreatedMovingPlatformNotify = 897; + public static final int UpdatePlayerShowAvatarListReq = 4025; + public static final int UpdatePlayerShowAvatarListRsp = 4083; + public static final int UpdatePlayerShowNameCardListReq = 4002; + public static final int UpdatePlayerShowNameCardListRsp = 4016; + public static final int UpdatePS4BlockListReq = 4036; + public static final int UpdatePS4BlockListRsp = 4028; + public static final int UpdatePS4FriendListNotify = 4086; + public static final int UpdatePS4FriendListReq = 4073; + public static final int UpdatePS4FriendListRsp = 4061; + public static final int UpdateRedPointNotify = 19; public static final int UpdateReunionWatcherNotify = 5087; - public static final int UpgradeRoguelikeShikigamiReq = 8575; - public static final int UpgradeRoguelikeShikigamiRsp = 8440; - public static final int UseItemReq = 645; - public static final int UseItemRsp = 675; - public static final int UseMiracleRingReq = 5235; - public static final int UseMiracleRingRsp = 5225; - public static final int UseWidgetCreateGadgetReq = 4278; - public static final int UseWidgetCreateGadgetRsp = 4290; - public static final int UseWidgetRetractGadgetReq = 4255; - public static final int UseWidgetRetractGadgetRsp = 4297; - public static final int VehicleInteractReq = 862; - public static final int VehicleInteractRsp = 889; - public static final int VehicleSpawnReq = 809; - public static final int VehicleSpawnRsp = 865; - public static final int VehicleStaminaNotify = 866; - public static final int ViewCodexReq = 4210; - public static final int ViewCodexRsp = 4209; - public static final int WatcherAllDataNotify = 2260; - public static final int WatcherChangeNotify = 2233; - public static final int WatcherEventNotify = 2210; - public static final int WatcherEventTypeNotify = 2215; - public static final int WaterSpritePhaseFinishNotify = 2097; - public static final int WeaponAwakenReq = 664; + public static final int UpgradeRoguelikeShikigamiReq = 8422; + public static final int UpgradeRoguelikeShikigamiRsp = 8671; + public static final int UseItemReq = 635; + public static final int UseItemRsp = 649; + public static final int UseMiracleRingReq = 5218; + public static final int UseMiracleRingRsp = 5209; + public static final int UseWidgetCreateGadgetReq = 4283; + public static final int UseWidgetCreateGadgetRsp = 4279; + public static final int UseWidgetRetractGadgetReq = 4290; + public static final int UseWidgetRetractGadgetRsp = 4271; + public static final int VehicleInteractReq = 838; + public static final int VehicleInteractRsp = 806; + public static final int VehicleStaminaNotify = 811; + public static final int ViewCodexReq = 4203; + public static final int ViewCodexRsp = 4202; + public static final int WatcherAllDataNotify = 2209; + public static final int WatcherChangeNotify = 2231; + public static final int WatcherEventNotify = 2241; + public static final int WatcherEventTypeNotify = 2237; + public static final int WaterSpritePhaseFinishNotify = 2028; + public static final int WeaponAwakenReq = 642; public static final int WeaponAwakenRsp = 601; - public static final int WeaponPromoteReq = 626; - public static final int WeaponPromoteRsp = 662; - public static final int WeaponUpgradeReq = 656; - public static final int WeaponUpgradeRsp = 683; - public static final int WearEquipReq = 688; - public static final int WearEquipRsp = 628; - public static final int WidgetActiveChangeNotify = 4273; - public static final int WidgetCoolDownNotify = 4277; - public static final int WidgetDoBagReq = 4269; - public static final int WidgetDoBagRsp = 4270; - public static final int WidgetGadgetAllDataNotify = 4260; - public static final int WidgetGadgetDataNotify = 4268; - public static final int WidgetGadgetDestroyNotify = 4282; + public static final int WeaponPromoteReq = 624; + public static final int WeaponPromoteRsp = 638; + public static final int WeaponUpgradeReq = 686; + public static final int WeaponUpgradeRsp = 663; + public static final int WearEquipReq = 630; + public static final int WearEquipRsp = 697; + public static final int WidgetActiveChangeNotify = 4295; + public static final int WidgetCoolDownNotify = 4263; + public static final int WidgetDoBagReq = 4284; + public static final int WidgetDoBagRsp = 4286; + public static final int WidgetGadgetAllDataNotify = 4285; + public static final int WidgetGadgetDataNotify = 4292; + public static final int WidgetGadgetDestroyNotify = 4275; public static final int WidgetReportReq = 4287; - public static final int WidgetReportRsp = 4256; - public static final int WidgetSlotChangeNotify = 4299; - public static final int WidgetUseAttachAbilityGroupChangeNotify = 4257; - public static final int WindSeedClientNotify = 1134; - public static final int WorktopOptionNotify = 815; - public static final int WorldAllRoutineTypeNotify = 3525; - public static final int WorldDataNotify = 3330; - public static final int WorldOwnerBlossomBriefInfoNotify = 2715; - public static final int WorldOwnerBlossomScheduleInfoNotify = 2737; - public static final int WorldOwnerDailyTaskNotify = 130; - public static final int WorldPlayerDieNotify = 211; - public static final int WorldPlayerInfoNotify = 3088; - public static final int WorldPlayerLocationNotify = 224; - public static final int WorldPlayerReviveReq = 216; - public static final int WorldPlayerReviveRsp = 222; - public static final int WorldPlayerRTTNotify = 26; - public static final int WorldRoutineChangeNotify = 3548; - public static final int WorldRoutineTypeCloseNotify = 3513; - public static final int WorldRoutineTypeRefreshNotify = 3545; + public static final int WidgetReportRsp = 4282; + public static final int WidgetSlotChangeNotify = 4274; + public static final int WidgetUseAttachAbilityGroupChangeNotify = 4299; + public static final int WindSeedClientNotify = 1182; + public static final int WorktopOptionNotify = 837; + public static final int WorldAllRoutineTypeNotify = 3509; + public static final int WorldDataNotify = 3131; + public static final int WorldOwnerBlossomBriefInfoNotify = 2737; + public static final int WorldOwnerBlossomScheduleInfoNotify = 2732; + public static final int WorldOwnerDailyTaskNotify = 102; + public static final int WorldPlayerDieNotify = 296; + public static final int WorldPlayerInfoNotify = 3304; + public static final int WorldPlayerLocationNotify = 283; + public static final int WorldPlayerReviveReq = 247; + public static final int WorldPlayerReviveRsp = 280; + public static final int WorldPlayerRTTNotify = 24; + public static final int WorldRoutineChangeNotify = 3504; + public static final int WorldRoutineTypeCloseNotify = 3528; + public static final int WorldRoutineTypeRefreshNotify = 3543; // Unknown - public static final int UNKNOWN = 2013; - public static final int UNKNOWN_1 = 2042; - public static final int UNKNOWN_2 = 343; - public static final int UNKNOWN_3 = 4259; - public static final int UNKNOWN_4 = 4261; - public static final int UNKNOWN_5 = 4306; - public static final int UNKNOWN_6 = 4315; - public static final int UNKNOWN_7 = 4317; - public static final int UNKNOWN_8 = 4326; - public static final int UNKNOWN_9 = 4327; - public static final int UNKNOWN_10 = 4336; - public static final int UNKNOWN_11 = 4337; - public static final int UNKNOWN_12 = 4343; - public static final int UNKNOWN_13 = 5932; - public static final int UNKNOWN_14 = 5959; - public static final int UNKNOWN_15 = 67; - public static final int UNKNOWN_16 = 8010; - public static final int UNKNOWN_17 = 8024; - public static final int UNKNOWN_18 = 8050; - public static final int UNKNOWN_19 = 8077; - public static final int UNKNOWN_20 = 8083; - public static final int UNKNOWN_21 = 8114; - public static final int UNKNOWN_22 = 8154; - public static final int UNKNOWN_23 = 8157; - public static final int UNKNOWN_24 = 8188; - public static final int UNKNOWN_25 = 8190; - public static final int UNKNOWN_26 = 8210; - public static final int UNKNOWN_27 = 8385; - public static final int UNKNOWN_28 = 8405; - public static final int UNKNOWN_29 = 8420; - public static final int UNKNOWN_30 = 8421; - public static final int UNKNOWN_31 = 8429; - public static final int UNKNOWN_32 = 8437; - public static final int UNKNOWN_33 = 8439; - public static final int UNKNOWN_34 = 8461; - public static final int UNKNOWN_35 = 8502; - public static final int UNKNOWN_36 = 8559; - public static final int UNKNOWN_37 = 8580; - public static final int UNKNOWN_38 = 8604; - public static final int UNKNOWN_39 = 8710; - public static final int UNKNOWN_40 = 8761; - public static final int UNKNOWN_41 = 8787; - public static final int UNKNOWN_42 = 8830; - public static final int UNKNOWN_43 = 8877; - public static final int UNKNOWN_44 = 8983; - public static final int UNKNOWN_45 = 943; - public static final List BANNED_PACKETS = Arrays.asList(PacketOpcodes.WindSeedClientNotify, PacketOpcodes.PlayerLuaShellNotify); + public static final HashSet BANNED_PACKETS = new HashSet() {{ + add(PacketOpcodes.WindSeedClientNotify); + add(PacketOpcodes.PlayerLuaShellNotify); + }}; } diff --git a/src/main/java/emu/grasscutter/net/packet/PacketOpcodesUtil.java b/src/main/java/emu/grasscutter/net/packet/PacketOpcodesUtil.java index 6395a7d4d..ff7be1e45 100644 --- a/src/main/java/emu/grasscutter/net/packet/PacketOpcodesUtil.java +++ b/src/main/java/emu/grasscutter/net/packet/PacketOpcodesUtil.java @@ -17,10 +17,12 @@ public class PacketOpcodesUtil { Field[] fields = PacketOpcodes.class.getFields(); for (Field f : fields) { - try { - opcodeMap.put(f.getInt(null), f.getName()); - } catch (Exception e) { - e.printStackTrace(); + if(f.getType().equals(int.class)) { + try { + opcodeMap.put(f.getInt(null), f.getName()); + } catch (Exception e) { + e.printStackTrace(); + } } } } diff --git a/src/main/java/emu/grasscutter/plugin/Plugin.java b/src/main/java/emu/grasscutter/plugin/Plugin.java index 97fc5fd77..f322adc93 100644 --- a/src/main/java/emu/grasscutter/plugin/Plugin.java +++ b/src/main/java/emu/grasscutter/plugin/Plugin.java @@ -3,11 +3,15 @@ package emu.grasscutter.plugin; import emu.grasscutter.Grasscutter; import emu.grasscutter.plugin.api.ServerHook; import emu.grasscutter.server.game.GameServer; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import java.io.File; import java.io.InputStream; import java.net.URLClassLoader; +import static emu.grasscutter.Configuration.*; + /** * The base class for all plugins to extend. */ @@ -17,6 +21,7 @@ public abstract class Plugin { private PluginIdentifier identifier; private URLClassLoader classLoader; private File dataFolder; + private Logger logger; /** * This method is reflected into. @@ -32,7 +37,8 @@ public abstract class Plugin { this.identifier = identifier; this.classLoader = classLoader; - this.dataFolder = new File(Grasscutter.getConfig().PLUGINS_FOLDER, identifier.name); + this.dataFolder = new File(PLUGIN(), identifier.name); + this.logger = LoggerFactory.getLogger(identifier.name); if(!this.dataFolder.exists() && !this.dataFolder.mkdirs()) { Grasscutter.getLogger().warn("Failed to create plugin data folder for " + this.identifier.name); @@ -101,6 +107,14 @@ public abstract class Plugin { public final ServerHook getHandle() { return this.server; } + + /** + * Returns the plugin's logger. + * @return A SLF4J logger. + */ + public final Logger getLogger() { + return this.logger; + } /* Called when the plugin is first loaded. */ public void onLoad() { } diff --git a/src/main/java/emu/grasscutter/plugin/PluginManager.java b/src/main/java/emu/grasscutter/plugin/PluginManager.java index bc78d12eb..f6f1cfbf7 100644 --- a/src/main/java/emu/grasscutter/plugin/PluginManager.java +++ b/src/main/java/emu/grasscutter/plugin/PluginManager.java @@ -7,6 +7,7 @@ import emu.grasscutter.server.event.HandlerPriority; import emu.grasscutter.utils.Utils; import java.io.File; +import java.io.FileNotFoundException; import java.io.InputStreamReader; import java.lang.reflect.Method; import java.net.MalformedURLException; @@ -16,6 +17,8 @@ import java.util.*; import java.util.jar.JarEntry; import java.util.jar.JarFile; +import static emu.grasscutter.Configuration.*; + /** * Manages the server's plugins and the event system. */ @@ -31,8 +34,7 @@ public final class PluginManager { * Loads plugins from the config-specified directory. */ private void loadPlugins() { - String directory = Grasscutter.getConfig().PLUGINS_FOLDER; - File pluginsDir = new File(Utils.toFilePath(directory)); + File pluginsDir = new File(Utils.toFilePath(PLUGIN())); if(!pluginsDir.exists() && !pluginsDir.mkdirs()) { Grasscutter.getLogger().error("Failed to create plugins directory: " + pluginsDir.getAbsolutePath()); return; @@ -89,6 +91,8 @@ public final class PluginManager { fileReader.close(); // Close the file reader. } catch (ClassNotFoundException ignored) { Grasscutter.getLogger().warn("Plugin " + plugin.getName() + " has an invalid main class."); + } catch (FileNotFoundException ignored) { + Grasscutter.getLogger().warn("Plugin " + plugin.getName() + " lacks a valid config file."); } } catch (Exception exception) { Grasscutter.getLogger().error("Failed to load plugin: " + plugin.getName(), exception); diff --git a/src/main/java/emu/grasscutter/plugin/api/PlayerHook.java b/src/main/java/emu/grasscutter/plugin/api/PlayerHook.java index 6b68b7622..0252b860d 100644 --- a/src/main/java/emu/grasscutter/plugin/api/PlayerHook.java +++ b/src/main/java/emu/grasscutter/plugin/api/PlayerHook.java @@ -83,7 +83,7 @@ public final class PlayerHook { public void teleport(Position position) { this.player.getPos().set(position); this.player.sendPacket(new PacketPlayerEnterSceneNotify(this.player, - EnterType.ENTER_JUMP, EnterReason.TransPoint, + EnterType.ENTER_TYPE_JUMP, EnterReason.TransPoint, this.player.getSceneId(), position )); } diff --git a/src/main/java/emu/grasscutter/plugin/api/ServerHook.java b/src/main/java/emu/grasscutter/plugin/api/ServerHook.java index a37abfb62..ffa19110d 100644 --- a/src/main/java/emu/grasscutter/plugin/api/ServerHook.java +++ b/src/main/java/emu/grasscutter/plugin/api/ServerHook.java @@ -1,10 +1,13 @@ package emu.grasscutter.plugin.api; +import emu.grasscutter.Grasscutter; +import emu.grasscutter.auth.AuthenticationSystem; import emu.grasscutter.command.Command; import emu.grasscutter.command.CommandHandler; import emu.grasscutter.game.player.Player; -import emu.grasscutter.server.dispatch.DispatchServer; import emu.grasscutter.server.game.GameServer; +import emu.grasscutter.server.http.HttpServer; +import emu.grasscutter.server.http.Router; import java.util.LinkedList; import java.util.List; @@ -15,7 +18,7 @@ import java.util.List; public final class ServerHook { private static ServerHook instance; private final GameServer gameServer; - private final DispatchServer dispatchServer; + private final HttpServer httpServer; /** * Gets the server hook instance. @@ -28,11 +31,11 @@ public final class ServerHook { /** * Hooks into a server. * @param gameServer The game server to hook into. - * @param dispatchServer The dispatch server to hook into. + * @param httpServer The HTTP server to hook into. */ - public ServerHook(GameServer gameServer, DispatchServer dispatchServer) { + public ServerHook(GameServer gameServer, HttpServer httpServer) { this.gameServer = gameServer; - this.dispatchServer = dispatchServer; + this.httpServer = httpServer; instance = this; } @@ -45,10 +48,10 @@ public final class ServerHook { } /** - * @return The dispatch server. + * @return The HTTP server. */ - public DispatchServer getDispatchServer() { - return this.dispatchServer; + public HttpServer getHttpServer() { + return this.httpServer; } /** @@ -70,4 +73,28 @@ public final class ServerHook { Command commandData = clazz.getAnnotation(Command.class); this.gameServer.getCommandMap().registerCommand(commandData.label(), handler); } + + /** + * Adds a router using an instance of a class. + * @param router A router instance. + */ + public void addRouter(Router router) { + this.addRouter(router.getClass()); + } + + /** + * Adds a router using a class. + * @param router The class of the router. + */ + public void addRouter(Class router) { + this.httpServer.addRouter(router); + } + + /** + * Sets the server's authentication system. + * @param authSystem An instance of the authentication system. + */ + public void setAuthSystem(AuthenticationSystem authSystem) { + Grasscutter.setAuthenticationSystem(authSystem); + } } \ No newline at end of file diff --git a/src/main/java/emu/grasscutter/scripts/SceneScriptManager.java b/src/main/java/emu/grasscutter/scripts/SceneScriptManager.java index 5f6a1b7e6..6ba0098a1 100644 --- a/src/main/java/emu/grasscutter/scripts/SceneScriptManager.java +++ b/src/main/java/emu/grasscutter/scripts/SceneScriptManager.java @@ -1,34 +1,22 @@ package emu.grasscutter.scripts; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.HashMap; -import java.util.HashSet; -import java.util.List; -import java.util.Map; -import java.util.Set; +import java.util.*; import java.util.stream.Collectors; import javax.script.Bindings; import javax.script.CompiledScript; import javax.script.ScriptException; -import org.luaj.vm2.LuaTable; +import emu.grasscutter.scripts.service.ScriptMonsterSpawnService; +import emu.grasscutter.scripts.service.ScriptMonsterTideService; +import org.luaj.vm2.LuaError; import org.luaj.vm2.LuaValue; import org.luaj.vm2.lib.jse.CoerceJavaToLua; import emu.grasscutter.Grasscutter; -import emu.grasscutter.data.GameData; -import emu.grasscutter.data.def.MonsterData; -import emu.grasscutter.data.def.WorldLevelData; import emu.grasscutter.game.entity.EntityGadget; -import emu.grasscutter.game.entity.EntityMonster; -import emu.grasscutter.game.entity.GameEntity; -import emu.grasscutter.game.props.EntityType; import emu.grasscutter.game.world.Scene; import emu.grasscutter.scripts.constants.EventType; -import emu.grasscutter.scripts.constants.ScriptGadgetState; -import emu.grasscutter.scripts.constants.ScriptRegionShape; import emu.grasscutter.scripts.data.SceneBlock; import emu.grasscutter.scripts.data.SceneConfig; import emu.grasscutter.scripts.data.SceneGadget; @@ -43,28 +31,43 @@ import emu.grasscutter.scripts.data.ScriptArgs; import it.unimi.dsi.fastutil.ints.Int2ObjectMap; import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap; +import static emu.grasscutter.Configuration.*; + public class SceneScriptManager { private final Scene scene; private final ScriptLib scriptLib; private final LuaValue scriptLibLua; private final Map variables; - private Bindings bindings; private SceneConfig config; private List blocks; private boolean isInit; - - private final Int2ObjectOpenHashMap> triggers; + /** + * SceneTrigger Set + */ + private final Map triggers; + /** + * current triggers controlled by RefreshGroup + */ + private final Int2ObjectOpenHashMap> currentTriggers; private final Int2ObjectOpenHashMap regions; - + private Map sceneGroups; + private SceneGroup currentGroup; + private ScriptMonsterTideService scriptMonsterTideService; + private ScriptMonsterSpawnService scriptMonsterSpawnService; + public SceneScriptManager(Scene scene) { this.scene = scene; this.scriptLib = new ScriptLib(this); this.scriptLibLua = CoerceJavaToLua.coerce(this.scriptLib); - this.triggers = new Int2ObjectOpenHashMap<>(); + this.triggers = new HashMap<>(); + this.currentTriggers = new Int2ObjectOpenHashMap<>(); + this.regions = new Int2ObjectOpenHashMap<>(); this.variables = new HashMap<>(); - + this.sceneGroups = new HashMap<>(); + this.scriptMonsterSpawnService = new ScriptMonsterSpawnService(this); + // TEMPORARY if (this.getScene().getId() < 10) { return; @@ -94,6 +97,10 @@ public class SceneScriptManager { return config; } + public SceneGroup getCurrentGroup() { + return currentGroup; + } + public List getBlocks() { return blocks; } @@ -103,17 +110,35 @@ public class SceneScriptManager { } public Set getTriggersByEvent(int eventId) { - return triggers.computeIfAbsent(eventId, e -> new HashSet<>()); + return currentTriggers.computeIfAbsent(eventId, e -> new HashSet<>()); } - public void registerTrigger(SceneTrigger trigger) { + this.triggers.put(trigger.name, trigger); getTriggersByEvent(trigger.event).add(trigger); } public void deregisterTrigger(SceneTrigger trigger) { + this.triggers.remove(trigger.name); getTriggersByEvent(trigger.event).remove(trigger); } - + public void resetTriggers(List triggerNames) { + for(var name : triggerNames){ + var instance = triggers.get(name); + this.currentTriggers.get(instance.event).clear(); + this.currentTriggers.get(instance.event).add(instance); + } + } + public void refreshGroup(SceneGroup group, int suiteIndex){ + var suite = group.getSuiteByIndex(suiteIndex); + if(suite == null){ + return; + } + if(suite.triggers.size() > 0){ + resetTriggers(suite.triggers); + } + spawnMonstersInGroup(group, suite); + spawnGadgetsInGroup(group, suite); + } public SceneRegion getRegionById(int id) { return regions.get(id); } @@ -141,7 +166,7 @@ public class SceneScriptManager { private void init() { // Get compiled script if cached CompiledScript cs = ScriptLoader.getScriptByPath( - Grasscutter.getConfig().SCRIPTS_FOLDER + "Scene/" + getScene().getId() + "/scene" + getScene().getId() + "." + ScriptLoader.getScriptType()); + SCRIPT("Scene/" + getScene().getId() + "/scene" + getScene().getId() + "." + ScriptLoader.getScriptType())); if (cs == null) { Grasscutter.getLogger().warn("No script found for scene " + getScene().getId()); @@ -188,7 +213,7 @@ public class SceneScriptManager { private void loadBlockFromScript(SceneBlock block) { CompiledScript cs = ScriptLoader.getScriptByPath( - Grasscutter.getConfig().SCRIPTS_FOLDER + "Scene/" + getScene().getId() + "/scene" + getScene().getId() + "_block" + block.id + "." + ScriptLoader.getScriptType()); + SCRIPT("Scene/" + getScene().getId() + "/scene" + getScene().getId() + "_block" + block.id + "." + ScriptLoader.getScriptType())); if (cs == null) { return; @@ -211,7 +236,7 @@ public class SceneScriptManager { group.setLoaded(true); CompiledScript cs = ScriptLoader.getScriptByPath( - Grasscutter.getConfig().SCRIPTS_FOLDER + "Scene/" + getScene().getId() + "/scene" + getScene().getId() + "_group" + group.id + "." + ScriptLoader.getScriptType()); + SCRIPT("Scene/" + getScene().getId() + "/scene" + getScene().getId() + "_group" + group.id + "." + ScriptLoader.getScriptType())); if (cs == null) { return; @@ -222,7 +247,8 @@ public class SceneScriptManager { cs.eval(getBindings()); // Set - group.monsters = ScriptLoader.getSerializer().toList(SceneMonster.class, bindings.get("monsters")); + group.monsters = ScriptLoader.getSerializer().toList(SceneMonster.class, bindings.get("monsters")).stream() + .collect(Collectors.toMap(x -> x.config_id, y -> y)); group.gadgets = ScriptLoader.getSerializer().toList(SceneGadget.class, bindings.get("gadgets")); group.triggers = ScriptLoader.getSerializer().toList(SceneTrigger.class, bindings.get("triggers")); group.suites = ScriptLoader.getSerializer().toList(SceneSuite.class, bindings.get("suites")); @@ -235,21 +261,21 @@ public class SceneScriptManager { // Add monsters to suite TODO optimize Int2ObjectMap map = new Int2ObjectOpenHashMap<>(); - group.monsters.forEach(m -> map.put(m.config_id, m)); + group.monsters.entrySet().forEach(m -> map.put(m.getValue().config_id, m)); group.gadgets.forEach(m -> map.put(m.config_id, m)); for (SceneSuite suite : group.suites) { suite.sceneMonsters = new ArrayList<>(suite.monsters.size()); - for (int id : suite.monsters) { - try { - SceneMonster monster = (SceneMonster) map.get(id); - if (monster != null) { - suite.sceneMonsters.add(monster); + suite.monsters.forEach(id -> { + Object objEntry = map.get(id.intValue()); + if (objEntry instanceof Map.Entry monsterEntry) { + Object monster = monsterEntry.getValue(); + if(monster instanceof SceneMonster sceneMonster){ + suite.sceneMonsters.add(sceneMonster); } - } catch (Exception e) { - continue; } - } + }); + suite.sceneGadgets = new ArrayList<>(suite.gadgets.size()); for (int id : suite.gadgets) { try { @@ -257,11 +283,10 @@ public class SceneScriptManager { if (gadget != null) { suite.sceneGadgets.add(gadget); } - } catch (Exception e) { - continue; - } + } catch (Exception ignored) { } } } + this.sceneGroups.put(group.id, group); } catch (ScriptException e) { Grasscutter.getLogger().error("Error loading group " + group.id + " in scene " + getScene().getId(), e); } @@ -321,62 +346,43 @@ public class SceneScriptManager { this.callEvent(EventType.EVENT_GADGET_CREATE, new ScriptArgs(entity.getConfigId())); } } - + public void spawnMonstersInGroup(SceneGroup group, int suiteIndex) { - spawnMonstersInGroup(group, group.getSuiteByIndex(suiteIndex)); + var suite = group.getSuiteByIndex(suiteIndex); + if(suite == null){ + return; + } + spawnMonstersInGroup(group, suite); + } + public void spawnMonstersInGroup(SceneGroup group, SceneSuite suite) { + if(suite == null || suite.sceneMonsters.size() <= 0){ + return; + } + this.currentGroup = group; + suite.sceneMonsters.forEach(mob -> this.scriptMonsterSpawnService.spawnMonster(group.id, mob)); } public void spawnMonstersInGroup(SceneGroup group) { - spawnMonstersInGroup(group, null); + this.currentGroup = group; + group.monsters.values().forEach(mob -> this.scriptMonsterSpawnService.spawnMonster(group.id, mob)); } - - public void spawnMonstersInGroup(SceneGroup group, SceneSuite suite) { - List monsters = group.monsters; - - if (suite != null) { - monsters = suite.sceneMonsters; - } - List toAdd = new ArrayList<>(); - - for (SceneMonster monster : monsters) { - MonsterData data = GameData.getMonsterDataMap().get(monster.monster_id); - - if (data == null) { - continue; - } - - // Calculate level - int level = monster.level; - - if (getScene().getDungeonData() != null) { - level = getScene().getDungeonData().getShowLevel(); - } else if (getScene().getWorld().getWorldLevel() > 0) { - WorldLevelData worldLevelData = GameData.getWorldLevelDataMap().get(getScene().getWorld().getWorldLevel()); - - if (worldLevelData != null) { - level = worldLevelData.getMonsterLevel(); - } - } - - // Spawn mob - EntityMonster entity = new EntityMonster(getScene(), data, monster.pos, level); - entity.getRotation().set(monster.rot); - entity.setGroupId(group.id); - entity.setConfigId(monster.config_id); - - toAdd.add(entity); - } - - if (toAdd.size() > 0) { - getScene().addEntities(toAdd); - - for (GameEntity entity : toAdd) { - callEvent(EventType.EVENT_ANY_MONSTER_LIVE, new ScriptArgs(entity.getConfigId())); - } - } + public void startMonsterTideInGroup(SceneGroup group, Integer[] ordersConfigId, int tideCount, int sceneLimit) { + this.currentGroup = group; + this.scriptMonsterTideService = + new ScriptMonsterTideService(this, group, tideCount, sceneLimit, ordersConfigId); + + } + public void unloadCurrentMonsterTide(){ + if(this.getScriptMonsterTideService() == null){ + return; + } + this.getScriptMonsterTideService().unload(); + } + public void spawnMonstersByConfigId(int configId, int delayTime) { + // TODO delay + this.scriptMonsterSpawnService.spawnMonster(this.currentGroup.id, this.currentGroup.monsters.get(configId)); } - // Events public void callEvent(int eventType, ScriptArgs params) { @@ -395,14 +401,39 @@ public class SceneScriptManager { if (params != null) { args = CoerceJavaToLua.coerce(params); } - - ret = condition.call(this.getScriptLibLua(), args); + + ScriptLib.logger.trace("Call Condition Trigger {}", trigger); + ret = safetyCall(trigger.condition, condition, args); } - if (ret.checkboolean() == true) { + if (ret.isboolean() && ret.checkboolean()) { + ScriptLib.logger.trace("Call Action Trigger {}", trigger); LuaValue action = (LuaValue) this.getBindings().get(trigger.action); - action.call(this.getScriptLibLua(), LuaValue.NIL); + // TODO impl the param of SetGroupVariableValueByGroup + var arg = new ScriptArgs(); + arg.param2 = 100; + var args = CoerceJavaToLua.coerce(arg); + safetyCall(trigger.action, action, args); } + //TODO some ret may not bool } } + + public LuaValue safetyCall(String name, LuaValue func, LuaValue args){ + try{ + return func.call(this.getScriptLibLua(), args); + }catch (LuaError error){ + ScriptLib.logger.error("[LUA] call trigger failed {},{}",name,args,error); + return LuaValue.valueOf(-1); + } + } + + public ScriptMonsterTideService getScriptMonsterTideService() { + return scriptMonsterTideService; + } + + public ScriptMonsterSpawnService getScriptMonsterSpawnService() { + return scriptMonsterSpawnService; + } + } diff --git a/src/main/java/emu/grasscutter/scripts/ScriptLib.java b/src/main/java/emu/grasscutter/scripts/ScriptLib.java index 941b00b60..b7fb5939f 100644 --- a/src/main/java/emu/grasscutter/scripts/ScriptLib.java +++ b/src/main/java/emu/grasscutter/scripts/ScriptLib.java @@ -1,28 +1,24 @@ package emu.grasscutter.scripts; -import java.util.ArrayList; -import java.util.List; -import java.util.Optional; - -import org.luaj.vm2.LuaTable; -import org.luaj.vm2.LuaValue; - -import emu.grasscutter.Grasscutter; -import emu.grasscutter.data.GameData; -import emu.grasscutter.data.def.MonsterData; import emu.grasscutter.game.dungeons.DungeonChallenge; import emu.grasscutter.game.entity.EntityGadget; import emu.grasscutter.game.entity.EntityMonster; import emu.grasscutter.game.entity.GameEntity; -import emu.grasscutter.scripts.constants.EventType; import emu.grasscutter.scripts.data.SceneGroup; -import emu.grasscutter.scripts.data.SceneMonster; import emu.grasscutter.scripts.data.SceneRegion; -import emu.grasscutter.scripts.data.ScriptArgs; +import emu.grasscutter.server.packet.send.PacketCanUseSkillNotify; import emu.grasscutter.server.packet.send.PacketGadgetStateNotify; import emu.grasscutter.server.packet.send.PacketWorktopOptionNotify; +import org.luaj.vm2.LuaTable; +import org.luaj.vm2.LuaValue; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.List; +import java.util.Optional; public class ScriptLib { + public static final Logger logger = LoggerFactory.getLogger(ScriptLib.class); private final SceneScriptManager sceneScriptManager; public ScriptLib(SceneScriptManager sceneScriptManager) { @@ -32,8 +28,20 @@ public class ScriptLib { public SceneScriptManager getSceneScriptManager() { return sceneScriptManager; } - + + private String printTable(LuaTable table){ + StringBuilder sb = new StringBuilder(); + sb.append("{"); + for(var meta : table.keys()){ + sb.append(meta).append(":").append(table.get(meta)).append(","); + } + sb.append("}"); + return sb.toString(); + } + public int SetGadgetStateByConfigId(int configId, int gadgetState) { + logger.debug("[LUA] Call SetGadgetStateByConfigId with {},{}", + configId,gadgetState); Optional entity = getSceneScriptManager().getScene().getEntities().values().stream() .filter(e -> e.getConfigId() == configId).findFirst(); @@ -53,6 +61,8 @@ public class ScriptLib { } public int SetGroupGadgetStateByConfigId(int groupId, int configId, int gadgetState) { + logger.debug("[LUA] Call SetGroupGadgetStateByConfigId with {},{},{}", + groupId,configId,gadgetState); List list = getSceneScriptManager().getScene().getEntities().values().stream() .filter(e -> e.getGroupId() == groupId).toList(); @@ -71,6 +81,8 @@ public class ScriptLib { } public int SetWorktopOptionsByGroupId(int groupId, int configId, int[] options) { + logger.debug("[LUA] Call SetWorktopOptionsByGroupId with {},{},{}", + groupId,configId,options); Optional entity = getSceneScriptManager().getScene().getEntities().values().stream() .filter(e -> e.getConfigId() == configId && e.getGroupId() == groupId).findFirst(); @@ -90,6 +102,8 @@ public class ScriptLib { } public int DelWorktopOptionByGroupId(int groupId, int configId, int option) { + logger.debug("[LUA] Call DelWorktopOptionByGroupId with {},{},{}",groupId,configId,option); + Optional entity = getSceneScriptManager().getScene().getEntities().values().stream() .filter(e -> e.getConfigId() == configId && e.getGroupId() == groupId).findFirst(); @@ -109,72 +123,110 @@ public class ScriptLib { } // Some fields are guessed - public int AutoMonsterTide(int challengeIndex, int groupId, int[] config_ids, int param4, int param5, int param6) { + public int AutoMonsterTide(int challengeIndex, int groupId, Integer[] ordersConfigId, int tideCount, int sceneLimit, int param6) { + logger.debug("[LUA] Call AutoMonsterTide with {},{},{},{},{},{}", + challengeIndex,groupId,ordersConfigId,tideCount,sceneLimit,param6); + SceneGroup group = getSceneScriptManager().getGroupById(groupId); - + if (group == null || group.monsters == null) { return 1; } - - // TODO just spawn all from group for now - this.getSceneScriptManager().spawnMonstersInGroup(group); + + this.getSceneScriptManager().startMonsterTideInGroup(group, ordersConfigId, tideCount, sceneLimit); return 0; } public int AddExtraGroupSuite(int groupId, int suite) { + logger.debug("[LUA] Call AddExtraGroupSuite with {},{}", + groupId,suite); SceneGroup group = getSceneScriptManager().getGroupById(groupId); if (group == null || group.monsters == null) { return 1; } - - // TODO just spawn all from group for now + + // avoid spawn wrong monster + if(getSceneScriptManager().getScene().getChallenge() != null) + if(!getSceneScriptManager().getScene().getChallenge().inProgress() || + getSceneScriptManager().getScene().getChallenge().getGroup().id != groupId){ + return 0; + } this.getSceneScriptManager().spawnMonstersInGroup(group, suite); return 0; } // param3 (probably time limit for timed dungeons) - public int ActiveChallenge(int challengeId, int challengeIndex, int param3, int groupId, int objectiveKills, int param5) { + public int ActiveChallenge(int challengeId, int challengeIndex, int timeLimitOrGroupId, int groupId, int objectiveKills, int param5) { + logger.debug("[LUA] Call ActiveChallenge with {},{},{},{},{},{}", + challengeId,challengeIndex,timeLimitOrGroupId,groupId,objectiveKills,param5); + SceneGroup group = getSceneScriptManager().getGroupById(groupId); + var objective = objectiveKills; + + if(group == null){ + group = getSceneScriptManager().getGroupById(timeLimitOrGroupId); + objective = groupId; + } if (group == null || group.monsters == null) { return 1; } - - DungeonChallenge challenge = new DungeonChallenge(getSceneScriptManager().getScene(), group); - challenge.setChallengeId(challengeId); - challenge.setChallengeIndex(challengeIndex); - challenge.setObjective(objectiveKills); - + + if(getSceneScriptManager().getScene().getChallenge() != null && + getSceneScriptManager().getScene().getChallenge().inProgress()) + { + return 0; + } + + DungeonChallenge challenge = new DungeonChallenge(getSceneScriptManager().getScene(), + group, challengeId, challengeIndex, objective); + // set if tower first stage (6-1) + challenge.setStage(getSceneScriptManager().getVariables().getOrDefault("stage", -1) == 0); + getSceneScriptManager().getScene().setChallenge(challenge); - + challenge.start(); return 0; } public int GetGroupMonsterCountByGroupId(int groupId) { + logger.debug("[LUA] Call GetGroupMonsterCountByGroupId with {}", + groupId); return (int) getSceneScriptManager().getScene().getEntities().values().stream() .filter(e -> e instanceof EntityMonster && e.getGroupId() == groupId) .count(); } public int GetGroupVariableValue(String var) { + logger.debug("[LUA] Call GetGroupVariableValue with {}", + var); return getSceneScriptManager().getVariables().getOrDefault(var, 0); } public int SetGroupVariableValue(String var, int value) { + logger.debug("[LUA] Call SetGroupVariableValue with {},{}", + var, value); getSceneScriptManager().getVariables().put(var, value); return 0; } public LuaValue ChangeGroupVariableValue(String var, int value) { + logger.debug("[LUA] Call ChangeGroupVariableValue with {},{}", + var, value); + getSceneScriptManager().getVariables().put(var, getSceneScriptManager().getVariables().get(var) + value); return LuaValue.ZERO; } - + + /** + * Set the actions and triggers to designated group + */ public int RefreshGroup(LuaTable table) { + logger.debug("[LUA] Call RefreshGroup with {}", + printTable(table)); // Kill and Respawn? int groupId = table.get("group_id").toint(); int suite = table.get("suite").toint(); @@ -185,13 +237,14 @@ public class ScriptLib { return 1; } - this.getSceneScriptManager().spawnMonstersInGroup(group, suite); - this.getSceneScriptManager().spawnGadgetsInGroup(group, suite); + getSceneScriptManager().refreshGroup(group, suite); return 0; } public int GetRegionEntityCount(LuaTable table) { + logger.debug("[LUA] Call GetRegionEntityCount with {}", + table); int regionId = table.get("region_eid").toint(); int entityType = table.get("entity_type").toint(); @@ -205,21 +258,103 @@ public class ScriptLib { } public void PrintContextLog(String msg) { - Grasscutter.getLogger().info("[LUA] " + msg); + logger.info("[LUA] " + msg); } - public int TowerCountTimeStatus(int var1, int var2){ + public int TowerCountTimeStatus(int isDone, int var2){ + logger.debug("[LUA] Call TowerCountTimeStatus with {},{}", + isDone,var2); + // TODO record time return 0; } public int GetGroupMonsterCount(int var1){ - // Maybe... - return GetGroupMonsterCountByGroupId(var1); + logger.debug("[LUA] Call GetGroupMonsterCount with {}", + var1); + + return (int) getSceneScriptManager().getScene().getEntities().values().stream() + .filter(e -> e instanceof EntityMonster && e.getGroupId() == getSceneScriptManager().getCurrentGroup().id) + .count(); } public int SetMonsterBattleByGroup(int var1, int var2, int var3){ + logger.debug("[LUA] Call SetMonsterBattleByGroup with {},{},{}", + var1,var2,var3); + // TODO return 0; } public int CauseDungeonFail(int var1){ + logger.debug("[LUA] Call CauseDungeonFail with {}", + var1); + + return 0; + } + + public int GetGroupVariableValueByGroup(String name, int groupId){ + logger.debug("[LUA] Call GetGroupVariableValueByGroup with {},{}", + name,groupId); + + return getSceneScriptManager().getVariables().getOrDefault(name, 0); + } + + public int SetIsAllowUseSkill(int canUse, int var2){ + logger.debug("[LUA] Call SetIsAllowUseSkill with {},{}", + canUse,var2); + + getSceneScriptManager().getScene().broadcastPacket(new PacketCanUseSkillNotify(canUse == 1)); + return 0; + } + + public int KillEntityByConfigId(LuaTable table){ + logger.debug("[LUA] Call KillEntityByConfigId with {}", + printTable(table)); + var configId = table.get("config_id"); + if(configId == LuaValue.NIL){ + return 1; + } + + var entity = getSceneScriptManager().getScene().getEntityByConfigId(configId.toint()); + if(entity == null){ + return 1; + } + getSceneScriptManager().getScene().killEntity(entity, 0); + return 0; + } + + public int SetGroupVariableValueByGroup(String key, int value, int groupId){ + logger.debug("[LUA] Call SetGroupVariableValueByGroup with {},{},{}", + key,value,groupId); + + getSceneScriptManager().getVariables().put(key, value); + return 0; + } + + public int CreateMonster(LuaTable table){ + logger.debug("[LUA] Call CreateMonster with {}", + printTable(table)); + var configId = table.get("config_id").toint(); + var delayTime = table.get("delay_time").toint(); + + getSceneScriptManager().spawnMonstersByConfigId(configId, delayTime); + return 0; + } + + public int TowerMirrorTeamSetUp(int team, int var1) { + logger.debug("[LUA] Call TowerMirrorTeamSetUp with {},{}", + team,var1); + + getSceneScriptManager().unloadCurrentMonsterTide(); + getSceneScriptManager().getScene().getPlayers().get(0).getTowerManager().mirrorTeamSetUp(team-1); + + return 0; + } + + public int CreateGadget(LuaTable table){ + logger.debug("[LUA] Call CreateGadget with {}", + printTable(table)); + var configId = table.get("config_id").toint(); + + //TODO + return 0; } } diff --git a/src/main/java/emu/grasscutter/scripts/data/SceneGroup.java b/src/main/java/emu/grasscutter/scripts/data/SceneGroup.java index a13db7b68..690cd3d0d 100644 --- a/src/main/java/emu/grasscutter/scripts/data/SceneGroup.java +++ b/src/main/java/emu/grasscutter/scripts/data/SceneGroup.java @@ -1,17 +1,21 @@ package emu.grasscutter.scripts.data; -import java.util.List; - import emu.grasscutter.utils.Position; +import java.util.List; +import java.util.Map; + public class SceneGroup { public transient int block_id; // Not an actual variable in the scripts but we will keep it here for reference public int id; public int refresh_id; public Position pos; - - public List monsters; + + /** + * ConfigId - Monster + */ + public Map monsters; public List gadgets; public List triggers; public List regions; diff --git a/src/main/java/emu/grasscutter/scripts/data/SceneTrigger.java b/src/main/java/emu/grasscutter/scripts/data/SceneTrigger.java index a1603b1e6..301fdb8e0 100644 --- a/src/main/java/emu/grasscutter/scripts/data/SceneTrigger.java +++ b/src/main/java/emu/grasscutter/scripts/data/SceneTrigger.java @@ -7,4 +7,29 @@ public class SceneTrigger { public String source; public String condition; public String action; + + @Override + public boolean equals(Object obj) { + if(obj instanceof SceneTrigger sceneTrigger){ + return this.name.equals(sceneTrigger.name); + } + return super.equals(obj); + } + + @Override + public int hashCode() { + return name.hashCode(); + } + + @Override + public String toString() { + return "SceneTrigger{" + + "name='" + name + '\'' + + ", config_id=" + config_id + + ", event=" + event + + ", source='" + source + '\'' + + ", condition='" + condition + '\'' + + ", action='" + action + '\'' + + '}'; + } } diff --git a/src/main/java/emu/grasscutter/scripts/listener/ScriptMonsterListener.java b/src/main/java/emu/grasscutter/scripts/listener/ScriptMonsterListener.java new file mode 100644 index 000000000..b3b99fd61 --- /dev/null +++ b/src/main/java/emu/grasscutter/scripts/listener/ScriptMonsterListener.java @@ -0,0 +1,8 @@ +package emu.grasscutter.scripts.listener; + +import emu.grasscutter.game.entity.EntityMonster; + +public interface ScriptMonsterListener { + + void onNotify(EntityMonster sceneMonster); +} diff --git a/src/main/java/emu/grasscutter/scripts/serializer/LuaSerializer.java b/src/main/java/emu/grasscutter/scripts/serializer/LuaSerializer.java index a63328b55..c476b150f 100644 --- a/src/main/java/emu/grasscutter/scripts/serializer/LuaSerializer.java +++ b/src/main/java/emu/grasscutter/scripts/serializer/LuaSerializer.java @@ -70,16 +70,14 @@ public class LuaSerializer implements Serializer { } try { - object = type.getDeclaredConstructor().newInstance(null); + //noinspection ConfusingArgumentToVarargsMethod + object = type.getDeclaredConstructor().newInstance(); LuaValue[] keys = table.keys(); for (LuaValue k : keys) { try { Field field = object.getClass().getDeclaredField(k.checkjstring()); - if (field == null) { - continue; - } - + field.setAccessible(true); LuaValue keyValue = table.get(k); diff --git a/src/main/java/emu/grasscutter/scripts/service/ScriptMonsterSpawnService.java b/src/main/java/emu/grasscutter/scripts/service/ScriptMonsterSpawnService.java new file mode 100644 index 000000000..e8e0d0668 --- /dev/null +++ b/src/main/java/emu/grasscutter/scripts/service/ScriptMonsterSpawnService.java @@ -0,0 +1,78 @@ +package emu.grasscutter.scripts.service; + +import emu.grasscutter.data.GameData; +import emu.grasscutter.data.excels.MonsterData; +import emu.grasscutter.data.excels.WorldLevelData; +import emu.grasscutter.game.entity.EntityMonster; +import emu.grasscutter.scripts.SceneScriptManager; +import emu.grasscutter.scripts.constants.EventType; +import emu.grasscutter.scripts.data.SceneMonster; +import emu.grasscutter.scripts.data.ScriptArgs; +import emu.grasscutter.scripts.listener.ScriptMonsterListener; + +import java.util.ArrayList; +import java.util.List; + +public class ScriptMonsterSpawnService { + + private final SceneScriptManager sceneScriptManager; + private final List onMonsterCreatedListener = new ArrayList<>(); + + private final List onMonsterDeadListener = new ArrayList<>(); + + public ScriptMonsterSpawnService(SceneScriptManager sceneScriptManager){ + this.sceneScriptManager = sceneScriptManager; + } + + public void addMonsterCreatedListener(ScriptMonsterListener scriptMonsterListener){ + onMonsterCreatedListener.add(scriptMonsterListener); + } + public void addMonsterDeadListener(ScriptMonsterListener scriptMonsterListener){ + onMonsterDeadListener.add(scriptMonsterListener); + } + public void removeMonsterCreatedListener(ScriptMonsterListener scriptMonsterListener){ + onMonsterCreatedListener.remove(scriptMonsterListener); + } + public void removeMonsterDeadListener(ScriptMonsterListener scriptMonsterListener){ + onMonsterDeadListener.remove(scriptMonsterListener); + } + public void onMonsterDead(EntityMonster entityMonster){ + onMonsterDeadListener.forEach(l -> l.onNotify(entityMonster)); + } + public void spawnMonster(int groupId, SceneMonster monster) { + if(monster == null){ + return; + } + + MonsterData data = GameData.getMonsterDataMap().get(monster.monster_id); + + if (data == null) { + return; + } + + // Calculate level + int level = monster.level; + + if (sceneScriptManager.getScene().getDungeonData() != null) { + level = sceneScriptManager.getScene().getDungeonData().getShowLevel(); + } else if (sceneScriptManager.getScene().getWorld().getWorldLevel() > 0) { + WorldLevelData worldLevelData = GameData.getWorldLevelDataMap().get(sceneScriptManager.getScene().getWorld().getWorldLevel()); + + if (worldLevelData != null) { + level = worldLevelData.getMonsterLevel(); + } + } + + // Spawn mob + EntityMonster entity = new EntityMonster(sceneScriptManager.getScene(), data, monster.pos, level); + entity.getRotation().set(monster.rot); + entity.setGroupId(groupId); + entity.setConfigId(monster.config_id); + + onMonsterCreatedListener.forEach(action -> action.onNotify(entity)); + + sceneScriptManager.getScene().addEntity(entity); + + sceneScriptManager.callEvent(EventType.EVENT_ANY_MONSTER_LIVE, new ScriptArgs(entity.getConfigId())); + } +} diff --git a/src/main/java/emu/grasscutter/scripts/service/ScriptMonsterTideService.java b/src/main/java/emu/grasscutter/scripts/service/ScriptMonsterTideService.java new file mode 100644 index 000000000..57d4735ba --- /dev/null +++ b/src/main/java/emu/grasscutter/scripts/service/ScriptMonsterTideService.java @@ -0,0 +1,89 @@ +package emu.grasscutter.scripts.service; + +import emu.grasscutter.game.entity.EntityMonster; +import emu.grasscutter.scripts.SceneScriptManager; +import emu.grasscutter.scripts.constants.EventType; +import emu.grasscutter.scripts.data.SceneGroup; +import emu.grasscutter.scripts.data.SceneMonster; +import emu.grasscutter.scripts.data.ScriptArgs; +import emu.grasscutter.scripts.listener.ScriptMonsterListener; + +import java.util.List; +import java.util.concurrent.ConcurrentLinkedQueue; +import java.util.concurrent.atomic.AtomicInteger; + +public class ScriptMonsterTideService { + private final SceneScriptManager sceneScriptManager; + private final SceneGroup currentGroup; + private final AtomicInteger monsterAlive; + private final AtomicInteger monsterTideCount; + private final AtomicInteger monsterKillCount; + private final int monsterSceneLimit; + private final ConcurrentLinkedQueue monsterConfigOrders; + private final OnMonsterCreated onMonsterCreated= new OnMonsterCreated(); + private final OnMonsterDead onMonsterDead= new OnMonsterDead(); + + public ScriptMonsterTideService(SceneScriptManager sceneScriptManager, + SceneGroup group, int tideCount, int monsterSceneLimit, Integer[] ordersConfigId){ + this.sceneScriptManager = sceneScriptManager; + this.currentGroup = group; + this.monsterSceneLimit = monsterSceneLimit; + this.monsterTideCount = new AtomicInteger(tideCount); + this.monsterKillCount = new AtomicInteger(0); + this.monsterAlive = new AtomicInteger(0); + this.monsterConfigOrders = new ConcurrentLinkedQueue<>(List.of(ordersConfigId)); + + this.sceneScriptManager.getScriptMonsterSpawnService().addMonsterCreatedListener(onMonsterCreated); + this.sceneScriptManager.getScriptMonsterSpawnService().addMonsterDeadListener(onMonsterDead); + // spawn the first turn + for (int i = 0; i < this.monsterSceneLimit; i++) { + this.sceneScriptManager.getScriptMonsterSpawnService().spawnMonster(group.id, getNextMonster()); + } + } + + public class OnMonsterCreated implements ScriptMonsterListener{ + @Override + public void onNotify(EntityMonster sceneMonster) { + if(monsterSceneLimit > 0){ + monsterAlive.incrementAndGet(); + monsterTideCount.decrementAndGet(); + } + } + } + + public SceneMonster getNextMonster(){ + var nextId = this.monsterConfigOrders.poll(); + if(currentGroup.monsters.containsKey(nextId)){ + return currentGroup.monsters.get(nextId); + } + // TODO some monster config_id do not exist in groups, so temporarily set it to the first + return currentGroup.monsters.values().stream().findFirst().orElse(null); + } + + public class OnMonsterDead implements ScriptMonsterListener { + @Override + public void onNotify(EntityMonster sceneMonster) { + if (monsterSceneLimit <= 0) { + return; + } + if (monsterAlive.decrementAndGet() >= monsterSceneLimit) { + // maybe not happen + return; + } + monsterKillCount.incrementAndGet(); + if (monsterTideCount.get() > 0) { + // add more + sceneScriptManager.getScriptMonsterSpawnService().spawnMonster(currentGroup.id, getNextMonster()); + } + // spawn the last turn of monsters + // fix the 5-2 + sceneScriptManager.callEvent(EventType.EVENT_MONSTER_TIDE_DIE, new ScriptArgs(monsterKillCount.get())); + } + + } + + public void unload(){ + this.sceneScriptManager.getScriptMonsterSpawnService().removeMonsterCreatedListener(onMonsterCreated); + this.sceneScriptManager.getScriptMonsterSpawnService().removeMonsterDeadListener(onMonsterDead); + } +} diff --git a/src/main/java/emu/grasscutter/server/dispatch/AnnouncementHandler.java b/src/main/java/emu/grasscutter/server/dispatch/AnnouncementHandler.java deleted file mode 100644 index 333d8ea21..000000000 --- a/src/main/java/emu/grasscutter/server/dispatch/AnnouncementHandler.java +++ /dev/null @@ -1,35 +0,0 @@ -package emu.grasscutter.server.dispatch; - -import emu.grasscutter.Grasscutter; -import express.http.HttpContextHandler; -import express.http.Request; -import express.http.Response; - -import java.io.File; -import java.io.FileInputStream; -import java.io.IOException; -import java.util.Objects; - -public final class AnnouncementHandler implements HttpContextHandler { - @Override - public void handle(Request request, Response response) throws IOException {//event - if (Objects.equals(request.baseUrl(), "/common/hk4e_global/announcement/api/getAnnContent")) { - response.send("{\"retcode\":0,\"message\":\"OK\",\"data\":" + readToString(new File(Grasscutter.getConfig().DATA_FOLDER + "GameAnnouncement.json")) +"}"); - } else if (Objects.equals(request.baseUrl(), "/common/hk4e_global/announcement/api/getAnnList")) { - String data = readToString(new File(Grasscutter.getConfig().DATA_FOLDER + "GameAnnouncementList.json")).replace("System.currentTimeMillis()",String.valueOf(System.currentTimeMillis())); - response.send("{\"retcode\":0,\"message\":\"OK\",\"data\": "+data +"}"); - } - } - private static String readToString(File file) { - Long filelength = file.length(); - byte[] filecontent = new byte[filelength.intValue()]; - try { - FileInputStream in = new FileInputStream(file); - in.read(filecontent); - in.close(); - } catch (IOException fileNotFoundException) { - fileNotFoundException.printStackTrace(); - } - return new String(filecontent); - } -} \ No newline at end of file diff --git a/src/main/java/emu/grasscutter/server/dispatch/ClientLogHandler.java b/src/main/java/emu/grasscutter/server/dispatch/ClientLogHandler.java deleted file mode 100644 index b3d48dbbb..000000000 --- a/src/main/java/emu/grasscutter/server/dispatch/ClientLogHandler.java +++ /dev/null @@ -1,19 +0,0 @@ -package emu.grasscutter.server.dispatch; - -import express.http.HttpContextHandler; -import express.http.Request; -import express.http.Response; - -import java.io.IOException; - -/** - * Used for processing crash dumps and logs generated by the game. - * Logs are in JSON, and are sent to the server for logging. - */ -public final class ClientLogHandler implements HttpContextHandler { - @Override - public void handle(Request request, Response response) throws IOException { - // TODO: Figure out how to dump request body and log to file. - response.send("{\"code\":0}"); - } -} diff --git a/src/main/java/emu/grasscutter/server/dispatch/DispatchServer.java b/src/main/java/emu/grasscutter/server/dispatch/DispatchServer.java deleted file mode 100644 index 7e4b1655e..000000000 --- a/src/main/java/emu/grasscutter/server/dispatch/DispatchServer.java +++ /dev/null @@ -1,538 +0,0 @@ -package emu.grasscutter.server.dispatch; - -import com.google.gson.Gson; -import com.google.gson.GsonBuilder; -import com.google.protobuf.ByteString; - -import emu.grasscutter.Config; -import emu.grasscutter.Grasscutter; -import emu.grasscutter.Grasscutter.ServerDebugMode; -import emu.grasscutter.Grasscutter.ServerRunMode; -import emu.grasscutter.database.DatabaseHelper; -import emu.grasscutter.game.Account; -import emu.grasscutter.net.proto.QueryCurrRegionHttpRspOuterClass.QueryCurrRegionHttpRsp; -import emu.grasscutter.net.proto.QueryRegionListHttpRspOuterClass.QueryRegionListHttpRsp; -import emu.grasscutter.net.proto.RegionInfoOuterClass.RegionInfo; -import emu.grasscutter.net.proto.RegionSimpleInfoOuterClass.RegionSimpleInfo; -import emu.grasscutter.server.dispatch.authentication.AuthenticationHandler; -import emu.grasscutter.server.dispatch.authentication.DefaultAuthenticationHandler; -import emu.grasscutter.server.dispatch.http.GachaRecordHandler; -import emu.grasscutter.server.dispatch.json.*; -import emu.grasscutter.server.dispatch.json.ComboTokenReqJson.LoginTokenData; -import emu.grasscutter.server.event.dispatch.QueryAllRegionsEvent; -import emu.grasscutter.server.event.dispatch.QueryCurrentRegionEvent; -import emu.grasscutter.tools.Tools; -import emu.grasscutter.utils.FileUtils; -import emu.grasscutter.utils.Utils; -import express.Express; -import io.javalin.http.staticfiles.Location; -import org.eclipse.jetty.server.Connector; -import org.eclipse.jetty.server.Server; -import org.eclipse.jetty.server.ServerConnector; -import org.eclipse.jetty.util.ssl.SslContextFactory; - -import java.io.*; -import java.net.URLDecoder; -import java.util.*; - -import static emu.grasscutter.utils.Language.translate; - -public final class DispatchServer { - public static String query_region_list = ""; - public static String query_cur_region = ""; - - private final Gson gson; - private final String defaultServerName = "os_usa"; - - public String regionListBase64; - public Map regions; - private AuthenticationHandler authHandler; - private Express httpServer; - - public DispatchServer() { - this.regions = new HashMap<>(); - this.gson = new GsonBuilder().create(); - - this.loadQueries(); - this.initRegion(); - } - - public Express getServer() { - return httpServer; - } - - public void setHttpServer(Express httpServer) { - this.httpServer.stop(); - this.httpServer = httpServer; - this.httpServer.listen(Grasscutter.getConfig().getDispatchOptions().Port); - } - - public Gson getGsonFactory() { - return gson; - } - - public QueryCurrRegionHttpRsp getCurrRegion() { - // Needs to be fixed by having the game servers connect to the dispatch server. - if (Grasscutter.getConfig().RunMode == ServerRunMode.HYBRID) { - return regions.get(defaultServerName).parsedRegionQuery; - } - - Grasscutter.getLogger().warn("[Dispatch] Unsupported run mode for getCurrRegion()"); - return null; - } - - public void loadQueries() { - File file; - - file = new File(Grasscutter.getConfig().DATA_FOLDER + "query_region_list.txt"); - if (file.exists()) { - query_region_list = new String(FileUtils.read(file)); - } else { - Grasscutter.getLogger().warn("[Dispatch] query_region_list not found! Using default region list."); - } - - file = new File(Grasscutter.getConfig().DATA_FOLDER + "query_cur_region.txt"); - if (file.exists()) { - query_cur_region = new String(FileUtils.read(file)); - } else { - Grasscutter.getLogger().warn("[Dispatch] query_cur_region not found! Using default current region."); - } - } - - private void initRegion() { - try { - byte[] decoded = Base64.getDecoder().decode(query_region_list); - QueryRegionListHttpRsp rl = QueryRegionListHttpRsp.parseFrom(decoded); - - byte[] decoded2 = Base64.getDecoder().decode(query_cur_region); - QueryCurrRegionHttpRsp regionQuery = QueryCurrRegionHttpRsp.parseFrom(decoded2); - - List servers = new ArrayList<>(); - List usedNames = new ArrayList<>(); // List to check for potential naming conflicts - if (Grasscutter.getConfig().RunMode == ServerRunMode.HYBRID) { // Automatically add the game server if in - // hybrid mode - RegionSimpleInfo server = RegionSimpleInfo.newBuilder() - .setName("os_usa") - .setTitle(Grasscutter.getConfig().getGameServerOptions().Name) - .setType("DEV_PUBLIC") - .setDispatchUrl( - "http" + (Grasscutter.getConfig().getDispatchOptions().FrontHTTPS ? "s" : "") + "://" - + (Grasscutter.getConfig().getDispatchOptions().PublicIp.isEmpty() - ? Grasscutter.getConfig().getDispatchOptions().Ip - : Grasscutter.getConfig().getDispatchOptions().PublicIp) - + ":" - + (Grasscutter.getConfig().getDispatchOptions().PublicPort != 0 - ? Grasscutter.getConfig().getDispatchOptions().PublicPort - : Grasscutter.getConfig().getDispatchOptions().Port) - + "/query_cur_region/" + defaultServerName) - .build(); - usedNames.add(defaultServerName); - servers.add(server); - - RegionInfo serverRegion = regionQuery.getRegionInfo().toBuilder() - .setGateserverIp((Grasscutter.getConfig().getGameServerOptions().PublicIp.isEmpty() - ? Grasscutter.getConfig().getGameServerOptions().Ip - : Grasscutter.getConfig().getGameServerOptions().PublicIp)) - .setGateserverPort(Grasscutter.getConfig().getGameServerOptions().PublicPort != 0 - ? Grasscutter.getConfig().getGameServerOptions().PublicPort - : Grasscutter.getConfig().getGameServerOptions().Port) - .setSecretKey(ByteString - .copyFrom(FileUtils.read(Grasscutter.getConfig().KEY_FOLDER + "dispatchSeed.bin"))) - .build(); - - QueryCurrRegionHttpRsp parsedRegionQuery = regionQuery.toBuilder().setRegionInfo(serverRegion).build(); - regions.put(defaultServerName, new RegionData(parsedRegionQuery, - Base64.getEncoder().encodeToString(parsedRegionQuery.toByteString().toByteArray()))); - - } else { - if (Grasscutter.getConfig().getDispatchOptions().getGameServers().length == 0) { - Grasscutter.getLogger() - .error("[Dispatch] There are no game servers available. Exiting due to unplayable state."); - System.exit(1); - } - } - - for (Config.DispatchServerOptions.RegionInfo regionInfo : Grasscutter.getConfig().getDispatchOptions() - .getGameServers()) { - if (usedNames.contains(regionInfo.Name)) { - Grasscutter.getLogger().error("Region name already in use."); - continue; - } - RegionSimpleInfo server = RegionSimpleInfo.newBuilder() - .setName(regionInfo.Name) - .setTitle(regionInfo.Title) - .setType("DEV_PUBLIC") - .setDispatchUrl( - "http" + (Grasscutter.getConfig().getDispatchOptions().FrontHTTPS ? "s" : "") + "://" - + (Grasscutter.getConfig().getDispatchOptions().PublicIp.isEmpty() - ? Grasscutter.getConfig().getDispatchOptions().Ip - : Grasscutter.getConfig().getDispatchOptions().PublicIp) - + ":" + (Grasscutter.getConfig().getDispatchOptions().PublicPort != 0 - ? Grasscutter.getConfig().getDispatchOptions().PublicPort - : Grasscutter.getConfig().getDispatchOptions().Port) + "/query_cur_region/" + regionInfo.Name) - .build(); - usedNames.add(regionInfo.Name); - servers.add(server); - - RegionInfo serverRegion = regionQuery.getRegionInfo().toBuilder() - .setGateserverIp(regionInfo.Ip) - .setGateserverPort(regionInfo.Port) - .setSecretKey(ByteString - .copyFrom(FileUtils.read(Grasscutter.getConfig().KEY_FOLDER + "dispatchSeed.bin"))) - .build(); - - QueryCurrRegionHttpRsp parsedRegionQuery = regionQuery.toBuilder().setRegionInfo(serverRegion).build(); - regions.put(regionInfo.Name, new RegionData(parsedRegionQuery, - Base64.getEncoder().encodeToString(parsedRegionQuery.toByteString().toByteArray()))); - } - - QueryRegionListHttpRsp regionList = QueryRegionListHttpRsp.newBuilder() - .addAllRegionList(servers) - .setClientSecretKey(rl.getClientSecretKey()) - .setClientCustomConfigEncrypted(rl.getClientCustomConfigEncrypted()) - .setEnableLoginPc(true) - .build(); - - this.regionListBase64 = Base64.getEncoder().encodeToString(regionList.toByteString().toByteArray()); - } catch (Exception e) { - Grasscutter.getLogger().error("[Dispatch] Error while initializing region info!", e); - } - } - - public void start() throws Exception { - httpServer = new Express(config -> { - config.server(() -> { - Server server = new Server(); - ServerConnector serverConnector; - - if(Grasscutter.getConfig().getDispatchOptions().UseSSL) { - SslContextFactory.Server sslContextFactory = new SslContextFactory.Server(); - File keystoreFile = new File(Grasscutter.getConfig().getDispatchOptions().KeystorePath); - - if(keystoreFile.exists()) { - try { - sslContextFactory.setKeyStorePath(keystoreFile.getPath()); - sslContextFactory.setKeyStorePassword(Grasscutter.getConfig().getDispatchOptions().KeystorePassword); - } catch (Exception e) { - e.printStackTrace(); - Grasscutter.getLogger().warn(translate("messages.dispatch.keystore.password_error")); - - try { - sslContextFactory.setKeyStorePath(keystoreFile.getPath()); - sslContextFactory.setKeyStorePassword("123456"); - Grasscutter.getLogger().warn(translate("messages.dispatch.keystore.default_password")); - } catch (Exception e2) { - Grasscutter.getLogger().warn(translate("messages.dispatch.keystore.general_error")); - e2.printStackTrace(); - } - } - - serverConnector = new ServerConnector(server, sslContextFactory); - } else { - Grasscutter.getLogger().warn(translate("messages.dispatch.keystore.no_keystore_error")); - Grasscutter.getConfig().getDispatchOptions().UseSSL = false; - - serverConnector = new ServerConnector(server); - } - } else { - serverConnector = new ServerConnector(server); - } - - serverConnector.setPort(Grasscutter.getConfig().getDispatchOptions().Port); - server.setConnectors(new Connector[]{serverConnector}); - return server; - }); - - config.enforceSsl = Grasscutter.getConfig().getDispatchOptions().UseSSL; - if(Grasscutter.getConfig().DebugMode == ServerDebugMode.ALL) { - config.enableDevLogging(); - } - if (Grasscutter.getConfig().getDispatchOptions().CORS){ - if (Grasscutter.getConfig().getDispatchOptions().CORSAllowedOrigins.length > 0) config.enableCorsForOrigin(Grasscutter.getConfig().getDispatchOptions().CORSAllowedOrigins); - else config.enableCorsForAllOrigins(); - } - }); - httpServer.get("/", (req, res) -> res.send(translate("messages.status.welcome"))); - - httpServer.raw().error(404, ctx -> { - if(Grasscutter.getConfig().DebugMode == ServerDebugMode.MISSING) { - Grasscutter.getLogger().info(translate("messages.dispatch.unhandled_request_error", ctx.method(), ctx.url())); - } - ctx.contentType("text/html"); - ctx.result(""); // I'm like 70% sure this won't break anything. - }); - - // Authentication Handler - // These routes are so that authentication routes are always the same no matter what auth system is used. - httpServer.get("/authentication/type", (req, res) -> { - res.send(this.getAuthHandler().getClass().getName()); - }); - - httpServer.post("/authentication/login", (req, res) -> this.getAuthHandler().handleLogin(req, res)); - httpServer.post("/authentication/register", (req, res) -> this.getAuthHandler().handleRegister(req, res)); - httpServer.post("/authentication/change_password", (req, res) -> this.getAuthHandler().handleChangePassword(req, res)); - - // Dispatch - httpServer.get("/query_region_list", (req, res) -> { - // Log - Grasscutter.getLogger().info(String.format("[Dispatch] Client %s request: query_region_list", req.ip())); - - // Invoke event. - QueryAllRegionsEvent event = new QueryAllRegionsEvent(regionListBase64); event.call(); - // Respond with event result. - res.send(event.getRegionList()); - }); - - httpServer.get("/query_cur_region/:id", (req, res) -> { - String regionName = req.params("id"); - // Log - Grasscutter.getLogger().info( - String.format("Client %s request: query_cur_region/%s", req.ip(), regionName)); - // Create a response form the request query parameters - String response = "CAESGE5vdCBGb3VuZCB2ZXJzaW9uIGNvbmZpZw=="; - if (req.query().values().size() > 0) { - response = regions.get(regionName).Base64; - } - - // Invoke event. - QueryCurrentRegionEvent event = new QueryCurrentRegionEvent(response); event.call(); - // Respond with event result. - res.send(event.getRegionInfo()); - }); - - // Login - - httpServer.post("/hk4e_global/mdk/shield/api/login", (req, res) -> { - // Get post data - LoginAccountRequestJson requestData = null; - try { - String body = req.ctx().body(); - requestData = getGsonFactory().fromJson(body, LoginAccountRequestJson.class); - } catch (Exception ignored) { } - - // Create response json - if (requestData == null) { - return; - } - Grasscutter.getLogger().info(translate("messages.dispatch.account.login_attempt", req.ip())); - - res.send(this.getAuthHandler().handleGameLogin(req, requestData)); - }); - - // Login via token - httpServer.post("/hk4e_global/mdk/shield/api/verify", (req, res) -> { - // Get post data - LoginTokenRequestJson requestData = null; - try { - String body = req.ctx().body(); - requestData = getGsonFactory().fromJson(body, LoginTokenRequestJson.class); - } catch (Exception ignored) { - } - - // Create response json - if (requestData == null) { - return; - } - LoginResultJson responseData = new LoginResultJson(); - Grasscutter.getLogger().info(translate("messages.dispatch.account.login_token_attempt", req.ip())); - - // Login - Account account = DatabaseHelper.getAccountById(requestData.uid); - - // Test - if (account == null || !account.getSessionKey().equals(requestData.token)) { - responseData.retcode = -111; - responseData.message = translate("messages.dispatch.account.account_cache_error"); - - Grasscutter.getLogger().info(translate("messages.dispatch.account.login_token_error", req.ip())); - } else { - responseData.message = "OK"; - responseData.data.account.uid = requestData.uid; - responseData.data.account.token = requestData.token; - responseData.data.account.email = account.getEmail(); - - Grasscutter.getLogger().info(translate("messages.dispatch.account.login_token_success", req.ip(), requestData.uid)); - } - - res.send(responseData); - }); - - // Exchange for combo token - httpServer.post("/hk4e_global/combo/granter/login/v2/login", (req, res) -> { - // Get post data - ComboTokenReqJson requestData = null; - try { - String body = req.ctx().body(); - requestData = getGsonFactory().fromJson(body, ComboTokenReqJson.class); - } catch (Exception ignored) { - } - - // Create response json - if (requestData == null || requestData.data == null) { - return; - } - LoginTokenData loginData = getGsonFactory().fromJson(requestData.data, LoginTokenData.class); // Get login - // data - ComboTokenResJson responseData = new ComboTokenResJson(); - - // Login - Account account = DatabaseHelper.getAccountById(loginData.uid); - - // Test - if (account == null || !account.getSessionKey().equals(loginData.token)) { - responseData.retcode = -201; - responseData.message = translate("messages.dispatch.account.session_key_error"); - - Grasscutter.getLogger().info(translate("messages.dispatch.account.combo_token_error", req.ip())); - } else { - responseData.message = "OK"; - responseData.data.open_id = loginData.uid; - responseData.data.combo_id = "157795300"; - responseData.data.combo_token = account.generateLoginToken(); - - Grasscutter.getLogger().info(translate("messages.dispatch.account.combo_token_success", req.ip())); - } - - res.send(responseData); - }); - - // TODO: There are some missing route request types here (You can tell if they are missing if they are .all and not anything else) - // When http requests for theses routes are found please remove it from the list in DispatchHttpJsonHandler and update the route request types here - - // Agreement and Protocol - // hk4e-sdk-os.hoyoverse.com - httpServer.get("/hk4e_global/mdk/agreement/api/getAgreementInfos", new DispatchHttpJsonHandler("{\"retcode\":0,\"message\":\"OK\",\"data\":{\"marketing_agreements\":[]}}")); - // hk4e-sdk-os.hoyoverse.com - // this could be either GET or POST based on the observation of different clients - httpServer.all("/hk4e_global/combo/granter/api/compareProtocolVersion", new DispatchHttpJsonHandler("{\"retcode\":0,\"message\":\"OK\",\"data\":{\"modified\":true,\"protocol\":{\"id\":0,\"app_id\":4,\"language\":\"en\",\"user_proto\":\"\",\"priv_proto\":\"\",\"major\":7,\"minimum\":0,\"create_time\":\"0\",\"teenager_proto\":\"\",\"third_proto\":\"\"}}}")); - - // Game data - // hk4e-api-os.hoyoverse.com - httpServer.all("/common/hk4e_global/announcement/api/getAlertPic", new DispatchHttpJsonHandler("{\"retcode\":0,\"message\":\"OK\",\"data\":{\"total\":0,\"list\":[]}}")); - // hk4e-api-os.hoyoverse.com - httpServer.all("/common/hk4e_global/announcement/api/getAlertAnn", new DispatchHttpJsonHandler("{\"retcode\":0,\"message\":\"OK\",\"data\":{\"alert\":false,\"alert_id\":0,\"remind\":true}}")); - // hk4e-api-os.hoyoverse.com - httpServer.all("/common/hk4e_global/announcement/api/getAnnList", new AnnouncementHandler()); - // hk4e-api-os-static.hoyoverse.com - httpServer.all("/common/hk4e_global/announcement/api/getAnnContent", new AnnouncementHandler()); - // hk4e-sdk-os.hoyoverse.com - httpServer.all("/hk4e_global/mdk/shopwindow/shopwindow/listPriceTier", new DispatchHttpJsonHandler("{\"retcode\":0,\"message\":\"OK\",\"data\":{\"suggest_currency\":\"USD\",\"tiers\":[]}}")); - - // Captcha - // api-account-os.hoyoverse.com - httpServer.post("/account/risky/api/check", new DispatchHttpJsonHandler("{\"retcode\":0,\"message\":\"OK\",\"data\":{\"id\":\"none\",\"action\":\"ACTION_NONE\",\"geetest\":null}}")); - - // Config - // sdk-os-static.hoyoverse.com - httpServer.get("/combo/box/api/config/sdk/combo", new DispatchHttpJsonHandler("{\"retcode\":0,\"message\":\"OK\",\"data\":{\"vals\":{\"disable_email_bind_skip\":\"false\",\"email_bind_remind_interval\":\"7\",\"email_bind_remind\":\"true\"}}}")); - // hk4e-sdk-os-static.hoyoverse.com - httpServer.get("/hk4e_global/combo/granter/api/getConfig", new DispatchHttpJsonHandler("{\"retcode\":0,\"message\":\"OK\",\"data\":{\"protocol\":true,\"qr_enabled\":false,\"log_level\":\"INFO\",\"announce_url\":\"https://webstatic-sea.hoyoverse.com/hk4e/announcement/index.html?sdk_presentation_style=fullscreen\\u0026sdk_screen_transparent=true\\u0026game_biz=hk4e_global\\u0026auth_appid=announcement\\u0026game=hk4e#/\",\"push_alias_type\":2,\"disable_ysdk_guard\":false,\"enable_announce_pic_popup\":true}}")); - // hk4e-sdk-os-static.hoyoverse.com - httpServer.get("/hk4e_global/mdk/shield/api/loadConfig", new DispatchHttpJsonHandler("{\"retcode\":0,\"message\":\"OK\",\"data\":{\"id\":6,\"game_key\":\"hk4e_global\",\"client\":\"PC\",\"identity\":\"I_IDENTITY\",\"guest\":false,\"ignore_versions\":\"\",\"scene\":\"S_NORMAL\",\"name\":\"原神海外\",\"disable_regist\":false,\"enable_email_captcha\":false,\"thirdparty\":[\"fb\",\"tw\"],\"disable_mmt\":false,\"server_guest\":false,\"thirdparty_ignore\":{\"tw\":\"\",\"fb\":\"\"},\"enable_ps_bind_account\":false,\"thirdparty_login_configs\":{\"tw\":{\"token_type\":\"TK_GAME_TOKEN\",\"game_token_expires_in\":604800},\"fb\":{\"token_type\":\"TK_GAME_TOKEN\",\"game_token_expires_in\":604800}}}}")); - // Test api? - // abtest-api-data-sg.hoyoverse.com - httpServer.post("/data_abtest_api/config/experiment/list", new DispatchHttpJsonHandler("{\"retcode\":0,\"success\":true,\"message\":\"\",\"data\":[{\"code\":1000,\"type\":2,\"config_id\":\"14\",\"period_id\":\"6036_99\",\"version\":\"1\",\"configs\":{\"cardType\":\"old\"}}]}")); - - // log-upload-os.mihoyo.com - httpServer.all("/log/sdk/upload", new DispatchHttpJsonHandler("{\"code\":0}")); - httpServer.all("/sdk/upload", new DispatchHttpJsonHandler("{\"code\":0}")); - httpServer.post("/sdk/dataUpload", new DispatchHttpJsonHandler("{\"code\":0}")); - // /perf/config/verify?device_id=xxx&platform=x&name=xxx - httpServer.all("/perf/config/verify", new DispatchHttpJsonHandler("{\"code\":0}")); - - // Logging servers - // overseauspider.yuanshen.com - httpServer.all("/log", new ClientLogHandler()); - // log-upload-os.mihoyo.com - httpServer.all("/crash/dataUpload", new ClientLogHandler()); - - // webstatic-sea.hoyoverse.com - httpServer.get("/admin/mi18n/plat_oversea/m202003048/m202003048-version.json", new DispatchHttpJsonHandler("{\"version\":51}")); - - // gacha record. - String gachaMappingsPath = Utils.toFilePath(Grasscutter.getConfig().DATA_FOLDER + "/gacha_mappings.js"); - // TODO: Only serve the html page and have a subsequent request to fetch the gacha data. - httpServer.get("/gacha", new GachaRecordHandler()); - if(!(new File(gachaMappingsPath).exists())) { - Tools.createGachaMapping(gachaMappingsPath); - } - - httpServer.raw().config.addSinglePageRoot("/gacha/mappings", gachaMappingsPath, Location.EXTERNAL); - - // static file support for plugins - httpServer.raw().config.precompressStaticFiles = false; // If this isn't set to false, files such as images may appear corrupted when serving static files - - httpServer.listen(Grasscutter.getConfig().getDispatchOptions().Port); - Grasscutter.getLogger().info(translate("messages.dispatch.port_bind", Integer.toString(httpServer.raw().port()))); - } - - private Map parseQueryString(String qs) { - Map result = new HashMap<>(); - if (qs == null) { - return result; - } - - int last = 0, next, l = qs.length(); - while (last < l) { - next = qs.indexOf('&', last); - if (next == -1) { - next = l; - } - - if (next > last) { - int eqPos = qs.indexOf('=', last); - try { - if (eqPos < 0 || eqPos > next) { - result.put(URLDecoder.decode(qs.substring(last, next), "utf-8"), ""); - } else { - result.put(URLDecoder.decode(qs.substring(last, eqPos), "utf-8"), - URLDecoder.decode(qs.substring(eqPos + 1, next), "utf-8")); - } - } catch (UnsupportedEncodingException e) { - throw new RuntimeException(e); // will never happen, utf-8 support is mandatory for java - } - } - last = next + 1; - } - return result; - } - - public AuthenticationHandler getAuthHandler() { - if(authHandler == null) { - return new DefaultAuthenticationHandler(); - } - return authHandler; - } - - public boolean registerAuthHandler(AuthenticationHandler authHandler) { - if(this.authHandler != null) { - Grasscutter.getLogger().error(String.format("[Dispatch] Unable to register '%s' authentication handler. \n" + - "The '%s' authentication handler has already been registered", authHandler.getClass().getName(), this.authHandler.getClass().getName())); - return false; - } - this.authHandler = authHandler; - return true; - } - - public void resetAuthHandler() { - this.authHandler = null; - } - - public static class RegionData { - QueryCurrRegionHttpRsp parsedRegionQuery; - String Base64; - - public RegionData(QueryCurrRegionHttpRsp prq, String b64) { - this.parsedRegionQuery = prq; - this.Base64 = b64; - } - - public QueryCurrRegionHttpRsp getParsedRegionQuery() { - return parsedRegionQuery; - } - - public String getBase64() { - return Base64; - } - } -} diff --git a/src/main/java/emu/grasscutter/server/dispatch/authentication/AuthenticationHandler.java b/src/main/java/emu/grasscutter/server/dispatch/authentication/AuthenticationHandler.java deleted file mode 100644 index 92a2961ea..000000000 --- a/src/main/java/emu/grasscutter/server/dispatch/authentication/AuthenticationHandler.java +++ /dev/null @@ -1,16 +0,0 @@ -package emu.grasscutter.server.dispatch.authentication; - -import emu.grasscutter.server.dispatch.json.LoginAccountRequestJson; -import emu.grasscutter.server.dispatch.json.LoginResultJson; -import express.http.Request; -import express.http.Response; - -public interface AuthenticationHandler { - - // This is in case plugins also want some sort of authentication - void handleLogin(Request req, Response res); - void handleRegister(Request req, Response res); - void handleChangePassword(Request req, Response res); - - LoginResultJson handleGameLogin(Request req, LoginAccountRequestJson requestData); -} diff --git a/src/main/java/emu/grasscutter/server/dispatch/authentication/DefaultAuthenticationHandler.java b/src/main/java/emu/grasscutter/server/dispatch/authentication/DefaultAuthenticationHandler.java deleted file mode 100644 index e5a4ca055..000000000 --- a/src/main/java/emu/grasscutter/server/dispatch/authentication/DefaultAuthenticationHandler.java +++ /dev/null @@ -1,79 +0,0 @@ -package emu.grasscutter.server.dispatch.authentication; - -import emu.grasscutter.Grasscutter; -import emu.grasscutter.database.DatabaseHelper; -import emu.grasscutter.game.Account; -import emu.grasscutter.server.dispatch.json.LoginAccountRequestJson; -import emu.grasscutter.server.dispatch.json.LoginResultJson; -import express.http.Request; -import express.http.Response; - -import static emu.grasscutter.utils.Language.translate; - -public class DefaultAuthenticationHandler implements AuthenticationHandler { - - @Override - public void handleLogin(Request req, Response res) { - res.send("Authentication is not available with the default authentication method"); - } - - @Override - public void handleRegister(Request req, Response res) { - res.send("Authentication is not available with the default authentication method"); - } - - @Override - public void handleChangePassword(Request req, Response res) { - res.send("Authentication is not available with the default authentication method"); - } - - @Override - public LoginResultJson handleGameLogin(Request req, LoginAccountRequestJson requestData) { - LoginResultJson responseData = new LoginResultJson(); - - // Login - Account account = DatabaseHelper.getAccountByName(requestData.account); - - // Check if account exists, else create a new one. - if (account == null) { - // Account doesn't exist, so we can either auto create it if the config value is set. - if (Grasscutter.getConfig().getDispatchOptions().AutomaticallyCreateAccounts) { - // This account has been created AUTOMATICALLY. There will be no permissions added. - account = DatabaseHelper.createAccountWithId(requestData.account, 0); - - for (String permission : Grasscutter.getConfig().getDispatchOptions().defaultPermissions) { - account.addPermission(permission); - } - - if (account != null) { - responseData.message = "OK"; - responseData.data.account.uid = account.getId(); - responseData.data.account.token = account.generateSessionKey(); - responseData.data.account.email = account.getEmail(); - - Grasscutter.getLogger().info(translate("messages.dispatch.account.account_login_create_success", req.ip(), responseData.data.account.uid)); - } else { - responseData.retcode = -201; - responseData.message = translate("messages.dispatch.account.username_create_error"); - - Grasscutter.getLogger().info(translate("messages.dispatch.account.account_login_create_error", req.ip())); - } - } else { - responseData.retcode = -201; - responseData.message = translate("messages.dispatch.account.username_error"); - - Grasscutter.getLogger().info(translate("messages.dispatch.account.account_login_exist_error", req.ip())); - } - } else { - // Account was found, log the player in - responseData.message = "OK"; - responseData.data.account.uid = account.getId(); - responseData.data.account.token = account.generateSessionKey(); - responseData.data.account.email = account.getEmail(); - - Grasscutter.getLogger().info(translate("messages.dispatch.account.login_success", req.ip(), responseData.data.account.uid)); - } - - return responseData; - } -} diff --git a/src/main/java/emu/grasscutter/server/dispatch/http/GachaRecordHandler.java b/src/main/java/emu/grasscutter/server/dispatch/http/GachaRecordHandler.java deleted file mode 100644 index 8676574bb..000000000 --- a/src/main/java/emu/grasscutter/server/dispatch/http/GachaRecordHandler.java +++ /dev/null @@ -1,53 +0,0 @@ -package emu.grasscutter.server.dispatch.http; - -import java.io.File; -import java.io.IOException; - -import emu.grasscutter.Grasscutter; -import emu.grasscutter.database.DatabaseHelper; -import emu.grasscutter.game.Account; -import emu.grasscutter.utils.FileUtils; -import emu.grasscutter.utils.Utils; -import express.http.HttpContextHandler; -import express.http.Request; -import express.http.Response; - -public final class GachaRecordHandler implements HttpContextHandler { - String render_template; - public GachaRecordHandler() { - File template = new File(Utils.toFilePath(Grasscutter.getConfig().DATA_FOLDER + "/gacha_records.html")); - if (template.exists()) { - // Load from cache - render_template = new String(FileUtils.read(template)); - } else { - render_template = "{{REPLACE_RECORD}}"; - } - } - - @Override - public void handle(Request req, Response res) throws IOException { - // Grasscutter.getLogger().info( req.query().toString() ); - String sessionKey = req.query("s"); - int page = 0; - int gachaType = 0; - if (req.query("p") != null) { - page = Integer.valueOf(req.query("p")); - } - - if (req.query("gachaType") != null) { - gachaType = Integer.valueOf(req.query("gachaType")); - } - - Account account = DatabaseHelper.getAccountBySessionKey(sessionKey); - if (account != null) { - String records = DatabaseHelper.getGachaRecords(account.getPlayerUid(), page, gachaType).toString(); - // Grasscutter.getLogger().info(records); - String response = render_template.replace("{{REPLACE_RECORD}}", records) - .replace("{{REPLACE_MAXPAGE}}", String.valueOf(DatabaseHelper.getGachaRecordsMaxPage(account.getPlayerUid(), page, gachaType))); - - res.send(response); - } else { - res.send("No account found."); - } - } -} diff --git a/src/main/java/emu/grasscutter/server/event/game/CommandResponseEvent.java b/src/main/java/emu/grasscutter/server/event/game/CommandResponseEvent.java new file mode 100644 index 000000000..c3c8fd89a --- /dev/null +++ b/src/main/java/emu/grasscutter/server/event/game/CommandResponseEvent.java @@ -0,0 +1,24 @@ +package emu.grasscutter.server.event.game; + +import emu.grasscutter.game.player.Player; +import emu.grasscutter.server.event.types.GameEvent; +import emu.grasscutter.server.event.types.ServerEvent; + +public class CommandResponseEvent extends ServerEvent { + private String message; + private Player player; + + public CommandResponseEvent(Type type, Player player,String message) { + super(type); + this.message = message; + this.player = player; + } + + public String getMessage() { + return message; + } + + public Player getPlayer() { + return player; + } +} diff --git a/src/main/java/emu/grasscutter/server/event/internal/ServerLogEvent.java b/src/main/java/emu/grasscutter/server/event/internal/ServerLogEvent.java new file mode 100644 index 000000000..5903d4021 --- /dev/null +++ b/src/main/java/emu/grasscutter/server/event/internal/ServerLogEvent.java @@ -0,0 +1,22 @@ +package emu.grasscutter.server.event.internal; + +import ch.qos.logback.classic.spi.ILoggingEvent; +import ch.qos.logback.core.AppenderBase; +import emu.grasscutter.server.event.types.ServerEvent; + +public class ServerLogEvent extends ServerEvent { + ILoggingEvent loggingEvent; + String consoleMessage; + + public ServerLogEvent(Type type, ILoggingEvent loggingEvent, String consoleMessage) { + super(type); + this.loggingEvent = loggingEvent; + this.consoleMessage = consoleMessage; + } + + public ILoggingEvent getLoggingEvent() { return loggingEvent; } + + public String getConsoleMessage() { + return consoleMessage; + } +} diff --git a/src/main/java/emu/grasscutter/server/game/GameServer.java b/src/main/java/emu/grasscutter/server/game/GameServer.java index 7ce8488ef..c0560fcc0 100644 --- a/src/main/java/emu/grasscutter/server/game/GameServer.java +++ b/src/main/java/emu/grasscutter/server/game/GameServer.java @@ -10,11 +10,14 @@ import emu.grasscutter.game.drop.DropManager; import emu.grasscutter.game.dungeons.DungeonManager; import emu.grasscutter.game.expedition.ExpeditionManager; import emu.grasscutter.game.gacha.GachaManager; -import emu.grasscutter.game.managers.ChatManager; +import emu.grasscutter.game.managers.ChatManager.ChatManager; +import emu.grasscutter.game.managers.ChatManager.ChatManagerHandler; import emu.grasscutter.game.managers.InventoryManager; import emu.grasscutter.game.managers.MultiplayerManager; import emu.grasscutter.game.player.Player; +import emu.grasscutter.game.quest.ServerQuestHandler; import emu.grasscutter.game.shop.ShopManager; +import emu.grasscutter.game.tower.TowerScheduleManager; import emu.grasscutter.game.world.World; import emu.grasscutter.net.packet.PacketHandler; import emu.grasscutter.net.proto.SocialDetailOuterClass.SocialDetail; @@ -24,25 +27,25 @@ import emu.grasscutter.server.event.game.ServerTickEvent; import emu.grasscutter.server.event.internal.ServerStartEvent; import emu.grasscutter.server.event.internal.ServerStopEvent; import emu.grasscutter.task.TaskMap; +import emu.grasscutter.BuildConfig; import java.net.InetSocketAddress; import java.time.OffsetDateTime; import java.util.*; import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.Executors; -import java.util.concurrent.ScheduledExecutorService; -import java.util.concurrent.TimeUnit; import static emu.grasscutter.utils.Language.translate; +import static emu.grasscutter.Configuration.*; public final class GameServer extends KcpServer { private final InetSocketAddress address; private final GameServerPacketHandler packetHandler; - + private final ServerQuestHandler questHandler; + private final Map players; private final Set worlds; - private final ChatManager chatManager; + private ChatManagerHandler chatManager; private final InventoryManager inventoryManager; private final GachaManager gachaManager; private final ShopManager shopManager; @@ -54,20 +57,30 @@ public final class GameServer extends KcpServer { private final DropManager dropManager; private final CombineManger combineManger; + private final TowerScheduleManager towerScheduleManager; - public GameServer() { - this(new InetSocketAddress( - Grasscutter.getConfig().getGameServerOptions().Ip, - Grasscutter.getConfig().getGameServerOptions().Port - )); + private static InetSocketAddress getAdapterInetSocketAddress(){ + InetSocketAddress inetSocketAddress = null; + if(GAME_INFO.bindAddress.equals("")){ + inetSocketAddress=new InetSocketAddress(GAME_INFO.bindPort); + }else{ + inetSocketAddress=new InetSocketAddress( + GAME_INFO.bindAddress, + GAME_INFO.bindPort + ); + } + return inetSocketAddress; + } + public GameServer() { + this(getAdapterInetSocketAddress()); } - public GameServer(InetSocketAddress address) { super(address); this.setServerInitializer(new GameServerInitializer(this)); this.address = address; this.packetHandler = new GameServerPacketHandler(PacketHandler.class); + this.questHandler = new ServerQuestHandler(); this.players = new ConcurrentHashMap<>(); this.worlds = Collections.synchronizedSet(new HashSet<>()); @@ -82,7 +95,7 @@ public final class GameServer extends KcpServer { this.dropManager = new DropManager(this); this.expeditionManager = new ExpeditionManager(this); this.combineManger = new CombineManger(this); - + this.towerScheduleManager = new TowerScheduleManager(this); // Hook into shutdown event. Runtime.getRuntime().addShutdownHook(new Thread(this::onServerShutdown)); } @@ -91,6 +104,10 @@ public final class GameServer extends KcpServer { return packetHandler; } + public ServerQuestHandler getQuestHandler() { + return questHandler; + } + public Map getPlayers() { return players; } @@ -99,9 +116,13 @@ public final class GameServer extends KcpServer { return worlds; } - public ChatManager getChatManager() { + public ChatManagerHandler getChatManager() { return chatManager; } + + public void setChatManager(ChatManagerHandler chatManager) { + this.chatManager = chatManager; + } public InventoryManager getInventoryManager() { return inventoryManager; @@ -139,6 +160,10 @@ public final class GameServer extends KcpServer { return this.combineManger; } + public TowerScheduleManager getTowerScheduleManager() { + return towerScheduleManager; + } + public TaskMap getTaskMap() { return this.taskMap; } @@ -166,12 +191,17 @@ public final class GameServer extends KcpServer { // Check database if character isnt here if (player == null) { - player = DatabaseHelper.getPlayerById(id); + player = DatabaseHelper.getPlayerByUid(id); } return player; } + public Player getPlayerByAccountId(String accountId) { + Optional playerOpt = getPlayers().values().stream().filter(player -> player.getAccount().getId().equals(accountId)).findFirst(); + return playerOpt.orElse(null); + } + public SocialDetail.Builder getSocialDetailByUid(int id) { // Get from online players Player player = this.getPlayerByUid(id, true); diff --git a/src/main/java/emu/grasscutter/server/game/GameServerInitializer.java b/src/main/java/emu/grasscutter/server/game/GameServerInitializer.java index 1c5f51388..6c48e30c1 100644 --- a/src/main/java/emu/grasscutter/server/game/GameServerInitializer.java +++ b/src/main/java/emu/grasscutter/server/game/GameServerInitializer.java @@ -13,8 +13,10 @@ public class GameServerInitializer extends KcpServerInitializer { @Override protected void initChannel(UkcpChannel ch) throws Exception { - ChannelPipeline pipeline = ch.pipeline(); - GameSession session = new GameSession(server); - pipeline.addLast(session); + ChannelPipeline pipeline=null; + if(ch!=null){ + pipeline = ch.pipeline(); + } + new GameSession(server,pipeline); } } diff --git a/src/main/java/emu/grasscutter/server/game/GameServerPacketHandler.java b/src/main/java/emu/grasscutter/server/game/GameServerPacketHandler.java index 88e7fa17f..4bba854ef 100644 --- a/src/main/java/emu/grasscutter/server/game/GameServerPacketHandler.java +++ b/src/main/java/emu/grasscutter/server/game/GameServerPacketHandler.java @@ -14,6 +14,8 @@ import emu.grasscutter.server.game.GameSession.SessionState; import it.unimi.dsi.fastutil.ints.Int2ObjectMap; import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap; +import static emu.grasscutter.Configuration.*; + @SuppressWarnings("unchecked") public class GameServerPacketHandler { private final Int2ObjectMap handlers; @@ -92,7 +94,7 @@ public class GameServerPacketHandler { } // Log unhandled packets - if (Grasscutter.getConfig().DebugMode == ServerDebugMode.MISSING) { + if (SERVER.debugLevel == ServerDebugMode.MISSING) { Grasscutter.getLogger().info("Unhandled packet (" + opcode + "): " + emu.grasscutter.net.packet.PacketOpcodesUtil.getOpcodeName(opcode)); } } diff --git a/src/main/java/emu/grasscutter/server/game/GameSession.java b/src/main/java/emu/grasscutter/server/game/GameSession.java index d1d7eef01..397d5ba6f 100644 --- a/src/main/java/emu/grasscutter/server/game/GameSession.java +++ b/src/main/java/emu/grasscutter/server/game/GameSession.java @@ -3,7 +3,8 @@ package emu.grasscutter.server.game; import java.io.File; import java.net.InetSocketAddress; import java.nio.ByteBuffer; -import java.util.HashSet; +import java.util.Iterator; +import java.util.Map; import java.util.Set; import emu.grasscutter.Grasscutter; @@ -18,14 +19,17 @@ import emu.grasscutter.server.event.game.SendPacketEvent; import emu.grasscutter.utils.Crypto; import emu.grasscutter.utils.FileUtils; import emu.grasscutter.utils.Utils; +import io.jpower.kcp.netty.UkcpChannel; import io.netty.buffer.ByteBuf; import io.netty.buffer.Unpooled; import io.netty.channel.ChannelHandlerContext; +import io.netty.channel.ChannelPipeline; import static emu.grasscutter.utils.Language.translate; +import static emu.grasscutter.Configuration.*; public class GameSession extends KcpChannel { - private GameServer server; + private final GameServer server; private Account account; private Player player; @@ -36,11 +40,30 @@ public class GameSession extends KcpChannel { private int clientTime; private long lastPingTime; private int lastClientSeq = 10; - + + private final ChannelPipeline pipeline; + @Override + public void close() { + setState(SessionState.INACTIVE); + //send disconnection pack in case of reconnection + try { + send(new BasePacket(PacketOpcodes.ServerDisconnectClientNotify)); + }catch (Throwable ignore){ + + } + super.close(); + } public GameSession(GameServer server) { + this(server,null); + } + public GameSession(GameServer server, ChannelPipeline pipeline) { this.server = server; this.state = SessionState.WAITING_FOR_TOKEN; this.lastPingTime = System.currentTimeMillis(); + this.pipeline = pipeline; + if(pipeline!=null) { + pipeline.addLast(this); + } } public GameServer getServer() { @@ -124,13 +147,17 @@ public class GameSession extends KcpChannel { // Set state so no more packets can be handled this.setState(SessionState.INACTIVE); - + // Save after disconnecting if (this.isLoggedIn()) { + Player player = getPlayer(); // Call logout event. - getPlayer().onLogout(); - // Remove from server. - getServer().getPlayers().remove(getPlayer().getUid()); + player.onLogout(); + } + try { + pipeline.remove(this); + } catch (Throwable ignore) { + } } @@ -140,7 +167,7 @@ public class GameSession extends KcpChannel { } public void replayPacket(int opcode, String name) { - String filePath = Grasscutter.getConfig().PACKETS_FOLDER + name; + String filePath = PACKET(name); File p = new File(filePath); if (!p.exists()) return; @@ -172,7 +199,7 @@ public class GameSession extends KcpChannel { } // Log - if (Grasscutter.getConfig().DebugMode == ServerDebugMode.ALL) { + if (SERVER.debugLevel == ServerDebugMode.ALL) { logPacket(packet); } @@ -239,7 +266,7 @@ public class GameSession extends KcpChannel { } // Log packet - if (Grasscutter.getConfig().DebugMode == ServerDebugMode.ALL) { + if (SERVER.debugLevel == ServerDebugMode.ALL) { if (!loopPacket.contains(opcode)) { Grasscutter.getLogger().info("RECV: " + PacketOpcodesUtil.getOpcodeName(opcode) + " (" + opcode + ")"); System.out.println(Utils.bytesToHex(payload)); @@ -252,6 +279,7 @@ public class GameSession extends KcpChannel { } catch (Exception e) { e.printStackTrace(); } finally { + data.release(); packet.release(); } } diff --git a/src/main/java/emu/grasscutter/server/http/HttpServer.java b/src/main/java/emu/grasscutter/server/http/HttpServer.java new file mode 100644 index 000000000..bcc020ed7 --- /dev/null +++ b/src/main/java/emu/grasscutter/server/http/HttpServer.java @@ -0,0 +1,202 @@ +package emu.grasscutter.server.http; + +import emu.grasscutter.Grasscutter; +import emu.grasscutter.Grasscutter.ServerDebugMode; +import emu.grasscutter.utils.FileUtils; +import express.Express; +import express.http.MediaType; +import io.javalin.Javalin; +import org.eclipse.jetty.server.Server; +import org.eclipse.jetty.server.ServerConnector; +import org.eclipse.jetty.util.ssl.SslContextFactory; + +import java.io.File; +import java.io.IOException; + +import static emu.grasscutter.Configuration.*; +import static emu.grasscutter.utils.Language.translate; + +/** + * Manages all HTTP-related classes. + * (including dispatch, announcements, gacha, etc.) + */ +public final class HttpServer { + private final Express express; + + /** + * Configures the Express application. + */ + public HttpServer() { + this.express = new Express(config -> { + // Set the Express HTTP server. + config.server(HttpServer::createServer); + + // Configure encryption/HTTPS/SSL. + config.enforceSsl = HTTP_ENCRYPTION.useEncryption; + + // Configure HTTP policies. + if(HTTP_POLICIES.cors.enabled) { + var allowedOrigins = HTTP_POLICIES.cors.allowedOrigins; + if (allowedOrigins.length > 0) + config.enableCorsForOrigin(allowedOrigins); + else config.enableCorsForAllOrigins(); + } + + // Configure debug logging. + if(SERVER.debugLevel == ServerDebugMode.ALL) + config.enableDevLogging(); + + // Disable compression on static files. + config.precompressStaticFiles = false; + }); + } + + /** + * Creates an HTTP(S) server. + * @return A server instance. + */ + @SuppressWarnings("resource") + private static Server createServer() { + Server server = new Server(); + ServerConnector serverConnector + = new ServerConnector(server); + + if(HTTP_ENCRYPTION.useEncryption) { + var sslContextFactory = new SslContextFactory.Server(); + var keystoreFile = new File(HTTP_ENCRYPTION.keystore); + + if(!keystoreFile.exists()) { + HTTP_ENCRYPTION.useEncryption = false; + HTTP_ENCRYPTION.useInRouting = false; + + Grasscutter.getLogger().warn(translate("messages.dispatch.keystore.no_keystore_error")); + } else try { + sslContextFactory.setKeyStorePath(keystoreFile.getPath()); + sslContextFactory.setKeyStorePassword(HTTP_ENCRYPTION.keystorePassword); + } catch (Exception ignored) { + Grasscutter.getLogger().warn(translate("messages.dispatch.keystore.password_error")); + + try { + sslContextFactory.setKeyStorePath(keystoreFile.getPath()); + sslContextFactory.setKeyStorePassword("123456"); + + Grasscutter.getLogger().warn(translate("messages.dispatch.keystore.default_password")); + } catch (Exception exception) { + Grasscutter.getLogger().warn(translate("messages.dispatch.keystore.general_error"), exception); + } + } finally { + serverConnector = new ServerConnector(server, sslContextFactory); + } + } + + serverConnector.setPort(HTTP_INFO.bindPort); + server.setConnectors(new ServerConnector[]{serverConnector}); + + return server; + } + + /** + * Returns the handle for the Express application. + * @return A Javalin instance. + */ + public Javalin getHandle() { + return this.express.raw(); + } + + /** + * Initializes the provided class. + * @param router The router class. + * @return Method chaining. + */ + @SuppressWarnings("UnusedReturnValue") + public HttpServer addRouter(Class router, Object... args) { + // Get all constructor parameters. + Class[] types = new Class[args.length]; + for(var argument : args) + types[args.length - 1] = argument.getClass(); + + try { // Create a router instance & apply routes. + var constructor = router.getDeclaredConstructor(types); // Get the constructor. + var routerInstance = constructor.newInstance(args); // Create instance. + routerInstance.applyRoutes(this.express, this.getHandle()); // Apply routes. + } catch (Exception exception) { + Grasscutter.getLogger().warn(translate("messages.dispatch.router_error"), exception); + } return this; + } + + /** + * Starts listening on the HTTP server. + */ + public void start() { + // Attempt to start the HTTP server. + if(HTTP_INFO.bindAddress.equals("")){ + this.express.listen(HTTP_INFO.bindPort); + }else{ + this.express.listen(HTTP_INFO.bindAddress, HTTP_INFO.bindPort); + } + + // Log bind information. + Grasscutter.getLogger().info(translate("messages.dispatch.port_bind", Integer.toString(this.express.raw().port()))); + } + + /** + * Handles the '/' (index) endpoint on the Express application. + */ + public static class DefaultRequestRouter implements Router { + @Override public void applyRoutes(Express express, Javalin handle) { + express.get("/", (request, response) -> { + File file = new File(HTTP_STATIC_FILES.indexFile); + if(!file.exists()) + response.send(""" + + + + + + %s + + """.formatted(translate("messages.status.welcome"))); + else { + final var filePath = file.getPath(); + final MediaType fromExtension = MediaType.getByExtension(filePath.substring(filePath.lastIndexOf(".") + 1)); + response.type((fromExtension != null) ? fromExtension.getMIME() : "text/plain") + .send(FileUtils.read(filePath)); + } + }); + } + } + + /** + * Handles unhandled endpoints on the Express application. + */ + public static class UnhandledRequestRouter implements Router { + @Override public void applyRoutes(Express express, Javalin handle) { + handle.error(404, context -> { + if(SERVER.debugLevel == ServerDebugMode.MISSING) + Grasscutter.getLogger().info(translate("messages.dispatch.unhandled_request_error", context.method(), context.url())); + context.contentType("text/html"); + + File file = new File(HTTP_STATIC_FILES.errorFile); + if(!file.exists()) + context.result(""" + + + + + + + + + + + """); + else { + final var filePath = file.getPath(); + final MediaType fromExtension = MediaType.getByExtension(filePath.substring(filePath.lastIndexOf(".") + 1)); + context.contentType((fromExtension != null) ? fromExtension.getMIME() : "text/plain") + .result(FileUtils.read(filePath)); + } + }); + } + } +} diff --git a/src/main/java/emu/grasscutter/server/http/Router.java b/src/main/java/emu/grasscutter/server/http/Router.java new file mode 100644 index 000000000..1720d7ca0 --- /dev/null +++ b/src/main/java/emu/grasscutter/server/http/Router.java @@ -0,0 +1,16 @@ +package emu.grasscutter.server.http; + +import express.Express; +import io.javalin.Javalin; + +/** + * Defines routes for an {@link Express} instance. + */ +public interface Router { + + /** + * Called when the router is initialized by Express. + * @param express An Express instance. + */ + void applyRoutes(Express express, Javalin handle); +} diff --git a/src/main/java/emu/grasscutter/server/http/dispatch/DispatchHandler.java b/src/main/java/emu/grasscutter/server/http/dispatch/DispatchHandler.java new file mode 100644 index 000000000..a5545877f --- /dev/null +++ b/src/main/java/emu/grasscutter/server/http/dispatch/DispatchHandler.java @@ -0,0 +1,121 @@ +package emu.grasscutter.server.http.dispatch; + +import emu.grasscutter.Grasscutter; +import emu.grasscutter.auth.AuthenticationSystem; +import emu.grasscutter.auth.OAuthAuthenticator; +import emu.grasscutter.auth.OAuthAuthenticator.ClientType; +import emu.grasscutter.server.http.Router; +import emu.grasscutter.server.http.objects.*; +import emu.grasscutter.server.http.objects.ComboTokenReqJson.LoginTokenData; +import emu.grasscutter.utils.Utils; +import express.Express; +import express.http.Request; +import express.http.Response; +import io.javalin.Javalin; + +import static emu.grasscutter.utils.Language.translate; + +/** + * Handles requests related to authentication. (aka dispatch) + */ +public final class DispatchHandler implements Router { + @Override public void applyRoutes(Express express, Javalin handle) { + // Username & Password login (from client). + express.post("/hk4e_global/mdk/shield/api/login", DispatchHandler::clientLogin); + // Cached token login (from registry). + express.post("/hk4e_global/mdk/shield/api/verify", DispatchHandler::tokenLogin); + // Combo token login (from session key). + express.post("/hk4e_global/combo/granter/login/v2/login", DispatchHandler::sessionKeyLogin); + + // External login (from other clients). + express.get("/authentication/type", (request, response) -> response.send(Grasscutter.getAuthenticationSystem().getClass().getSimpleName())); + express.post("/authentication/login", (request, response) -> Grasscutter.getAuthenticationSystem().getExternalAuthenticator() + .handleLogin(AuthenticationSystem.fromExternalRequest(request, response))); + express.post("/authentication/register", (request, response) -> Grasscutter.getAuthenticationSystem().getExternalAuthenticator() + .handleAccountCreation(AuthenticationSystem.fromExternalRequest(request, response))); + express.post("/authentication/change_password", (request, response) -> Grasscutter.getAuthenticationSystem().getExternalAuthenticator() + .handlePasswordReset(AuthenticationSystem.fromExternalRequest(request, response))); + + // External login (from OAuth2). + express.post("/hk4e_global/mdk/shield/api/loginByThirdparty", (request, response) -> Grasscutter.getAuthenticationSystem().getOAuthAuthenticator() + .handleLogin(AuthenticationSystem.fromExternalRequest(request, response))); + express.get("/authentication/openid/redirect", (request, response) -> Grasscutter.getAuthenticationSystem().getOAuthAuthenticator() + .handleTokenProcess(AuthenticationSystem.fromExternalRequest(request, response))); + express.get("/Api/twitter_login", (request, response) -> Grasscutter.getAuthenticationSystem().getOAuthAuthenticator() + .handleRedirection(AuthenticationSystem.fromExternalRequest(request, response), ClientType.DESKTOP)); + express.get("/sdkTwitterLogin.html", (request, response) -> Grasscutter.getAuthenticationSystem().getOAuthAuthenticator() + .handleRedirection(AuthenticationSystem.fromExternalRequest(request, response), ClientType.MOBILE)); + } + + /** + * @route /hk4e_global/mdk/shield/api/login + */ + private static void clientLogin(Request request, Response response) { + // Parse body data. + String rawBodyData = request.ctx().body(); + var bodyData = Utils.jsonDecode(rawBodyData, LoginAccountRequestJson.class); + + // Validate body data. + if(bodyData == null) + return; + + // Pass data to authentication handler. + var responseData = Grasscutter.getAuthenticationSystem() + .getPasswordAuthenticator() + .authenticate(AuthenticationSystem.fromPasswordRequest(request, bodyData)); + // Send response. + response.send(responseData); + + // Log to console. + Grasscutter.getLogger().info(translate("messages.dispatch.account.login_attempt", request.ip())); + } + + /** + * @route /hk4e_global/mdk/shield/api/verify + */ + private static void tokenLogin(Request request, Response response) { + // Parse body data. + String rawBodyData = request.ctx().body(); + var bodyData = Utils.jsonDecode(rawBodyData, LoginTokenRequestJson.class); + + // Validate body data. + if(bodyData == null) + return; + + // Pass data to authentication handler. + var responseData = Grasscutter.getAuthenticationSystem() + .getTokenAuthenticator() + .authenticate(AuthenticationSystem.fromTokenRequest(request, bodyData)); + // Send response. + response.send(responseData); + + // Log to console. + Grasscutter.getLogger().info(translate("messages.dispatch.account.login_attempt", request.ip())); + } + + /** + * @route /hk4e_global/combo/granter/login/v2/login + */ + private static void sessionKeyLogin(Request request, Response response) { + // Parse body data. + String rawBodyData = request.ctx().body(); + var bodyData = Utils.jsonDecode(rawBodyData, ComboTokenReqJson.class); + + // Validate body data. + if(bodyData == null || bodyData.data == null) + return; + + // Decode additional body data. + var tokenData = Utils.jsonDecode(bodyData.data, LoginTokenData.class); + + // Pass data to authentication handler. + var responseData = Grasscutter.getAuthenticationSystem() + .getSessionKeyAuthenticator() + .authenticate(AuthenticationSystem.fromComboTokenRequest(request, bodyData, tokenData)); + // Send response. + response.send(responseData); + + // Log to console. + Grasscutter.getLogger().info(translate("messages.dispatch.account.login_attempt", request.ip())); + } +} diff --git a/src/main/java/emu/grasscutter/server/http/dispatch/RegionHandler.java b/src/main/java/emu/grasscutter/server/http/dispatch/RegionHandler.java new file mode 100644 index 000000000..f2d1cc4ca --- /dev/null +++ b/src/main/java/emu/grasscutter/server/http/dispatch/RegionHandler.java @@ -0,0 +1,175 @@ +package emu.grasscutter.server.http.dispatch; + +import com.google.protobuf.ByteString; +import com.google.protobuf.InvalidProtocolBufferException; +import emu.grasscutter.Grasscutter; +import emu.grasscutter.Grasscutter.ServerRunMode; +import emu.grasscutter.net.proto.QueryCurrRegionHttpRspOuterClass.*; +import emu.grasscutter.net.proto.RegionInfoOuterClass; +import emu.grasscutter.net.proto.RegionInfoOuterClass.RegionInfo; +import emu.grasscutter.net.proto.RegionSimpleInfoOuterClass.RegionSimpleInfo; +import emu.grasscutter.server.event.dispatch.QueryAllRegionsEvent; +import emu.grasscutter.server.event.dispatch.QueryCurrentRegionEvent; +import emu.grasscutter.server.http.Router; +import emu.grasscutter.utils.Crypto; +import emu.grasscutter.utils.FileUtils; +import emu.grasscutter.utils.Utils; +import express.Express; +import express.http.Request; +import express.http.Response; +import io.javalin.Javalin; + +import java.io.File; +import java.util.ArrayList; +import java.util.Base64; +import java.util.List; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; + +import static emu.grasscutter.Configuration.*; +import static emu.grasscutter.net.proto.QueryRegionListHttpRspOuterClass.*; + +/** + * Handles requests related to region queries. + */ +public final class RegionHandler implements Router { + private static final Map regions = new ConcurrentHashMap<>(); + private static String regionListResponse; + + public RegionHandler() { + try { // Read & initialize region data. + this.initialize(); + } catch (Exception exception) { + Grasscutter.getLogger().error("Failed to initialize region data.", exception); + } + } + + /** + * Configures region data according to configuration. + */ + private void initialize() { + String dispatchDomain = "http" + (HTTP_ENCRYPTION.useInRouting ? "s" : "") + "://" + + lr(HTTP_INFO.accessAddress, HTTP_INFO.bindAddress) + ":" + + lr(HTTP_INFO.accessPort, HTTP_INFO.bindPort); + + // Create regions. + List servers = new ArrayList<>(); + List usedNames = new ArrayList<>(); // List to check for potential naming conflicts. + + var configuredRegions = new ArrayList<>(List.of(DISPATCH_INFO.regions)); + if(SERVER.runMode != ServerRunMode.HYBRID && configuredRegions.size() == 0) { + Grasscutter.getLogger().error("[Dispatch] There are no game servers available. Exiting due to unplayable state."); + System.exit(1); + } else if (configuredRegions.size() == 0) + configuredRegions.add(new Region("os_usa", DISPATCH_INFO.defaultName, + lr(GAME_INFO.accessAddress, GAME_INFO.bindAddress), + lr(GAME_INFO.accessPort, GAME_INFO.bindPort))); + + configuredRegions.forEach(region -> { + if (usedNames.contains(region.Name)) { + Grasscutter.getLogger().error("Region name already in use."); + return; + } + + // Create a region identifier. + var identifier = RegionSimpleInfo.newBuilder() + .setName(region.Name).setTitle(region.Title).setType("DEV_PUBLIC") + .setDispatchUrl(dispatchDomain + "/query_cur_region/" + region.Name) + .build(); + usedNames.add(region.Name); servers.add(identifier); + + // Create a region info object. + var regionInfo = RegionInfo.newBuilder() + .setGateserverIp(region.Ip).setGateserverPort(region.Port) + .setSecretKey(ByteString.copyFrom(Crypto.DISPATCH_SEED)) + .build(); + // Create an updated region query. + var updatedQuery = QueryCurrRegionHttpRsp.newBuilder().setRegionInfo(regionInfo).build(); + regions.put(region.Name, new RegionData(updatedQuery, Utils.base64Encode(updatedQuery.toByteString().toByteArray()))); + }); + + // Create a config object. + byte[] customConfig = "{\"sdkenv\":\"2\",\"checkdevice\":\"false\",\"loadPatch\":\"false\",\"showexception\":\"false\",\"regionConfig\":\"pm|fk|add\",\"downloadMode\":\"0\"}".getBytes(); + Crypto.xor(customConfig, Crypto.DISPATCH_KEY); // XOR the config with the key. + + // Create an updated region list. + QueryRegionListHttpRsp updatedRegionList = QueryRegionListHttpRsp.newBuilder() + .addAllRegionList(servers) + .setClientSecretKey(ByteString.copyFrom(Crypto.DISPATCH_SEED)) + .setClientCustomConfigEncrypted(ByteString.copyFrom(customConfig)) + .setEnableLoginPc(true).build(); + + // Set the region list response. + regionListResponse = Utils.base64Encode(updatedRegionList.toByteString().toByteArray()); + } + + @Override public void applyRoutes(Express express, Javalin handle) { + express.get("/query_region_list", RegionHandler::queryRegionList); + express.get("/query_cur_region/:region", RegionHandler::queryCurrentRegion ); + } + + /** + * @route /query_region_list + */ + private static void queryRegionList(Request request, Response response) { + // Invoke event. + QueryAllRegionsEvent event = new QueryAllRegionsEvent(regionListResponse); event.call(); + // Respond with event result. + response.send(event.getRegionList()); + + // Log to console. + Grasscutter.getLogger().info(String.format("[Dispatch] Client %s request: query_region_list", request.ip())); + } + + /** + * @route /query_cur_region/:region + */ + private static void queryCurrentRegion(Request request, Response response) { + // Get region to query. + String regionName = request.params("region"); + + // Get region data. + String regionData = "CAESGE5vdCBGb3VuZCB2ZXJzaW9uIGNvbmZpZw=="; + if (request.query().values().size() > 0) { + var region = regions.get(regionName); + if(region != null) regionData = region.getBase64(); + } + + // Invoke event. + QueryCurrentRegionEvent event = new QueryCurrentRegionEvent(regionData); event.call(); + // Respond with event result. + response.send(event.getRegionInfo()); + + // Log to console. + Grasscutter.getLogger().info(String.format("Client %s request: query_cur_region/%s", request.ip(), regionName)); + } + + /** + * Region data container. + */ + public static class RegionData { + private final QueryCurrRegionHttpRsp regionQuery; + private final String base64; + + public RegionData(QueryCurrRegionHttpRsp prq, String b64) { + this.regionQuery = prq; + this.base64 = b64; + } + + public QueryCurrRegionHttpRsp getRegionQuery() { + return this.regionQuery; + } + + public String getBase64() { + return this.base64; + } + } + + /** + * Gets the current region query. + * @return A {@link QueryCurrRegionHttpRsp} object. + */ + public static QueryCurrRegionHttpRsp getCurrentRegion() { + return SERVER.runMode == ServerRunMode.HYBRID ? regions.get("os_usa").getRegionQuery() : null; + } +} diff --git a/src/main/java/emu/grasscutter/server/http/documentation/DocumentationHandler.java b/src/main/java/emu/grasscutter/server/http/documentation/DocumentationHandler.java new file mode 100644 index 000000000..de7f543a3 --- /dev/null +++ b/src/main/java/emu/grasscutter/server/http/documentation/DocumentationHandler.java @@ -0,0 +1,9 @@ +package emu.grasscutter.server.http.documentation; + +import express.http.Request; +import express.http.Response; + +interface DocumentationHandler { + + void handle(Request request, Response response); +} diff --git a/src/main/java/emu/grasscutter/server/http/documentation/DocumentationServerHandler.java b/src/main/java/emu/grasscutter/server/http/documentation/DocumentationServerHandler.java new file mode 100644 index 000000000..24c8236de --- /dev/null +++ b/src/main/java/emu/grasscutter/server/http/documentation/DocumentationServerHandler.java @@ -0,0 +1,19 @@ +package emu.grasscutter.server.http.documentation; + +import emu.grasscutter.server.http.Router; +import express.Express; +import io.javalin.Javalin; + +public final class DocumentationServerHandler implements Router { + + @Override + public void applyRoutes(Express express, Javalin handle) { + final RootRequestHandler root = new RootRequestHandler(); + final HandbookRequestHandler handbook = new HandbookRequestHandler(); + final GachaMappingRequestHandler gachaMapping = new GachaMappingRequestHandler(); + + express.get("/documentation/handbook", handbook::handle); + express.get("/documentation/gachamapping", gachaMapping::handle); + express.get("/documentation", root::handle); + } +} diff --git a/src/main/java/emu/grasscutter/server/http/documentation/GachaMappingRequestHandler.java b/src/main/java/emu/grasscutter/server/http/documentation/GachaMappingRequestHandler.java new file mode 100644 index 000000000..4810978ad --- /dev/null +++ b/src/main/java/emu/grasscutter/server/http/documentation/GachaMappingRequestHandler.java @@ -0,0 +1,153 @@ +package emu.grasscutter.server.http.documentation; + +import static emu.grasscutter.Configuration.RESOURCE; + +import com.google.gson.reflect.TypeToken; +import emu.grasscutter.Grasscutter; +import emu.grasscutter.data.GameData; +import emu.grasscutter.data.excels.AvatarData; +import emu.grasscutter.data.excels.ItemData; +import emu.grasscutter.utils.Utils; +import static emu.grasscutter.Configuration.DOCUMENT_LANGUAGE; +import express.http.Request; +import express.http.Response; +import java.io.FileInputStream; +import java.io.IOException; +import java.io.InputStreamReader; +import java.nio.charset.StandardCharsets; +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +final class GachaMappingRequestHandler implements DocumentationHandler { + + private Map map; + + GachaMappingRequestHandler() { + final String textMapFile = "TextMap/TextMap" + DOCUMENT_LANGUAGE + ".json"; + try (InputStreamReader fileReader = new InputStreamReader(new FileInputStream( + Utils.toFilePath(RESOURCE(textMapFile))), StandardCharsets.UTF_8)) { + map = Grasscutter.getGsonFactory().fromJson(fileReader, + new TypeToken>() { + }.getType()); + } catch (IOException e) { + Grasscutter.getLogger().warn("Resource does not exist: " + textMapFile); + map = new HashMap<>(); + } + } + + @Override + public void handle(Request request, Response response) { + if (map.isEmpty()) { + response.status(500); + } else { + response.set("Content-Type", "application/json") + .ctx() + .result(createGachaMappingJson()); + } + } + + private String createGachaMappingJson() { + List list; + + final StringBuilder sb = new StringBuilder(); + list = new ArrayList<>(GameData.getAvatarDataMap().keySet()); + Collections.sort(list); + + final String newLine = System.lineSeparator(); + + // if the user made choices for language, I assume it's okay to assign his/her selected language to "en-us" + // since it's the fallback language and there will be no difference in the gacha record page. + // The enduser can still modify the `gacha_mappings.js` directly to enable multilingual for the gacha record system. + sb.append("{").append(newLine); + + // Avatars + boolean first = true; + for (Integer id : list) { + AvatarData data = GameData.getAvatarDataMap().get(id); + int avatarID = data.getId(); + if (avatarID >= 11000000) { // skip test avatar + continue; + } + if (first) { // skip adding comma for the first element + first = false; + } else { + sb.append(","); + } + String color; + switch (data.getQualityType()) { + case "QUALITY_PURPLE": + color = "purple"; + break; + case "QUALITY_ORANGE": + color = "yellow"; + break; + case "QUALITY_BLUE": + default: + color = "blue"; + } + // Got the magic number 4233146695 from manually search in the json file + sb.append("\"") + .append(avatarID % 1000 + 1000) + .append("\" : [\"") + .append(map.get(data.getNameTextMapHash())) + .append("(") + .append(map.get(4233146695L)) + .append(")\", \"") + .append(color) + .append("\"]") + .append(newLine); + } + + list = new ArrayList<>(GameData.getItemDataMap().keySet()); + Collections.sort(list); + + // Weapons + for (Integer id : list) { + ItemData data = GameData.getItemDataMap().get(id); + if (data.getId() <= 11101 || data.getId() >= 20000) { + continue; //skip non weapon items + } + String color; + + switch (data.getRankLevel()) { + case 3: + color = "blue"; + break; + case 4: + color = "purple"; + break; + case 5: + color = "yellow"; + break; + default: + continue; // skip unnecessary entries + } + + // Got the magic number 4231343903 from manually search in the json file + + sb.append(",\"") + .append(data.getId()) + .append("\" : [\"") + .append(map.get(data.getNameTextMapHash()).replaceAll("\"", "")) + .append("(") + .append(map.get(4231343903L)) + .append(")\",\"") + .append(color) + .append("\"]") + .append(newLine); + } + sb.append(",\"200\": \"") + .append(map.get(332935371L)) + .append("\", \"301\": \"") + .append(map.get(2272170627L)) + .append("\", \"302\": \"") + .append(map.get(2864268523L)) + .append("\"") + .append("}\n}") + .append(newLine); + return sb.toString(); + } +} diff --git a/src/main/java/emu/grasscutter/server/http/documentation/HandbookRequestHandler.java b/src/main/java/emu/grasscutter/server/http/documentation/HandbookRequestHandler.java new file mode 100644 index 000000000..8153368a0 --- /dev/null +++ b/src/main/java/emu/grasscutter/server/http/documentation/HandbookRequestHandler.java @@ -0,0 +1,123 @@ +package emu.grasscutter.server.http.documentation; + +import static emu.grasscutter.Configuration.*; +import static emu.grasscutter.utils.Language.translate; + +import com.google.gson.reflect.TypeToken; +import emu.grasscutter.Grasscutter; +import emu.grasscutter.command.CommandMap; +import emu.grasscutter.data.GameData; +import emu.grasscutter.data.excels.AvatarData; +import emu.grasscutter.data.excels.ItemData; +import emu.grasscutter.data.excels.MonsterData; +import emu.grasscutter.data.excels.SceneData; +import emu.grasscutter.utils.FileUtils; +import emu.grasscutter.utils.Utils; +import express.http.Request; +import express.http.Response; +import it.unimi.dsi.fastutil.ints.Int2ObjectMap; +import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; +import java.io.InputStreamReader; +import java.nio.charset.StandardCharsets; +import java.util.HashMap; +import java.util.Map; +import java.util.stream.Collectors; + +final class HandbookRequestHandler implements DocumentationHandler { + + private final String template; + private Map map; + + + public HandbookRequestHandler() { + final File templateFile = new File(Utils.toFilePath(DATA("documentation/handbook.html"))); + if (templateFile.exists()) { + template = new String(FileUtils.read(templateFile), StandardCharsets.UTF_8); + } else { + Grasscutter.getLogger().warn("File does not exist: " + templateFile); + template = null; + } + + final String textMapFile = "TextMap/TextMap" + DOCUMENT_LANGUAGE + ".json"; + try (InputStreamReader fileReader = new InputStreamReader(new FileInputStream( + Utils.toFilePath(RESOURCE(textMapFile))), StandardCharsets.UTF_8)) { + map = Grasscutter.getGsonFactory() + .fromJson(fileReader, new TypeToken>() { + }.getType()); + } catch (IOException e) { + Grasscutter.getLogger().warn("Resource does not exist: " + textMapFile); + map = new HashMap<>(); + } + } + + @Override + public void handle(Request request, Response response) { + if (template == null) { + response.status(500); + return; + } + + final CommandMap cmdMap = new CommandMap(true); + final Int2ObjectMap avatarMap = GameData.getAvatarDataMap(); + final Int2ObjectMap itemMap = GameData.getItemDataMap(); + final Int2ObjectMap sceneMap = GameData.getSceneDataMap(); + final Int2ObjectMap monsterMap = GameData.getMonsterDataMap(); + + // Add translated title etc. to the page. + String content = template.replace("{{TITLE}}", translate("documentation.handbook.title")) + .replace("{{TITLE_COMMANDS}}", translate("documentation.handbook.title_commands")) + .replace("{{TITLE_AVATARS}}", translate("documentation.handbook.title_avatars")) + .replace("{{TITLE_ITEMS}}", translate("documentation.handbook.title_items")) + .replace("{{TITLE_SCENES}}", translate("documentation.handbook.title_scenes")) + .replace("{{TITLE_MONSTERS}}", translate("documentation.handbook.title_monsters")) + .replace("{{HEADER_ID}}", translate("documentation.handbook.header_id")) + .replace("{{HEADER_COMMAND}}", translate("documentation.handbook.header_command")) + .replace("{{HEADER_DESCRIPTION}}", + translate("documentation.handbook.header_description")) + .replace("{{HEADER_AVATAR}}", translate("documentation.handbook.header_avatar")) + .replace("{{HEADER_ITEM}}", translate("documentation.handbook.header_item")) + .replace("{{HEADER_SCENE}}", translate("documentation.handbook.header_scene")) + .replace("{{HEADER_MONSTER}}", translate("documentation.handbook.header_monster")) + // Commands table + .replace("{{COMMANDS_TABLE}}", cmdMap.getAnnotationsAsList() + .stream() + .map(cmd -> "" + cmd.label() + "" + + cmd.description() + "") + .collect(Collectors.joining("\n"))) + // Avatars table + .replace("{{AVATARS_TABLE}}", GameData.getAvatarDataMap().keySet() + .intStream() + .sorted() + .mapToObj(avatarMap::get) + .map(data -> "" + data.getId() + "" + + map.get(data.getNameTextMapHash()) + "") + .collect(Collectors.joining("\n"))) + // Items table + .replace("{{ITEMS_TABLE}}", GameData.getItemDataMap().keySet() + .intStream() + .sorted() + .mapToObj(itemMap::get) + .map(data -> "" + data.getId() + "" + + map.get(data.getNameTextMapHash()) + "") + .collect(Collectors.joining("\n"))) + // Scenes table + .replace("{{SCENES_TABLE}}", GameData.getSceneDataMap().keySet() + .intStream() + .sorted() + .mapToObj(sceneMap::get) + .map(data -> "" + data.getId() + "" + + data.getScriptData() + "") + .collect(Collectors.joining("\n"))) + .replace("{{MONSTERS_TABLE}}", GameData.getMonsterDataMap().keySet() + .intStream() + .sorted() + .mapToObj(monsterMap::get) + .map(data -> "" + data.getId() + "" + + map.get(data.getNameTextMapHash()) + "") + .collect(Collectors.joining("\n"))); + + response.send(content); + } +} diff --git a/src/main/java/emu/grasscutter/server/http/documentation/RootRequestHandler.java b/src/main/java/emu/grasscutter/server/http/documentation/RootRequestHandler.java new file mode 100644 index 000000000..12b738bf3 --- /dev/null +++ b/src/main/java/emu/grasscutter/server/http/documentation/RootRequestHandler.java @@ -0,0 +1,41 @@ +package emu.grasscutter.server.http.documentation; + +import static emu.grasscutter.Configuration.DATA; +import static emu.grasscutter.utils.Language.translate; + +import emu.grasscutter.Grasscutter; +import emu.grasscutter.data.ResourceLoader; +import emu.grasscutter.utils.FileUtils; +import emu.grasscutter.utils.Utils; +import express.http.Request; +import express.http.Response; +import java.io.File; +import java.nio.charset.StandardCharsets; + +final class RootRequestHandler implements DocumentationHandler { + + private final String template; + + public RootRequestHandler() { + final File templateFile = new File(Utils.toFilePath(DATA("documentation/index.html"))); + if (templateFile.exists()) { + template = new String(FileUtils.read(templateFile), StandardCharsets.UTF_8); + } else { + Grasscutter.getLogger().warn("File does not exist: " + templateFile); + template = null; + } + } + + @Override + public void handle(Request request, Response response) { + if (template == null) { + response.status(500); + return; + } + + String content = template.replace("{{TITLE}}", translate("documentation.index.title")) + .replace("{{ITEM_HANDBOOK}}", translate("documentation.index.handbook")) + .replace("{{ITEM_GACHA_MAPPING}}", translate("documentation.index.gacha_mapping")); + response.send(content); + } +} diff --git a/src/main/java/emu/grasscutter/server/http/handlers/AnnouncementsHandler.java b/src/main/java/emu/grasscutter/server/http/handlers/AnnouncementsHandler.java new file mode 100644 index 000000000..07790a641 --- /dev/null +++ b/src/main/java/emu/grasscutter/server/http/handlers/AnnouncementsHandler.java @@ -0,0 +1,92 @@ +package emu.grasscutter.server.http.handlers; + +import emu.grasscutter.Grasscutter; +import emu.grasscutter.data.DataLoader; +import emu.grasscutter.server.http.objects.HttpJsonResponse; +import emu.grasscutter.server.http.Router; +import emu.grasscutter.utils.FileUtils; +import emu.grasscutter.utils.Utils; +import express.Express; +import express.http.MediaType; +import express.http.Request; +import express.http.Response; +import io.javalin.Javalin; + +import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.nio.charset.StandardCharsets; +import java.util.Objects; + +import static emu.grasscutter.Configuration.*; + +/** + * Handles requests related to the announcements page. + */ +public final class AnnouncementsHandler implements Router { + @Override public void applyRoutes(Express express, Javalin handle) { + // hk4e-api-os.hoyoverse.com + express.all("/common/hk4e_global/announcement/api/getAlertPic", new HttpJsonResponse("{\"retcode\":0,\"message\":\"OK\",\"data\":{\"total\":0,\"list\":[]}}")); + // hk4e-api-os.hoyoverse.com + express.all("/common/hk4e_global/announcement/api/getAlertAnn", new HttpJsonResponse("{\"retcode\":0,\"message\":\"OK\",\"data\":{\"alert\":false,\"alert_id\":0,\"remind\":true}}")); + // hk4e-api-os.hoyoverse.com + express.all("/common/hk4e_global/announcement/api/getAnnList", AnnouncementsHandler::getAnnouncement); + // hk4e-api-os-static.hoyoverse.com + express.all("/common/hk4e_global/announcement/api/getAnnContent", AnnouncementsHandler::getAnnouncement); + // hk4e-sdk-os.hoyoverse.com + express.all("/hk4e_global/mdk/shopwindow/shopwindow/listPriceTier", new HttpJsonResponse("{\"retcode\":0,\"message\":\"OK\",\"data\":{\"suggest_currency\":\"USD\",\"tiers\":[]}}")); + + express.get("/hk4e/announcement/*", AnnouncementsHandler::getPageResources); + } + + private static void getAnnouncement(Request request, Response response) { + String data = ""; + if (Objects.equals(request.baseUrl(), "/common/hk4e_global/announcement/api/getAnnContent")) { + try { + data = FileUtils.readToString(DataLoader.load("GameAnnouncement.json")); + } catch (Exception e) { + if(e.getClass() == IOException.class) { + Grasscutter.getLogger().info("Unable to read file 'GameAnnouncementList.json'. \n" + e); + } + } + } else if (Objects.equals(request.baseUrl(), "/common/hk4e_global/announcement/api/getAnnList")) { + try { + data = FileUtils.readToString(DataLoader.load("GameAnnouncementList.json")); + } catch (Exception e) { + if(e.getClass() == IOException.class) { + Grasscutter.getLogger().info("Unable to read file 'GameAnnouncementList.json'. \n" + e); + } + } + } else { + response.send("{\"retcode\":404,\"message\":\"Unknown request path\"}"); + } + + if (data.isEmpty()) { + response.send("{\"retcode\":500,\"message\":\"Unable to fetch requsted content\"}"); + return; + } + + String dispatchDomain = "http" + (HTTP_ENCRYPTION.useInRouting ? "s" : "") + "://" + + lr(HTTP_INFO.accessAddress, HTTP_INFO.bindAddress) + ":" + + lr(HTTP_INFO.accessPort, HTTP_INFO.bindPort); + + data = data + .replace("{{DISPATCH_PUBLIC}}", dispatchDomain) + .replace("{{SYSTEM_TIME}}", String.valueOf(System.currentTimeMillis())); + response.send("{\"retcode\":0,\"message\":\"OK\",\"data\": " + data + "}"); + } + + private static void getPageResources(Request request, Response response) { + try(InputStream filestream = DataLoader.load(request.path())) { + String possibleFilename = Utils.toFilePath(DATA(request.path())); + + MediaType fromExtension = MediaType.getByExtension(possibleFilename.substring(possibleFilename.lastIndexOf(".") + 1)); + response.type((fromExtension != null) ? fromExtension.getMIME() : "application/octet-stream"); + response.send(filestream.readAllBytes()); + } catch (Exception e) { + Grasscutter.getLogger().warn("File does not exist: " + request.path()); + response.status(404); + } + } +} diff --git a/src/main/java/emu/grasscutter/server/http/handlers/GachaHandler.java b/src/main/java/emu/grasscutter/server/http/handlers/GachaHandler.java new file mode 100644 index 000000000..3acb5a106 --- /dev/null +++ b/src/main/java/emu/grasscutter/server/http/handlers/GachaHandler.java @@ -0,0 +1,137 @@ +package emu.grasscutter.server.http.handlers; + +import emu.grasscutter.Grasscutter; +import emu.grasscutter.database.DatabaseHelper; +import emu.grasscutter.game.Account; +import emu.grasscutter.game.gacha.GachaBanner; +import emu.grasscutter.game.gacha.GachaManager; +import emu.grasscutter.game.player.Player; +import emu.grasscutter.server.http.Router; +import emu.grasscutter.tools.Tools; +import emu.grasscutter.utils.FileUtils; +import emu.grasscutter.utils.Utils; +import express.Express; +import express.http.Request; +import express.http.Response; +import io.javalin.Javalin; +import io.javalin.http.staticfiles.Location; + +import java.io.File; +import java.nio.charset.StandardCharsets; +import java.util.Arrays; +import java.util.LinkedHashSet; +import java.util.Set; + +import static emu.grasscutter.Configuration.DATA; +import static emu.grasscutter.utils.Language.translate; + +/** + * Handles all gacha-related HTTP requests. + */ +public final class GachaHandler implements Router { + public static final String gachaMappings = DATA(Utils.toFilePath("gacha/mappings.js")); + + @Override public void applyRoutes(Express express, Javalin handle) { + express.get("/gacha", GachaHandler::gachaRecords); + express.get("/gacha/details", GachaHandler::gachaDetails); + + express.useStaticFallback("/gacha/mappings", gachaMappings, Location.EXTERNAL); + } + + private static void gachaRecords(Request request, Response response) { + File recordsTemplate = new File(Utils.toFilePath(DATA("gacha/records.html"))); + if (!recordsTemplate.exists()) { + Grasscutter.getLogger().warn("File does not exist: " + recordsTemplate); + response.status(500); + return; + } + + String sessionKey = request.query("s"); + Account account = DatabaseHelper.getAccountBySessionKey(sessionKey); + if(account == null) { + response.status(403).send("Requested account was not found"); + return; + } + Player player = Grasscutter.getGameServer().getPlayerByAccountId(account.getId()); + if (player == null) { + response.status(403).send("No player associated with requested account"); + return; + } + + int page = 0, gachaType = 0; + if(request.query("p") != null) + page = Integer.parseInt(request.query("p")); + if(request.query("gachaType") != null) + gachaType = Integer.parseInt(request.query("gachaType")); + + String records = DatabaseHelper.getGachaRecords(player.getUid(), page, gachaType).toString(); + long maxPage = DatabaseHelper.getGachaRecordsMaxPage(player.getUid(), page, gachaType); + + String template = new String(FileUtils.read(recordsTemplate), StandardCharsets.UTF_8) + .replace("{{REPLACE_RECORDS}}", records) + .replace("{{REPLACE_MAXPAGE}}", String.valueOf(maxPage)) + .replace("{{LANGUAGE}}", Utils.getLanguageCode(account.getLocale())); + response.send(template); + } + + private static void gachaDetails(Request request, Response response) { + File detailsTemplate = new File(Utils.toFilePath(DATA("gacha/details.html"))); + if (!detailsTemplate.exists()) { + Grasscutter.getLogger().warn("File does not exist: " + detailsTemplate); + response.status(500); + return; + } + + String sessionKey = request.query("s"); + Account account = DatabaseHelper.getAccountBySessionKey(sessionKey); + if(account == null) { + response.status(403).send("Requested account was not found"); + return; + } + Player player = Grasscutter.getGameServer().getPlayerByAccountId(account.getId()); + if (player == null) { + response.status(403).send("No player associated with requested account"); + return; + } + + String template = new String(FileUtils.read(detailsTemplate), StandardCharsets.UTF_8); + + // Add translated title etc. to the page. + template = template.replace("{{TITLE}}", translate(player, "gacha.details.title")) + .replace("{{AVAILABLE_FIVE_STARS}}", translate(player, "gacha.details.available_five_stars")) + .replace("{{AVAILABLE_FOUR_STARS}}", translate(player, "gacha.details.available_four_stars")) + .replace("{{AVAILABLE_THREE_STARS}}", translate(player, "gacha.details.available_three_stars")) + .replace("{{LANGUAGE}}", Utils.getLanguageCode(account.getLocale())); + + // Get the banner info for the banner we want. + int scheduleId = Integer.parseInt(request.query("scheduleId")); + GachaManager manager = Grasscutter.getGameServer().getGachaManager(); + GachaBanner banner = manager.getGachaBanners().get(scheduleId); + + // Add 5-star items. + Set fiveStarItems = new LinkedHashSet<>(); + + Arrays.stream(banner.getRateUpItems5()).forEach(i -> fiveStarItems.add(Integer.toString(i))); + Arrays.stream(banner.getFallbackItems5Pool1()).forEach(i -> fiveStarItems.add(Integer.toString(i))); + Arrays.stream(banner.getFallbackItems5Pool2()).forEach(i -> fiveStarItems.add(Integer.toString(i))); + + template = template.replace("{{FIVE_STARS}}", "[" + String.join(",", fiveStarItems) + "]"); + + // Add 4-star items. + Set fourStarItems = new LinkedHashSet<>(); + + Arrays.stream(banner.getRateUpItems4()).forEach(i -> fourStarItems.add(Integer.toString(i))); + Arrays.stream(banner.getFallbackItems4Pool1()).forEach(i -> fourStarItems.add(Integer.toString(i))); + Arrays.stream(banner.getFallbackItems4Pool2()).forEach(i -> fourStarItems.add(Integer.toString(i))); + + template = template.replace("{{FOUR_STARS}}", "[" + String.join(",", fourStarItems) + "]"); + + // Add 3-star items. + Set threeStarItems = new LinkedHashSet<>(); + Arrays.stream(banner.getFallbackItems3()).forEach(i -> threeStarItems.add(Integer.toString(i))); + template = template.replace("{{THREE_STARS}}", "[" + String.join(",", threeStarItems) + "]"); + + // Done. + response.send(template); + } +} diff --git a/src/main/java/emu/grasscutter/server/http/handlers/GenericHandler.java b/src/main/java/emu/grasscutter/server/http/handlers/GenericHandler.java new file mode 100644 index 000000000..5b3be8d8b --- /dev/null +++ b/src/main/java/emu/grasscutter/server/http/handlers/GenericHandler.java @@ -0,0 +1,59 @@ +package emu.grasscutter.server.http.handlers; + +import emu.grasscutter.GameConstants; +import emu.grasscutter.Grasscutter; +import emu.grasscutter.server.http.objects.HttpJsonResponse; +import emu.grasscutter.server.http.Router; +import emu.grasscutter.server.http.objects.WebStaticVersionResponse; +import express.Express; +import express.http.Request; +import express.http.Response; +import io.javalin.Javalin; + +import static emu.grasscutter.Configuration.ACCOUNT; + +/** + * Handles all generic, hard-coded responses. + */ +public final class GenericHandler implements Router { + @Override public void applyRoutes(Express express, Javalin handle) { + // hk4e-sdk-os.hoyoverse.com + express.get("/hk4e_global/mdk/agreement/api/getAgreementInfos", new HttpJsonResponse("{\"retcode\":0,\"message\":\"OK\",\"data\":{\"marketing_agreements\":[]}}")); + // hk4e-sdk-os.hoyoverse.com + // this could be either GET or POST based on the observation of different clients + express.all("/hk4e_global/combo/granter/api/compareProtocolVersion", new HttpJsonResponse("{\"retcode\":0,\"message\":\"OK\",\"data\":{\"modified\":true,\"protocol\":{\"id\":0,\"app_id\":4,\"language\":\"en\",\"user_proto\":\"\",\"priv_proto\":\"\",\"major\":7,\"minimum\":0,\"create_time\":\"0\",\"teenager_proto\":\"\",\"third_proto\":\"\"}}}")); + + // api-account-os.hoyoverse.com + express.post("/account/risky/api/check", new HttpJsonResponse("{\"retcode\":0,\"message\":\"OK\",\"data\":{\"id\":\"none\",\"action\":\"ACTION_NONE\",\"geetest\":null}}")); + + // sdk-os-static.hoyoverse.com + express.get("/combo/box/api/config/sdk/combo", new HttpJsonResponse("{\"retcode\":0,\"message\":\"OK\",\"data\":{\"vals\":{\"disable_email_bind_skip\":\"false\",\"email_bind_remind_interval\":\"7\",\"email_bind_remind\":\"true\"}}}")); + // hk4e-sdk-os-static.hoyoverse.com + express.get("/hk4e_global/combo/granter/api/getConfig", new HttpJsonResponse("{\"retcode\":0,\"message\":\"OK\",\"data\":{\"protocol\":true,\"qr_enabled\":false,\"log_level\":\"INFO\",\"announce_url\":\"https://webstatic-sea.hoyoverse.com/hk4e/announcement/index.html?sdk_presentation_style=fullscreen\\u0026sdk_screen_transparent=true\\u0026game_biz=hk4e_global\\u0026auth_appid=announcement\\u0026game=hk4e#/\",\"push_alias_type\":2,\"disable_ysdk_guard\":false,\"enable_announce_pic_popup\":true}}")); + // hk4e-sdk-os-static.hoyoverse.com + express.get("/hk4e_global/mdk/shield/api/loadConfig", new HttpJsonResponse("{\"retcode\":0,\"message\":\"OK\",\"data\":{\"id\":6,\"game_key\":\"hk4e_global\",\"client\":\"PC\",\"identity\":\"I_IDENTITY\",\"guest\":false,\"ignore_versions\":\"\",\"scene\":\"S_NORMAL\",\"name\":\"原神海外\",\"disable_regist\":false,\"enable_email_captcha\":false,\"thirdparty\":[\"fb\",\"tw\"],\"disable_mmt\":false,\"server_guest\":false,\"thirdparty_ignore\":{\"tw\":\"\",\"fb\":\"\"},\"enable_ps_bind_account\":false,\"thirdparty_login_configs\":{\"tw\":{\"token_type\":\"TK_GAME_TOKEN\",\"game_token_expires_in\":604800},\"fb\":{\"token_type\":\"TK_GAME_TOKEN\",\"game_token_expires_in\":604800}}}}")); + // Test api? + // abtest-api-data-sg.hoyoverse.com + express.post("/data_abtest_api/config/experiment/list", new HttpJsonResponse("{\"retcode\":0,\"success\":true,\"message\":\"\",\"data\":[{\"code\":1000,\"type\":2,\"config_id\":\"14\",\"period_id\":\"6036_99\",\"version\":\"1\",\"configs\":{\"cardType\":\"old\"}}]}")); + + // log-upload-os.mihoyo.com + express.all("/log/sdk/upload", new HttpJsonResponse("{\"code\":0}")); + express.all("/sdk/upload", new HttpJsonResponse("{\"code\":0}")); + express.post("/sdk/dataUpload", new HttpJsonResponse("{\"code\":0}")); + // /perf/config/verify?device_id=xxx&platform=x&name=xxx + express.all("/perf/config/verify", new HttpJsonResponse("{\"code\":0}")); + + // webstatic-sea.hoyoverse.com + express.get("/admin/mi18n/plat_oversea/*", new WebStaticVersionResponse()); + + express.get("/status/server", GenericHandler::serverStatus); + } + + private static void serverStatus(Request request, Response response) { + int playerCount = Grasscutter.getGameServer().getPlayers().size(); + int maxPlayer = ACCOUNT.maxPlayer; + String version = GameConstants.VERSION; + + response.send("{\"retcode\":0,\"status\":{\"playerCount\":" + playerCount + ",\"maxPlayer\":" + maxPlayer + ",\"version\":\"" + version + "\"}}"); + } +} diff --git a/src/main/java/emu/grasscutter/server/http/handlers/LogHandler.java b/src/main/java/emu/grasscutter/server/http/handlers/LogHandler.java new file mode 100644 index 000000000..08025d365 --- /dev/null +++ b/src/main/java/emu/grasscutter/server/http/handlers/LogHandler.java @@ -0,0 +1,24 @@ +package emu.grasscutter.server.http.handlers; + +import emu.grasscutter.server.http.Router; +import express.Express; +import express.http.Request; +import express.http.Response; +import io.javalin.Javalin; + +/** + * Handles logging requests made to the server. + */ +public final class LogHandler implements Router { + @Override public void applyRoutes(Express express, Javalin handle) { + // overseauspider.yuanshen.com + express.post("/log", LogHandler::log); + // log-upload-os.mihoyo.com + express.post("/crash/dataUpload", LogHandler::log); + } + + private static void log(Request request, Response response) { + // TODO: Figure out how to dump request body and log to file. + response.send("{\"code\":0}"); + } +} diff --git a/src/main/java/emu/grasscutter/server/dispatch/json/ComboTokenReqJson.java b/src/main/java/emu/grasscutter/server/http/objects/ComboTokenReqJson.java similarity index 84% rename from src/main/java/emu/grasscutter/server/dispatch/json/ComboTokenReqJson.java rename to src/main/java/emu/grasscutter/server/http/objects/ComboTokenReqJson.java index b3497f8d4..5642f159a 100644 --- a/src/main/java/emu/grasscutter/server/dispatch/json/ComboTokenReqJson.java +++ b/src/main/java/emu/grasscutter/server/http/objects/ComboTokenReqJson.java @@ -1,4 +1,4 @@ -package emu.grasscutter.server.dispatch.json; +package emu.grasscutter.server.http.objects; public class ComboTokenReqJson { public int app_id; diff --git a/src/main/java/emu/grasscutter/server/dispatch/json/ComboTokenResJson.java b/src/main/java/emu/grasscutter/server/http/objects/ComboTokenResJson.java similarity index 89% rename from src/main/java/emu/grasscutter/server/dispatch/json/ComboTokenResJson.java rename to src/main/java/emu/grasscutter/server/http/objects/ComboTokenResJson.java index 7c49d1278..b592fa163 100644 --- a/src/main/java/emu/grasscutter/server/dispatch/json/ComboTokenResJson.java +++ b/src/main/java/emu/grasscutter/server/http/objects/ComboTokenResJson.java @@ -1,4 +1,4 @@ -package emu.grasscutter.server.dispatch.json; +package emu.grasscutter.server.http.objects; public class ComboTokenResJson { public String message; diff --git a/src/main/java/emu/grasscutter/server/dispatch/DispatchHttpJsonHandler.java b/src/main/java/emu/grasscutter/server/http/objects/HttpJsonResponse.java similarity index 73% rename from src/main/java/emu/grasscutter/server/dispatch/DispatchHttpJsonHandler.java rename to src/main/java/emu/grasscutter/server/http/objects/HttpJsonResponse.java index 54ee38988..35ca9b006 100644 --- a/src/main/java/emu/grasscutter/server/dispatch/DispatchHttpJsonHandler.java +++ b/src/main/java/emu/grasscutter/server/http/objects/HttpJsonResponse.java @@ -1,4 +1,4 @@ -package emu.grasscutter.server.dispatch; +package emu.grasscutter.server.http.objects; import java.io.IOException; import java.util.Arrays; @@ -11,8 +11,9 @@ import express.http.Request; import express.http.Response; import static emu.grasscutter.utils.Language.translate; +import static emu.grasscutter.Configuration.*; -public final class DispatchHttpJsonHandler implements HttpContextHandler { +public final class HttpJsonResponse implements HttpContextHandler { private final String response; private final String[] missingRoutes = { // TODO: When http requests for theses routes are found please remove it from this list and update the route request type in the DispatchServer "/common/hk4e_global/announcement/api/getAlertPic", @@ -27,15 +28,15 @@ public final class DispatchHttpJsonHandler implements HttpContextHandler { "/crash/dataUpload" }; - public DispatchHttpJsonHandler(String response) { + public HttpJsonResponse(String response) { this.response = response; } @Override public void handle(Request req, Response res) throws IOException { // Checking for ALL here isn't required as when ALL is enabled enableDevLogging() gets enabled - if(Grasscutter.getConfig().DebugMode == ServerDebugMode.MISSING && Arrays.stream(missingRoutes).anyMatch(x -> Objects.equals(x, req.baseUrl()))) { - Grasscutter.getLogger().info(translate("messages.dispatch.request", req.ip(), req.method(), req.baseUrl()) + (Grasscutter.getConfig().DebugMode == ServerDebugMode.MISSING ? "(MISSING)" : "")); + if(SERVER.debugLevel == ServerDebugMode.MISSING && Arrays.stream(missingRoutes).anyMatch(x -> Objects.equals(x, req.baseUrl()))) { + Grasscutter.getLogger().info(translate("messages.dispatch.request", req.ip(), req.method(), req.baseUrl()) + (SERVER.debugLevel == ServerDebugMode.MISSING ? "(MISSING)" : "")); } res.send(response); } diff --git a/src/main/java/emu/grasscutter/server/dispatch/json/LoginAccountRequestJson.java b/src/main/java/emu/grasscutter/server/http/objects/LoginAccountRequestJson.java similarity index 71% rename from src/main/java/emu/grasscutter/server/dispatch/json/LoginAccountRequestJson.java rename to src/main/java/emu/grasscutter/server/http/objects/LoginAccountRequestJson.java index cb3aff349..3a8193a97 100644 --- a/src/main/java/emu/grasscutter/server/dispatch/json/LoginAccountRequestJson.java +++ b/src/main/java/emu/grasscutter/server/http/objects/LoginAccountRequestJson.java @@ -1,4 +1,4 @@ -package emu.grasscutter.server.dispatch.json; +package emu.grasscutter.server.http.objects; public class LoginAccountRequestJson { public String account; diff --git a/src/main/java/emu/grasscutter/server/dispatch/json/LoginResultJson.java b/src/main/java/emu/grasscutter/server/http/objects/LoginResultJson.java similarity index 96% rename from src/main/java/emu/grasscutter/server/dispatch/json/LoginResultJson.java rename to src/main/java/emu/grasscutter/server/http/objects/LoginResultJson.java index 1f4dcd4b4..5601c1c29 100644 --- a/src/main/java/emu/grasscutter/server/dispatch/json/LoginResultJson.java +++ b/src/main/java/emu/grasscutter/server/http/objects/LoginResultJson.java @@ -1,4 +1,4 @@ -package emu.grasscutter.server.dispatch.json; +package emu.grasscutter.server.http.objects; public class LoginResultJson { public String message; diff --git a/src/main/java/emu/grasscutter/server/dispatch/json/LoginTokenRequestJson.java b/src/main/java/emu/grasscutter/server/http/objects/LoginTokenRequestJson.java similarity index 64% rename from src/main/java/emu/grasscutter/server/dispatch/json/LoginTokenRequestJson.java rename to src/main/java/emu/grasscutter/server/http/objects/LoginTokenRequestJson.java index 12fed8f09..d01c60401 100644 --- a/src/main/java/emu/grasscutter/server/dispatch/json/LoginTokenRequestJson.java +++ b/src/main/java/emu/grasscutter/server/http/objects/LoginTokenRequestJson.java @@ -1,4 +1,4 @@ -package emu.grasscutter.server.dispatch.json; +package emu.grasscutter.server.http.objects; public class LoginTokenRequestJson { public String uid; diff --git a/src/main/java/emu/grasscutter/server/http/objects/WebStaticVersionResponse.java b/src/main/java/emu/grasscutter/server/http/objects/WebStaticVersionResponse.java new file mode 100644 index 000000000..40a08ebd8 --- /dev/null +++ b/src/main/java/emu/grasscutter/server/http/objects/WebStaticVersionResponse.java @@ -0,0 +1,41 @@ +package emu.grasscutter.server.http.objects; + +import emu.grasscutter.Grasscutter; +import emu.grasscutter.data.DataLoader; +import emu.grasscutter.utils.FileUtils; +import emu.grasscutter.utils.Utils; +import express.http.HttpContextHandler; +import express.http.MediaType; +import express.http.Request; +import express.http.Response; +import io.javalin.core.util.FileUtil; + +import java.io.IOException; +import java.io.InputStream; + +import static emu.grasscutter.Configuration.DATA; + +public class WebStaticVersionResponse implements HttpContextHandler { + + @Override + public void handle(Request request, Response response) throws IOException { + String requestFor = request.path().substring(request.path().lastIndexOf("-") + 1); + + getPageResources("/webstatic/" + requestFor, response); + return; + } + + private static void getPageResources(String path, Response response) { + try(InputStream filestream = FileUtils.readResourceAsStream(path)) { + + MediaType fromExtension = MediaType.getByExtension(path.substring(path.lastIndexOf(".") + 1)); + response.type((fromExtension != null) ? fromExtension.getMIME() : "application/octet-stream"); + response.send(filestream.readAllBytes()); + } catch (Exception e) { + if(Grasscutter.getConfig().server.debugLevel.equals(Grasscutter.ServerDebugMode.MISSING)) { + Grasscutter.getLogger().warn("Webstatic File Missing: " + path); + } + response.status(404); + } + } +} diff --git a/src/main/java/emu/grasscutter/server/packet/recv/HandlerAbilityInvocationsNotify.java b/src/main/java/emu/grasscutter/server/packet/recv/HandlerAbilityInvocationsNotify.java index 710ea0fea..a5d4c7f36 100644 --- a/src/main/java/emu/grasscutter/server/packet/recv/HandlerAbilityInvocationsNotify.java +++ b/src/main/java/emu/grasscutter/server/packet/recv/HandlerAbilityInvocationsNotify.java @@ -6,6 +6,7 @@ import emu.grasscutter.net.proto.AbilityInvocationsNotifyOuterClass.AbilityInvoc import emu.grasscutter.net.proto.AbilityInvokeEntryOuterClass.AbilityInvokeEntry; import emu.grasscutter.net.packet.PacketHandler; import emu.grasscutter.server.game.GameSession; +import emu.grasscutter.utils.Utils; @Opcodes(PacketOpcodes.AbilityInvocationsNotify) public class HandlerAbilityInvocationsNotify extends PacketHandler { @@ -15,13 +16,9 @@ public class HandlerAbilityInvocationsNotify extends PacketHandler { AbilityInvocationsNotify notif = AbilityInvocationsNotify.parseFrom(payload); for (AbilityInvokeEntry entry : notif.getInvokesList()) { - //System.out.println(entry.getArgumentType() + ": " + Utils.bytesToHex(entry.getAbilityData().toByteArray())); + session.getPlayer().getAbilityManager().onAbilityInvoke(entry); session.getPlayer().getAbilityInvokeHandler().addEntry(entry.getForwardType(), entry); } - - if (notif.getInvokesList().size() > 0) { - session.getPlayer().getAbilityInvokeHandler().update(session.getPlayer()); - } } } diff --git a/src/main/java/emu/grasscutter/server/packet/recv/HandlerAvatarFetterLevelRewardReq.java b/src/main/java/emu/grasscutter/server/packet/recv/HandlerAvatarFetterLevelRewardReq.java index fa760a7a8..5ce8d74cf 100644 --- a/src/main/java/emu/grasscutter/server/packet/recv/HandlerAvatarFetterLevelRewardReq.java +++ b/src/main/java/emu/grasscutter/server/packet/recv/HandlerAvatarFetterLevelRewardReq.java @@ -1,7 +1,7 @@ package emu.grasscutter.server.packet.recv; import emu.grasscutter.data.GameData; -import emu.grasscutter.data.def.RewardData; +import emu.grasscutter.data.excels.RewardData; import emu.grasscutter.game.avatar.Avatar; import emu.grasscutter.game.inventory.GameItem; import emu.grasscutter.game.props.ActionReason; diff --git a/src/main/java/emu/grasscutter/server/packet/recv/HandlerBuyGoodsReq.java b/src/main/java/emu/grasscutter/server/packet/recv/HandlerBuyGoodsReq.java index 0a2c30802..7e8a9d8a0 100644 --- a/src/main/java/emu/grasscutter/server/packet/recv/HandlerBuyGoodsReq.java +++ b/src/main/java/emu/grasscutter/server/packet/recv/HandlerBuyGoodsReq.java @@ -18,9 +18,10 @@ import emu.grasscutter.server.packet.send.PacketBuyGoodsRsp; import emu.grasscutter.server.packet.send.PacketStoreItemChangeNotify; import emu.grasscutter.utils.Utils; -import java.util.HashMap; +import java.util.ArrayList; import java.util.List; import java.util.Optional; +import java.util.stream.Stream; @Opcodes(PacketOpcodes.BuyGoodsReq) public class HandlerBuyGoodsReq extends PacketHandler { @@ -33,7 +34,7 @@ public class HandlerBuyGoodsReq extends PacketHandler { return; // Don't trust your users' input - List targetShopGoodsId = buyGoodsReq.getGoodsListList().stream().map(ShopGoodsOuterClass.ShopGoods::getGoodsId).toList(); + List targetShopGoodsId = List.of(buyGoodsReq.getGoods().getGoodsId()); for (int goodsId : targetShopGoodsId) { Optional sg2 = configShop.stream().filter(x -> x.getGoodsId() == goodsId).findFirst(); if (sg2.isEmpty()) @@ -52,46 +53,23 @@ public class HandlerBuyGoodsReq extends PacketHandler { session.getPlayer().save(); } - if ((bought + buyGoodsReq.getBoughtNum() > sg.getBuyLimit()) && sg.getBuyLimit() != 0) { + if ((bought + buyGoodsReq.getBuyCount() > sg.getBuyLimit()) && sg.getBuyLimit() != 0) { return; } - if (sg.getScoin() > 0 && session.getPlayer().getMora() < buyGoodsReq.getBoughtNum() * sg.getScoin()) { - return; - } - if (sg.getHcoin() > 0 && session.getPlayer().getPrimogems() < buyGoodsReq.getBoughtNum() * sg.getHcoin()) { - return; - } - if (sg.getMcoin() > 0 && session.getPlayer().getCrystals() < buyGoodsReq.getBoughtNum() * sg.getMcoin()) { + List costs = new ArrayList(sg.getCostItemList()); // Can this even be null? + costs.add(new ItemParamData(202, sg.getScoin())); + costs.add(new ItemParamData(201, sg.getHcoin())); + costs.add(new ItemParamData(203, sg.getMcoin())); + if (!session.getPlayer().getInventory().payItems(costs.toArray(new ItemParamData[0]), buyGoodsReq.getBuyCount())) { return; } - HashMap itemsCache = new HashMap<>(); - if (sg.getCostItemList() != null) { - for (ItemParamData p : sg.getCostItemList()) { - Optional invItem = session.getPlayer().getInventory().getItems().values().stream().filter(x -> x.getItemId() == p.getId()).findFirst(); - if (invItem.isEmpty() || invItem.get().getCount() < p.getCount()) - return; - itemsCache.put(invItem.get(), p.getCount() * buyGoodsReq.getBoughtNum()); - } - } - - session.getPlayer().setMora(session.getPlayer().getMora() - buyGoodsReq.getBoughtNum() * sg.getScoin()); - session.getPlayer().setPrimogems(session.getPlayer().getPrimogems() - buyGoodsReq.getBoughtNum() * sg.getHcoin()); - session.getPlayer().setCrystals(session.getPlayer().getCrystals() - buyGoodsReq.getBoughtNum() * sg.getMcoin()); - - if (!itemsCache.isEmpty()) { - for (GameItem gi : itemsCache.keySet()) { - session.getPlayer().getInventory().removeItem(gi, itemsCache.get(gi)); - } - itemsCache.clear(); - } - - session.getPlayer().addShopLimit(sg.getGoodsId(), buyGoodsReq.getBoughtNum(), ShopManager.getShopNextRefreshTime(sg)); + session.getPlayer().addShopLimit(sg.getGoodsId(), buyGoodsReq.getBuyCount(), ShopManager.getShopNextRefreshTime(sg)); GameItem item = new GameItem(GameData.getItemDataMap().get(sg.getGoodsItem().getId())); - item.setCount(buyGoodsReq.getBoughtNum() * sg.getGoodsItem().getCount()); + item.setCount(buyGoodsReq.getBuyCount() * sg.getGoodsItem().getCount()); session.getPlayer().getInventory().addItem(item, ActionReason.Shop, true); // fix: not notify when got virtual item from shop - session.send(new PacketBuyGoodsRsp(buyGoodsReq.getShopType(), session.getPlayer().getGoodsLimit(sg.getGoodsId()).getHasBoughtInPeriod(), buyGoodsReq.getGoodsListList().stream().filter(x -> x.getGoodsId() == goodsId).findFirst().get())); + session.send(new PacketBuyGoodsRsp(buyGoodsReq.getShopType(), session.getPlayer().getGoodsLimit(sg.getGoodsId()).getHasBoughtInPeriod(), Stream.of(buyGoodsReq.getGoods()).filter(x -> x.getGoodsId() == goodsId).findFirst().get())); } session.getPlayer().save(); diff --git a/src/main/java/emu/grasscutter/server/packet/recv/HandlerClientAbilityInitFinishNotify.java b/src/main/java/emu/grasscutter/server/packet/recv/HandlerClientAbilityInitFinishNotify.java index cfe697b91..a1035af85 100644 --- a/src/main/java/emu/grasscutter/server/packet/recv/HandlerClientAbilityInitFinishNotify.java +++ b/src/main/java/emu/grasscutter/server/packet/recv/HandlerClientAbilityInitFinishNotify.java @@ -6,6 +6,7 @@ import emu.grasscutter.net.proto.AbilityInvokeEntryOuterClass.AbilityInvokeEntry import emu.grasscutter.net.proto.ClientAbilityInitFinishNotifyOuterClass.ClientAbilityInitFinishNotify; import emu.grasscutter.net.packet.PacketHandler; import emu.grasscutter.server.game.GameSession; +import emu.grasscutter.utils.Utils; @Opcodes(PacketOpcodes.ClientAbilityInitFinishNotify) public class HandlerClientAbilityInitFinishNotify extends PacketHandler { @@ -15,6 +16,7 @@ public class HandlerClientAbilityInitFinishNotify extends PacketHandler { ClientAbilityInitFinishNotify notif = ClientAbilityInitFinishNotify.parseFrom(payload); for (AbilityInvokeEntry entry : notif.getInvokesList()) { + session.getPlayer().getAbilityManager().onAbilityInvoke(entry); session.getPlayer().getClientAbilityInitFinishHandler().addEntry(entry.getForwardType(), entry); } diff --git a/src/main/java/emu/grasscutter/server/packet/recv/HandlerCombatInvocationsNotify.java b/src/main/java/emu/grasscutter/server/packet/recv/HandlerCombatInvocationsNotify.java index 27e4ca6ff..54bdad3b6 100644 --- a/src/main/java/emu/grasscutter/server/packet/recv/HandlerCombatInvocationsNotify.java +++ b/src/main/java/emu/grasscutter/server/packet/recv/HandlerCombatInvocationsNotify.java @@ -1,6 +1,8 @@ package emu.grasscutter.server.packet.recv; +import emu.grasscutter.Grasscutter; import emu.grasscutter.game.entity.GameEntity; +import emu.grasscutter.game.props.FightProperty; import emu.grasscutter.net.packet.Opcodes; import emu.grasscutter.net.packet.PacketOpcodes; import emu.grasscutter.net.proto.CombatInvocationsNotifyOuterClass.CombatInvocationsNotify; @@ -8,27 +10,63 @@ import emu.grasscutter.net.proto.CombatInvokeEntryOuterClass.CombatInvokeEntry; import emu.grasscutter.net.proto.EntityMoveInfoOuterClass.EntityMoveInfo; import emu.grasscutter.net.proto.EvtBeingHitInfoOuterClass.EvtBeingHitInfo; import emu.grasscutter.net.packet.PacketHandler; +import emu.grasscutter.net.proto.MotionInfoOuterClass.MotionInfo; +import emu.grasscutter.net.proto.MotionStateOuterClass.MotionState; +import emu.grasscutter.net.proto.PlayerDieTypeOuterClass; import emu.grasscutter.server.game.GameSession; +import emu.grasscutter.server.packet.send.PacketEntityFightPropUpdateNotify; @Opcodes(PacketOpcodes.CombatInvocationsNotify) public class HandlerCombatInvocationsNotify extends PacketHandler { + private float cachedLandingSpeed = 0; + private long cachedLandingTimeMillisecond = 0; + private boolean monitorLandingEvent = false; + @Override public void handle(GameSession session, byte[] header, byte[] payload) throws Exception { CombatInvocationsNotify notif = CombatInvocationsNotify.parseFrom(payload); for (CombatInvokeEntry entry : notif.getInvokeListList()) { switch (entry.getArgumentType()) { - case COMBAT_EVT_BEING_HIT: + case COMBAT_TYPE_ARGUMENT_EVT_BEING_HIT: // Handle damage EvtBeingHitInfo hitInfo = EvtBeingHitInfo.parseFrom(entry.getCombatData()); session.getPlayer().getAttackResults().add(hitInfo.getAttackResult()); + session.getPlayer().getEnergyManager().handleAttackHit(hitInfo); break; - case ENTITY_MOVE: + case COMBAT_TYPE_ARGUMENT_ENTITY_MOVE: // Handle movement EntityMoveInfo moveInfo = EntityMoveInfo.parseFrom(entry.getCombatData()); GameEntity entity = session.getPlayer().getScene().getEntityById(moveInfo.getEntityId()); if (entity != null) { - session.getPlayer().getMovementManager().handle(session, moveInfo, entity); + // Move player + MotionInfo motionInfo = moveInfo.getMotionInfo(); + entity.getPosition().set(motionInfo.getPos()); + entity.getRotation().set(motionInfo.getRot()); + entity.setLastMoveSceneTimeMs(moveInfo.getSceneTime()); + entity.setLastMoveReliableSeq(moveInfo.getReliableSeq()); + MotionState motionState = motionInfo.getState(); + entity.setMotionState(motionState); + + session.getPlayer().getStaminaManager().handleCombatInvocationsNotify(session, moveInfo, entity); + + // TODO: handle MOTION_FIGHT landing which has a different damage factor + // Also, for plunge attacks, LAND_SPEED is always -30 and is not useful. + // May need the height when starting plunge attack. + + // MOTION_LAND_SPEED and MOTION_FALL_ON_GROUND arrive in different packets. + // Cache land speed for later use. + if (motionState == MotionState.MOTION_STATE_LAND_SPEED) { + cachedLandingSpeed = motionInfo.getSpeed().getY(); + cachedLandingTimeMillisecond = System.currentTimeMillis(); + monitorLandingEvent = true; + } + if (monitorLandingEvent) { + if (motionState == MotionState.MOTION_STATE_FALL_ON_GROUND) { + monitorLandingEvent = false; + handleFallOnGround(session, entity, motionState); + } + } } break; default: @@ -37,15 +75,53 @@ public class HandlerCombatInvocationsNotify extends PacketHandler { session.getPlayer().getCombatInvokeHandler().addEntry(entry.getForwardType(), entry); } - - if (notif.getInvokeListList().size() > 0) { - session.getPlayer().getCombatInvokeHandler().update(session.getPlayer()); - } - // Handle attack results last - while (!session.getPlayer().getAttackResults().isEmpty()) { - session.getPlayer().getScene().handleAttack(session.getPlayer().getAttackResults().poll()); - } } - + private void handleFallOnGround(GameSession session, GameEntity entity, MotionState motionState) { + if (session.getPlayer().inGodmode()) { + return; + } + // People have reported that after plunge attack (client sends a FIGHT instead of FALL_ON_GROUND) they will die + // if they talk to an NPC (this is when the client sends a FALL_ON_GROUND) without jumping again. + // A dirty patch: if not received immediately after MOTION_LAND_SPEED, discard this packet. + // 200ms seems to be a reasonable delay. + int maxDelay = 200; + long actualDelay = System.currentTimeMillis() - cachedLandingTimeMillisecond; + Grasscutter.getLogger().trace("MOTION_FALL_ON_GROUND received after " + actualDelay + "/" + maxDelay + "ms." + (actualDelay > maxDelay ? " Discard" : "")); + if (actualDelay > maxDelay) { + return; + } + float currentHP = entity.getFightProperty(FightProperty.FIGHT_PROP_CUR_HP); + float maxHP = entity.getFightProperty(FightProperty.FIGHT_PROP_MAX_HP); + float damageFactor = 0; + if (cachedLandingSpeed < -23.5) { + damageFactor = 0.33f; + } + if (cachedLandingSpeed < -25) { + damageFactor = 0.5f; + } + if (cachedLandingSpeed < -26.5) { + damageFactor = 0.66f; + } + if (cachedLandingSpeed < -28) { + damageFactor = 1f; + } + float damage = maxHP * damageFactor; + float newHP = currentHP - damage; + if (newHP < 0) { + newHP = 0; + } + if (damageFactor > 0) { + Grasscutter.getLogger().debug(currentHP + "/" + maxHP + "\tLandingSpeed: " + cachedLandingSpeed + + "\tDamageFactor: " + damageFactor + "\tDamage: " + damage + "\tNewHP: " + newHP); + } else { + Grasscutter.getLogger().trace(currentHP + "/" + maxHP + "\tLandingSpeed: 0\tNo damage"); + } + entity.setFightProperty(FightProperty.FIGHT_PROP_CUR_HP, newHP); + entity.getWorld().broadcastPacket(new PacketEntityFightPropUpdateNotify(entity, FightProperty.FIGHT_PROP_CUR_HP)); + if (newHP == 0) { + session.getPlayer().getStaminaManager().killAvatar(session, entity, PlayerDieTypeOuterClass.PlayerDieType.PLAYER_DIE_TYPE_FALL); + } + cachedLandingSpeed = 0; + } } diff --git a/src/main/java/emu/grasscutter/server/packet/recv/HandlerCombineReq.java b/src/main/java/emu/grasscutter/server/packet/recv/HandlerCombineReq.java index 23a522b25..c5b52fab0 100644 --- a/src/main/java/emu/grasscutter/server/packet/recv/HandlerCombineReq.java +++ b/src/main/java/emu/grasscutter/server/packet/recv/HandlerCombineReq.java @@ -1,6 +1,7 @@ package emu.grasscutter.server.packet.recv; -import emu.grasscutter.data.def.CombineData; +import emu.grasscutter.data.common.ItemParamData; +import emu.grasscutter.data.excels.CombineData; import emu.grasscutter.net.packet.Opcodes; import emu.grasscutter.net.packet.PacketHandler; import emu.grasscutter.net.packet.PacketOpcodes; @@ -35,7 +36,7 @@ public class HandlerCombineReq extends PacketHandler { toItemParamList(result.getBack()))); } - private List toItemParamList(List list){ + private List toItemParamList(List list){ return list.stream() .map(item -> ItemParamOuterClass.ItemParam.newBuilder() .setItemId(item.getId()) diff --git a/src/main/java/emu/grasscutter/server/packet/recv/HandlerCreateVehicleReq.java b/src/main/java/emu/grasscutter/server/packet/recv/HandlerCreateVehicleReq.java new file mode 100644 index 000000000..367d46677 --- /dev/null +++ b/src/main/java/emu/grasscutter/server/packet/recv/HandlerCreateVehicleReq.java @@ -0,0 +1,21 @@ +package emu.grasscutter.server.packet.recv; + +import emu.grasscutter.net.packet.Opcodes; +import emu.grasscutter.net.packet.PacketHandler; +import emu.grasscutter.net.packet.PacketOpcodes; +import emu.grasscutter.net.proto.CreateVehicleReqOuterClass; + +import emu.grasscutter.server.game.GameSession; +import emu.grasscutter.server.packet.send.PacketCreateVehicleRsp; + +import emu.grasscutter.utils.Position; + +@Opcodes(PacketOpcodes.CreateVehicleReq) +public class HandlerCreateVehicleReq extends PacketHandler { + + @Override + public void handle(GameSession session, byte[] header, byte[] payload) throws Exception { + CreateVehicleReqOuterClass.CreateVehicleReq req = CreateVehicleReqOuterClass.CreateVehicleReq.parseFrom(payload); + session.send(new PacketCreateVehicleRsp(session.getPlayer(), req.getVehicleId(), req.getScenePointId(), new Position(req.getPos()), new Position(req.getRot()))); + } +} diff --git a/src/main/java/emu/grasscutter/server/packet/recv/HandlerDoGachaReq.java b/src/main/java/emu/grasscutter/server/packet/recv/HandlerDoGachaReq.java index 0aa680a18..1c06dbbfa 100644 --- a/src/main/java/emu/grasscutter/server/packet/recv/HandlerDoGachaReq.java +++ b/src/main/java/emu/grasscutter/server/packet/recv/HandlerDoGachaReq.java @@ -12,6 +12,6 @@ public class HandlerDoGachaReq extends PacketHandler { public void handle(GameSession session, byte[] header, byte[] payload) throws Exception { DoGachaReq req = DoGachaReq.parseFrom(payload); - session.getServer().getGachaManager().doPulls(session.getPlayer(), req.getGachaType(), req.getGachaTimes()); + session.getServer().getGachaManager().doPulls(session.getPlayer(), req.getGachaScheduleId(), req.getGachaTimes()); } } diff --git a/src/main/java/emu/grasscutter/server/packet/recv/HandlerEnterTransPointRegionNotify.java b/src/main/java/emu/grasscutter/server/packet/recv/HandlerEnterTransPointRegionNotify.java index 5591607fe..9a5872033 100644 --- a/src/main/java/emu/grasscutter/server/packet/recv/HandlerEnterTransPointRegionNotify.java +++ b/src/main/java/emu/grasscutter/server/packet/recv/HandlerEnterTransPointRegionNotify.java @@ -1,31 +1,16 @@ package emu.grasscutter.server.packet.recv; -import emu.grasscutter.game.managers.SotSManager.SotSManager; +import emu.grasscutter.game.managers.SotSManager; import emu.grasscutter.game.player.Player; -import emu.grasscutter.game.props.FightProperty; import emu.grasscutter.net.packet.Opcodes; import emu.grasscutter.net.packet.PacketHandler; import emu.grasscutter.net.packet.PacketOpcodes; -import emu.grasscutter.net.proto.ChangeHpReasonOuterClass.ChangeHpReason; -import emu.grasscutter.net.proto.PropChangeReasonOuterClass.PropChangeReason; import emu.grasscutter.server.game.GameSession; -import emu.grasscutter.server.packet.send.PacketAvatarFightPropUpdateNotify; -import emu.grasscutter.server.packet.send.PacketAvatarLifeStateChangeNotify; -import emu.grasscutter.server.packet.send.PacketEntityFightPropChangeReasonNotify; -import emu.grasscutter.server.packet.send.PacketEntityFightPropUpdateNotify; - -import java.util.List; @Opcodes(PacketOpcodes.EnterTransPointRegionNotify) public class HandlerEnterTransPointRegionNotify extends PacketHandler { @Override public void handle(GameSession session, byte[] header, byte[] payload) throws Exception{ - Player player = session.getPlayer(); - SotSManager sotsManager = player.getSotSManager(); - - sotsManager.refillSpringVolume(); - sotsManager.autoRevive(session); - sotsManager.scheduleAutoRecover(session); - // TODO: allow interaction with the SotS? + session.getPlayer().getSotSManager().handleEnterTransPointRegionNotify(); } } diff --git a/src/main/java/emu/grasscutter/server/packet/recv/HandlerEvtCreateGadgetNotify.java b/src/main/java/emu/grasscutter/server/packet/recv/HandlerEvtCreateGadgetNotify.java index 9b1cdb0fb..92229d400 100644 --- a/src/main/java/emu/grasscutter/server/packet/recv/HandlerEvtCreateGadgetNotify.java +++ b/src/main/java/emu/grasscutter/server/packet/recv/HandlerEvtCreateGadgetNotify.java @@ -14,11 +14,6 @@ public class HandlerEvtCreateGadgetNotify extends PacketHandler { public void handle(GameSession session, byte[] header, byte[] payload) throws Exception { EvtCreateGadgetNotify notify = EvtCreateGadgetNotify.parseFrom(payload); - // Dont handle in singleplayer - if (!session.getPlayer().getWorld().isMultiplayer()) { - return; - } - // Sanity check - dont add duplicate entities if (session.getPlayer().getScene().getEntityById(notify.getEntityId()) != null) { return; diff --git a/src/main/java/emu/grasscutter/server/packet/recv/HandlerEvtDestroyGadgetNotify.java b/src/main/java/emu/grasscutter/server/packet/recv/HandlerEvtDestroyGadgetNotify.java index 608215d0a..7d1abe8ea 100644 --- a/src/main/java/emu/grasscutter/server/packet/recv/HandlerEvtDestroyGadgetNotify.java +++ b/src/main/java/emu/grasscutter/server/packet/recv/HandlerEvtDestroyGadgetNotify.java @@ -12,11 +12,6 @@ public class HandlerEvtDestroyGadgetNotify extends PacketHandler { @Override public void handle(GameSession session, byte[] header, byte[] payload) throws Exception { EvtDestroyGadgetNotify notify = EvtDestroyGadgetNotify.parseFrom(payload); - - // Dont handle in singleplayer - if (!session.getPlayer().getWorld().isMultiplayer()) { - return; - } session.getPlayer().getScene().onPlayerDestroyGadget(notify.getEntityId()); } diff --git a/src/main/java/emu/grasscutter/server/packet/recv/HandlerEvtDoSkillSuccNotify.java b/src/main/java/emu/grasscutter/server/packet/recv/HandlerEvtDoSkillSuccNotify.java index a57ae9665..a714d936b 100644 --- a/src/main/java/emu/grasscutter/server/packet/recv/HandlerEvtDoSkillSuccNotify.java +++ b/src/main/java/emu/grasscutter/server/packet/recv/HandlerEvtDoSkillSuccNotify.java @@ -1,6 +1,5 @@ package emu.grasscutter.server.packet.recv; -import emu.grasscutter.Grasscutter; import emu.grasscutter.net.packet.Opcodes; import emu.grasscutter.net.packet.PacketHandler; import emu.grasscutter.net.packet.PacketOpcodes; @@ -13,12 +12,10 @@ public class HandlerEvtDoSkillSuccNotify extends PacketHandler { @Override public void handle(GameSession session, byte[] header, byte[] payload) throws Exception { EvtDoSkillSuccNotify notify = EvtDoSkillSuccNotify.parseFrom(payload); - // TODO: Will be used for deducting stamina for charged skills. - - int caster = notify.getCasterId(); int skillId = notify.getSkillId(); + int casterId = notify.getCasterId(); - session.getPlayer().getMovementManager().notifySkill(caster, skillId); + session.getPlayer().getStaminaManager().handleEvtDoSkillSuccNotify(session, skillId, casterId); + session.getPlayer().getEnergyManager().handleEvtDoSkillSuccNotify(session, skillId, casterId); } - } diff --git a/src/main/java/emu/grasscutter/server/packet/recv/HandlerExitTransPointRegionNotify.java b/src/main/java/emu/grasscutter/server/packet/recv/HandlerExitTransPointRegionNotify.java index 35ec957cb..03fcd4e3c 100644 --- a/src/main/java/emu/grasscutter/server/packet/recv/HandlerExitTransPointRegionNotify.java +++ b/src/main/java/emu/grasscutter/server/packet/recv/HandlerExitTransPointRegionNotify.java @@ -1,6 +1,7 @@ package emu.grasscutter.server.packet.recv; -import emu.grasscutter.game.managers.SotSManager.SotSManager; +import emu.grasscutter.Grasscutter; +import emu.grasscutter.game.managers.SotSManager; import emu.grasscutter.game.player.Player; import emu.grasscutter.net.packet.Opcodes; import emu.grasscutter.net.packet.PacketHandler; @@ -11,8 +12,6 @@ import emu.grasscutter.server.game.GameSession; public class HandlerExitTransPointRegionNotify extends PacketHandler { @Override public void handle(GameSession session, byte[] header, byte[] payload) throws Exception{ - Player player = session.getPlayer(); - SotSManager sotsManager = player.getSotSManager(); - sotsManager.cancelAutoRecover(); + session.getPlayer().getSotSManager().handleExitTransPointRegionNotify(); } } diff --git a/src/main/java/emu/grasscutter/server/packet/recv/HandlerGetAllMailReq.java b/src/main/java/emu/grasscutter/server/packet/recv/HandlerGetAllMailReq.java index 899ba6d95..2a7fdc295 100644 --- a/src/main/java/emu/grasscutter/server/packet/recv/HandlerGetAllMailReq.java +++ b/src/main/java/emu/grasscutter/server/packet/recv/HandlerGetAllMailReq.java @@ -16,6 +16,6 @@ public class HandlerGetAllMailReq extends PacketHandler { @Override public void handle(GameSession session, byte[] header, byte[] payload) throws Exception { GetAllMailReqOuterClass.GetAllMailReq req = GetAllMailReqOuterClass.GetAllMailReq.parseFrom(payload); - session.send(new PacketGetAllMailRsp(session.getPlayer(), req.getIsGiftMail())); + session.send(new PacketGetAllMailRsp(session.getPlayer(), req.getANKKGPJCINB())); } } diff --git a/src/main/java/emu/grasscutter/server/packet/recv/HandlerGetPlayerTokenReq.java b/src/main/java/emu/grasscutter/server/packet/recv/HandlerGetPlayerTokenReq.java index 5034fb01a..6546b6f1b 100644 --- a/src/main/java/emu/grasscutter/server/packet/recv/HandlerGetPlayerTokenReq.java +++ b/src/main/java/emu/grasscutter/server/packet/recv/HandlerGetPlayerTokenReq.java @@ -1,11 +1,17 @@ package emu.grasscutter.server.packet.recv; +import static emu.grasscutter.Configuration.ACCOUNT; + +import emu.grasscutter.Grasscutter; import emu.grasscutter.database.DatabaseHelper; import emu.grasscutter.game.Account; +import emu.grasscutter.game.player.Player; import emu.grasscutter.net.packet.Opcodes; import emu.grasscutter.net.packet.PacketOpcodes; import emu.grasscutter.net.proto.GetPlayerTokenReqOuterClass.GetPlayerTokenReq; import emu.grasscutter.net.packet.PacketHandler; +import emu.grasscutter.server.event.game.PlayerCreationEvent; +import emu.grasscutter.server.game.GameServer; import emu.grasscutter.server.game.GameSession; import emu.grasscutter.server.game.GameSession.SessionState; import emu.grasscutter.server.packet.send.PacketGetPlayerTokenRsp; @@ -15,42 +21,73 @@ public class HandlerGetPlayerTokenReq extends PacketHandler { @Override public void handle(GameSession session, byte[] header, byte[] payload) throws Exception { + GetPlayerTokenReq req = GetPlayerTokenReq.parseFrom(payload); // Authenticate Account account = DatabaseHelper.getAccountById(req.getAccountUid()); - if (account == null) { - return; - } - - // Check token - if (!account.getToken().equals(req.getAccountToken())) { + if (account == null || !account.getToken().equals(req.getAccountToken())) { return; } // Set account session.setAccount(account); - session.setUseSecretKey(true); - session.setState(SessionState.WAITING_FOR_LOGIN); - - // Has character - boolean doesPlayerExist = false; - if (account.getPlayerUid() > 0) { - // Set flag for player existing - doesPlayerExist = DatabaseHelper.checkPlayerExists(account.getPlayerUid()); - } - - // Set reserve player id if account doesnt exist - if (!doesPlayerExist) { - int id = DatabaseHelper.getNextPlayerId(session.getAccount().getPlayerUid()); - if (id != session.getAccount().getPlayerUid()) { - session.getAccount().setPlayerId(id); - session.getAccount().save(); + + + // Check if player object exists in server + // NOTE: CHECKING MUST SITUATED HERE (BEFORE getPlayerByUid)! because to save firstly ,to load secondly !!! + // TODO - optimize + boolean kicked = false; + Player exists = Grasscutter.getGameServer().getPlayerByAccountId(account.getId()); + if (exists != null) { + GameSession existsSession = exists.getSession(); + if (existsSession != session) {// No self-kicking + exists.onLogout();//must save immediately , or the below will load old data + existsSession.close(); + Grasscutter.getLogger().warn("Player {} was kicked due to duplicated login", account.getUsername()); + kicked = true; } } + //NOTE: If there are 5 online players, max count of player is 5, + // a new client want to login by kicking one of them , + // I think it should be allowed + if(!kicked) { + // Max players limit + if (ACCOUNT.maxPlayer > -1 && Grasscutter.getGameServer().getPlayers().size() >= ACCOUNT.maxPlayer) { + session.close(); + return; + } + } + + // Get player + Player player = DatabaseHelper.getPlayerByAccount(account); + + if (player == null) { + int nextPlayerUid = DatabaseHelper.getNextPlayerId(session.getAccount().getReservedPlayerUid()); + + // Call creation event. + PlayerCreationEvent event = new PlayerCreationEvent(session, Player.class); event.call(); + + // Create player instance from event. + player = event.getPlayerClass().getDeclaredConstructor(GameSession.class).newInstance(session); + + // Save to db + DatabaseHelper.generatePlayerUid(player, nextPlayerUid); + } + + // Set player object for session + session.setPlayer(player); + + // Load player from database + player.loadFromDatabase(); + + // Set session state + session.setUseSecretKey(true); + session.setState(SessionState.WAITING_FOR_LOGIN); + // Send packet - session.send(new PacketGetPlayerTokenRsp(session, doesPlayerExist)); + session.send(new PacketGetPlayerTokenRsp(session)); } } diff --git a/src/main/java/emu/grasscutter/server/packet/recv/HandlerGetWidgetSlotReq.java b/src/main/java/emu/grasscutter/server/packet/recv/HandlerGetWidgetSlotReq.java index b41a6cc1d..e7b352122 100644 --- a/src/main/java/emu/grasscutter/server/packet/recv/HandlerGetWidgetSlotReq.java +++ b/src/main/java/emu/grasscutter/server/packet/recv/HandlerGetWidgetSlotReq.java @@ -1,16 +1,20 @@ package emu.grasscutter.server.packet.recv; +import emu.grasscutter.game.player.Player; import emu.grasscutter.net.packet.Opcodes; import emu.grasscutter.net.packet.PacketOpcodes; import emu.grasscutter.net.packet.PacketHandler; import emu.grasscutter.server.game.GameSession; +import emu.grasscutter.server.packet.send.PacketGetShopRsp; +import emu.grasscutter.server.packet.send.PacketGetWidgetSlotRsp; @Opcodes(PacketOpcodes.GetWidgetSlotReq) public class HandlerGetWidgetSlotReq extends PacketHandler { @Override public void handle(GameSession session, byte[] header, byte[] payload) throws Exception { - // Unhandled + Player player = session.getPlayer(); + session.send(new PacketGetWidgetSlotRsp(player)); } } diff --git a/src/main/java/emu/grasscutter/server/packet/recv/HandlerHitTreeNotify.java b/src/main/java/emu/grasscutter/server/packet/recv/HandlerHitTreeNotify.java new file mode 100644 index 000000000..222116d56 --- /dev/null +++ b/src/main/java/emu/grasscutter/server/packet/recv/HandlerHitTreeNotify.java @@ -0,0 +1,21 @@ +package emu.grasscutter.server.packet.recv; + +import java.math.BigInteger; + +import emu.grasscutter.net.packet.Opcodes; +import emu.grasscutter.net.packet.PacketHandler; +import emu.grasscutter.net.packet.PacketOpcodes; +import emu.grasscutter.net.proto.HitTreeNotifyOuterClass.HitTreeNotify; +import emu.grasscutter.server.game.GameSession; + +/** + * Implement Deforestation Function + */ +@Opcodes(PacketOpcodes.HitTreeNotify) +public class HandlerHitTreeNotify extends PacketHandler { + @Override + public void handle(GameSession session, byte[] header, byte[] payload) throws Exception { + HitTreeNotify hit = HitTreeNotify.parseFrom(payload); + session.getPlayer().getDeforestationManager().onDeforestationInvoke(hit); + } +} \ No newline at end of file diff --git a/src/main/java/emu/grasscutter/server/packet/recv/HandlerHomeChooseModuleReq.java b/src/main/java/emu/grasscutter/server/packet/recv/HandlerHomeChooseModuleReq.java new file mode 100644 index 000000000..5a7c0dbe5 --- /dev/null +++ b/src/main/java/emu/grasscutter/server/packet/recv/HandlerHomeChooseModuleReq.java @@ -0,0 +1,26 @@ +package emu.grasscutter.server.packet.recv; + +import emu.grasscutter.net.packet.Opcodes; +import emu.grasscutter.net.packet.PacketHandler; +import emu.grasscutter.net.packet.PacketOpcodes; +import emu.grasscutter.net.proto.HomeChooseModuleReqOuterClass; +import emu.grasscutter.server.game.GameSession; +import emu.grasscutter.server.packet.send.PacketHomeChooseModuleRsp; +import emu.grasscutter.server.packet.send.PacketHomeComfortInfoNotify; +import emu.grasscutter.server.packet.send.PacketPlayerHomeCompInfoNotify; + + +@Opcodes(PacketOpcodes.HomeChooseModuleReq) +public class HandlerHomeChooseModuleReq extends PacketHandler { + + @Override + public void handle(GameSession session, byte[] header, byte[] payload) throws Exception { + HomeChooseModuleReqOuterClass.HomeChooseModuleReq req = + HomeChooseModuleReqOuterClass.HomeChooseModuleReq.parseFrom(payload); + session.getPlayer().addRealmList(req.getModuleId()); + session.getPlayer().setCurrentRealmId(req.getModuleId()); + session.send(new PacketHomeChooseModuleRsp(req.getModuleId())); + session.send(new PacketPlayerHomeCompInfoNotify(session.getPlayer())); + session.send(new PacketHomeComfortInfoNotify(session.getPlayer())); + } +} diff --git a/src/main/java/emu/grasscutter/server/packet/recv/HandlerMarkMapReq.java b/src/main/java/emu/grasscutter/server/packet/recv/HandlerMarkMapReq.java index fc5d0a602..58348a2e0 100644 --- a/src/main/java/emu/grasscutter/server/packet/recv/HandlerMarkMapReq.java +++ b/src/main/java/emu/grasscutter/server/packet/recv/HandlerMarkMapReq.java @@ -1,84 +1,17 @@ package emu.grasscutter.server.packet.recv; -import emu.grasscutter.game.managers.MapMarkManager.MapMark; -import emu.grasscutter.game.managers.MapMarkManager.MapMarksManager; -import emu.grasscutter.game.player.Player; import emu.grasscutter.net.packet.Opcodes; import emu.grasscutter.net.packet.PacketHandler; import emu.grasscutter.net.packet.PacketOpcodes; -import emu.grasscutter.net.proto.*; import emu.grasscutter.net.proto.MarkMapReqOuterClass.MarkMapReq; import emu.grasscutter.server.game.GameSession; -import emu.grasscutter.server.packet.send.PacketMarkMapRsp; -import emu.grasscutter.server.packet.send.PacketMarkNewNotify; -import emu.grasscutter.server.packet.send.PacketSceneEntityAppearNotify; -import emu.grasscutter.utils.Position; - -import java.util.ArrayList; -import java.util.HashMap; -import java.util.Map; @Opcodes(PacketOpcodes.MarkMapReq) public class HandlerMarkMapReq extends PacketHandler { - private static boolean isInt(String str) { - - try { - @SuppressWarnings("unused") - int x = Integer.parseInt(str); - return true; // String is an Integer - } catch (NumberFormatException e) { - return false; // String is not an Integer - } - - } - @Override public void handle(GameSession session, byte[] header, byte[] payload) throws Exception { MarkMapReq req = MarkMapReq.parseFrom(payload); - MarkMapReq.Operation op = req.getOp(); - Player player = session.getPlayer(); - MapMarksManager mapMarksManager = player.getMapMarksManager(); - if (op == MarkMapReq.Operation.ADD) { - MapMark newMapMark = new MapMark(req.getMark()); - // keep teleporting functionality on fishhook mark. - if (newMapMark.getMapMarkPointType() == MapMarkPointTypeOuterClass.MapMarkPointType.MAP_MARK_POINT_TYPE_FISH_POOL) { - teleport(player, newMapMark); - return; - } - if (mapMarksManager.addMapMark(newMapMark)) { - player.save(); - } - } else if (op == MarkMapReq.Operation.MOD) { - MapMark newMapMark = new MapMark(req.getMark()); - if (mapMarksManager.removeMapMark(newMapMark.getPosition())) { - if (mapMarksManager.addMapMark(newMapMark)) { - player.save(); - } - } - } else if (op == MarkMapReq.Operation.DEL) { - MapMark newMapMark = new MapMark(req.getMark()); - if (mapMarksManager.removeMapMark(newMapMark.getPosition())) { - player.save(); - } - } else if (op == MarkMapReq.Operation.GET) { - // no-op - } - // send all marks to refresh client map view. - HashMap mapMarks = mapMarksManager.getAllMapMarks(); - session.send(new PacketMarkMapRsp(player, mapMarks)); - } - - private void teleport(Player player, MapMark mapMark) { - float y = isInt(mapMark.getName()) ? Integer.parseInt(mapMark.getName()) : 300; - float x = mapMark.getPosition().getX(); - float z = mapMark.getPosition().getZ(); - player.getPos().set(x, y, z); - if (mapMark.getSceneId() != player.getSceneId()) { - player.getWorld().transferPlayerToScene(player, mapMark.getSceneId(), - player.getPos()); - } else { - player.getScene().broadcastPacket(new PacketSceneEntityAppearNotify(player)); - } + session.getPlayer().getMapMarksManager().handleMapMarkReq(req); } } diff --git a/src/main/java/emu/grasscutter/server/packet/recv/HandlerMcoinExchangeHcoinReq.java b/src/main/java/emu/grasscutter/server/packet/recv/HandlerMcoinExchangeHcoinReq.java index 37dc0fcc7..21acbfe21 100644 --- a/src/main/java/emu/grasscutter/server/packet/recv/HandlerMcoinExchangeHcoinReq.java +++ b/src/main/java/emu/grasscutter/server/packet/recv/HandlerMcoinExchangeHcoinReq.java @@ -15,13 +15,13 @@ public class HandlerMcoinExchangeHcoinReq extends PacketHandler { public void handle(GameSession session, byte[] header, byte[] payload) throws Exception { McoinExchangeHcoinReqOuterClass.McoinExchangeHcoinReq exchangeReq = McoinExchangeHcoinReqOuterClass.McoinExchangeHcoinReq.parseFrom(payload); - if (session.getPlayer().getCrystals() < exchangeReq.getMCoinNum() && exchangeReq.getMCoinNum() == exchangeReq.getHCoinNum()) { + if (session.getPlayer().getCrystals() < exchangeReq.getMcoinCost() && exchangeReq.getMcoinCost() == exchangeReq.getHcoin()) { session.send(new PacketMcoinExchangeHcoinRsp(RetcodeOuterClass.Retcode.RET_UNKNOWN_ERROR_VALUE)); return; } - session.getPlayer().setCrystals(session.getPlayer().getCrystals() - exchangeReq.getMCoinNum()); - session.getPlayer().setPrimogems(session.getPlayer().getPrimogems() + exchangeReq.getHCoinNum()); + session.getPlayer().setCrystals(session.getPlayer().getCrystals() - exchangeReq.getMcoinCost()); + session.getPlayer().setPrimogems(session.getPlayer().getPrimogems() + exchangeReq.getHcoin()); session.getPlayer().save(); session.send(new PacketMcoinExchangeHcoinRsp(RetcodeOuterClass.Retcode.RET_SUCC_VALUE)); } diff --git a/src/main/java/emu/grasscutter/server/packet/recv/HandlerNpcTalkReq.java b/src/main/java/emu/grasscutter/server/packet/recv/HandlerNpcTalkReq.java index 309d7e2e2..3dae7fe10 100644 --- a/src/main/java/emu/grasscutter/server/packet/recv/HandlerNpcTalkReq.java +++ b/src/main/java/emu/grasscutter/server/packet/recv/HandlerNpcTalkReq.java @@ -1,6 +1,7 @@ package emu.grasscutter.server.packet.recv; import emu.grasscutter.game.inventory.GameItem; +import emu.grasscutter.game.quest.enums.QuestTrigger; import emu.grasscutter.net.packet.Opcodes; import emu.grasscutter.net.packet.PacketOpcodes; import emu.grasscutter.net.proto.NpcTalkReqOuterClass.NpcTalkReq; @@ -14,6 +15,10 @@ public class HandlerNpcTalkReq extends PacketHandler { @Override public void handle(GameSession session, byte[] header, byte[] payload) throws Exception { NpcTalkReq req = NpcTalkReq.parseFrom(payload); + + // Why are there 2 quest triggers that do the same thing... + session.getPlayer().getQuestManager().triggerEvent(QuestTrigger.QUEST_CONTENT_COMPLETE_TALK, req.getTalkId()); + session.getPlayer().getQuestManager().triggerEvent(QuestTrigger.QUEST_CONTENT_FINISH_PLOT, req.getTalkId()); session.send(new PacketNpcTalkRsp(req.getNpcEntityId(), req.getTalkId(), req.getEntityId())); } diff --git a/src/main/java/emu/grasscutter/server/packet/recv/HandlerPersonalSceneJumpReq.java b/src/main/java/emu/grasscutter/server/packet/recv/HandlerPersonalSceneJumpReq.java index 98c6984ee..246dcbaa5 100644 --- a/src/main/java/emu/grasscutter/server/packet/recv/HandlerPersonalSceneJumpReq.java +++ b/src/main/java/emu/grasscutter/server/packet/recv/HandlerPersonalSceneJumpReq.java @@ -1,7 +1,7 @@ package emu.grasscutter.server.packet.recv; import emu.grasscutter.data.GameData; -import emu.grasscutter.data.custom.ScenePointEntry; +import emu.grasscutter.data.binout.ScenePointEntry; import emu.grasscutter.net.packet.Opcodes; import emu.grasscutter.net.packet.PacketHandler; import emu.grasscutter.net.packet.PacketOpcodes; diff --git a/src/main/java/emu/grasscutter/server/packet/recv/HandlerPlayerLoginReq.java b/src/main/java/emu/grasscutter/server/packet/recv/HandlerPlayerLoginReq.java index cfdce5b59..d5aea0db1 100644 --- a/src/main/java/emu/grasscutter/server/packet/recv/HandlerPlayerLoginReq.java +++ b/src/main/java/emu/grasscutter/server/packet/recv/HandlerPlayerLoginReq.java @@ -1,5 +1,6 @@ package emu.grasscutter.server.packet.recv; +import emu.grasscutter.Grasscutter; import emu.grasscutter.database.DatabaseHelper; import emu.grasscutter.game.player.Player; import emu.grasscutter.net.packet.BasePacket; @@ -12,6 +13,8 @@ import emu.grasscutter.server.game.GameSession.SessionState; import emu.grasscutter.server.packet.send.PacketPlayerLoginRsp; import emu.grasscutter.server.packet.send.PacketTakeAchievementRewardReq; +import static emu.grasscutter.Configuration.ACCOUNT; + @Opcodes(PacketOpcodes.PlayerLoginReq) // Sends initial data packets public class HandlerPlayerLoginReq extends PacketHandler { @@ -19,31 +22,30 @@ public class HandlerPlayerLoginReq extends PacketHandler { public void handle(GameSession session, byte[] header, byte[] payload) throws Exception { // Check if (session.getAccount() == null) { + session.close(); return; } - + // Parse request PlayerLoginReq req = PlayerLoginReq.parseFrom(payload); // Authenticate session if (!req.getToken().equals(session.getAccount().getToken())) { + session.close(); return; } // Load character from db - Player player = DatabaseHelper.getPlayerById(session.getAccount().getPlayerUid()); + Player player = session.getPlayer(); - if (player == null) { - // Send packets + // Show opening cutscene if player has no avatars + if (player.getAvatars().getAvatarCount() == 0) { + // Pick character session.setState(SessionState.PICKING_CHARACTER); session.send(new BasePacket(PacketOpcodes.DoSetPlayerBornDataNotify)); } else { - // Set character - session.setPlayer(player); - // Login done session.getPlayer().onLogin(); - session.setState(SessionState.ACTIVE); } // Final packet to tell client logging in is done diff --git a/src/main/java/emu/grasscutter/server/packet/recv/HandlerQueryCodexMonsterBeKilledNumReq.java b/src/main/java/emu/grasscutter/server/packet/recv/HandlerQueryCodexMonsterBeKilledNumReq.java new file mode 100644 index 000000000..dca38dcdb --- /dev/null +++ b/src/main/java/emu/grasscutter/server/packet/recv/HandlerQueryCodexMonsterBeKilledNumReq.java @@ -0,0 +1,20 @@ +package emu.grasscutter.server.packet.recv; + +import emu.grasscutter.net.packet.Opcodes; +import emu.grasscutter.net.packet.PacketOpcodes; +import emu.grasscutter.net.packet.PacketHandler; +import emu.grasscutter.net.proto.QueryCodexMonsterBeKilledNumReqOuterClass; +import emu.grasscutter.net.proto.QueryCodexMonsterBeKilledNumReqOuterClass.QueryCodexMonsterBeKilledNumReq; +import emu.grasscutter.server.game.GameSession; +import emu.grasscutter.server.packet.send.PacketQueryCodexMonsterBeKilledNumRsp; + +@Opcodes(PacketOpcodes.QueryCodexMonsterBeKilledNumReq) +public class HandlerQueryCodexMonsterBeKilledNumReq extends PacketHandler { + + @Override + public void handle(GameSession session, byte[] header, byte[] payload) throws Exception { + QueryCodexMonsterBeKilledNumReq req = QueryCodexMonsterBeKilledNumReq.parseFrom(payload); + session.send(new PacketQueryCodexMonsterBeKilledNumRsp(session.getPlayer(), req.getCodexIdListList())); + } + +} diff --git a/src/main/java/emu/grasscutter/server/packet/recv/HandlerQuickUseWidgetReq.java b/src/main/java/emu/grasscutter/server/packet/recv/HandlerQuickUseWidgetReq.java new file mode 100644 index 000000000..6e0149649 --- /dev/null +++ b/src/main/java/emu/grasscutter/server/packet/recv/HandlerQuickUseWidgetReq.java @@ -0,0 +1,54 @@ +package emu.grasscutter.server.packet.recv; + +import emu.grasscutter.Grasscutter; +import emu.grasscutter.game.inventory.GameItem; +import emu.grasscutter.game.inventory.Inventory; +import emu.grasscutter.game.inventory.InventoryTab; +import emu.grasscutter.game.inventory.ItemType; +import emu.grasscutter.game.player.Player; +import emu.grasscutter.net.packet.BasePacket; +import emu.grasscutter.net.packet.Opcodes; +import emu.grasscutter.net.packet.PacketHandler; +import emu.grasscutter.net.packet.PacketOpcodes; +import emu.grasscutter.net.proto.QuickUseWidgetRspOuterClass.QuickUseWidgetRsp; +import emu.grasscutter.server.game.GameSession; +import emu.grasscutter.server.packet.send.PacketPullRecentChatRsp; + +@Opcodes(PacketOpcodes.QuickUseWidgetReq) +public class HandlerQuickUseWidgetReq extends PacketHandler { + /* + * WARNING: with the consuming of material widget ( Example: bomb ), + * this is just a implement designed to the decreasing of count + * + * ### Known Bug: No effects after using item but decrease. ### + * + * If you know which Packet could make the effects, feel free to contribute! + * */ + @Override + public void handle(GameSession session, byte[] header, byte[] payload) throws Exception { + QuickUseWidgetRsp.Builder proto = QuickUseWidgetRsp.newBuilder(); + Player pl = session.getPlayer(); + synchronized (pl) { + int materialId = pl.getWidgetId(); + Inventory inventory = session.getPlayer().getInventory(); + InventoryTab inventoryTab = inventory.getInventoryTab(ItemType.ITEM_MATERIAL); + GameItem item = inventoryTab.getItemById(materialId); + int remain = item.getCount(); + if (remain > 0) { + remain--; + if (remain <= 0) { + proto.setRetcode(1); + } else { + proto.setRetcode(0); + } + proto.setMaterialId(materialId); + inventory.removeItem(item,1);// decrease count + BasePacket rsp = new BasePacket(PacketOpcodes.QuickUseWidgetRsp); + rsp.setData(proto); + session.send(rsp); + Grasscutter.getLogger().warn("class has no effects in the game, feel free to implement it"); + // but no effects in the game, feel free to implement it! + } + } + } +} diff --git a/src/main/java/emu/grasscutter/server/packet/recv/HandlerSceneInitFinishReq.java b/src/main/java/emu/grasscutter/server/packet/recv/HandlerSceneInitFinishReq.java index 587cf7ce1..9d7dc8443 100644 --- a/src/main/java/emu/grasscutter/server/packet/recv/HandlerSceneInitFinishReq.java +++ b/src/main/java/emu/grasscutter/server/packet/recv/HandlerSceneInitFinishReq.java @@ -9,12 +9,12 @@ import emu.grasscutter.server.game.GameSession; import emu.grasscutter.server.packet.send.PacketHostPlayerNotify; import emu.grasscutter.server.packet.send.PacketPlayerEnterSceneInfoNotify; import emu.grasscutter.server.packet.send.PacketPlayerGameTimeNotify; +import emu.grasscutter.server.packet.send.PacketPlayerWorldSceneInfoListNotify; import emu.grasscutter.server.packet.send.PacketSceneAreaWeatherNotify; import emu.grasscutter.server.packet.send.PacketSceneInitFinishRsp; import emu.grasscutter.server.packet.send.PacketScenePlayerInfoNotify; import emu.grasscutter.server.packet.send.PacketSceneTeamUpdateNotify; import emu.grasscutter.server.packet.send.PacketSceneTimeNotify; -import emu.grasscutter.server.packet.send.PacketSceneUnlockInfoNotify; import emu.grasscutter.server.packet.send.PacketServerTimeNotify; import emu.grasscutter.server.packet.send.PacketSyncScenePlayTeamEntityNotify; import emu.grasscutter.server.packet.send.PacketSyncTeamEntityNotify; @@ -30,7 +30,7 @@ public class HandlerSceneInitFinishReq extends PacketHandler { session.send(new PacketServerTimeNotify()); session.send(new PacketWorldPlayerInfoNotify(session.getPlayer().getWorld())); session.send(new PacketWorldDataNotify(session.getPlayer().getWorld())); - session.send(new PacketSceneUnlockInfoNotify()); + session.send(new PacketPlayerWorldSceneInfoListNotify()); session.send(new BasePacket(PacketOpcodes.SceneForceUnlockNotify)); session.send(new PacketHostPlayerNotify(session.getPlayer().getWorld())); diff --git a/src/main/java/emu/grasscutter/server/packet/recv/HandlerSceneTransToPointReq.java b/src/main/java/emu/grasscutter/server/packet/recv/HandlerSceneTransToPointReq.java index 65783d01b..209dacdbb 100644 --- a/src/main/java/emu/grasscutter/server/packet/recv/HandlerSceneTransToPointReq.java +++ b/src/main/java/emu/grasscutter/server/packet/recv/HandlerSceneTransToPointReq.java @@ -1,7 +1,7 @@ package emu.grasscutter.server.packet.recv; import emu.grasscutter.data.GameData; -import emu.grasscutter.data.custom.ScenePointEntry; +import emu.grasscutter.data.binout.ScenePointEntry; import emu.grasscutter.net.packet.Opcodes; import emu.grasscutter.net.packet.PacketOpcodes; import emu.grasscutter.net.proto.SceneTransToPointReqOuterClass.SceneTransToPointReq; diff --git a/src/main/java/emu/grasscutter/server/packet/recv/HandlerSetEntityClientDataNotify.java b/src/main/java/emu/grasscutter/server/packet/recv/HandlerSetEntityClientDataNotify.java index 5151034f2..6c4d86f7e 100644 --- a/src/main/java/emu/grasscutter/server/packet/recv/HandlerSetEntityClientDataNotify.java +++ b/src/main/java/emu/grasscutter/server/packet/recv/HandlerSetEntityClientDataNotify.java @@ -23,7 +23,7 @@ public class HandlerSetEntityClientDataNotify extends PacketHandler { BasePacket packet = new BasePacket(PacketOpcodes.SetEntityClientDataNotify, true); packet.setData(notif); - session.getPlayer().getScene().broadcastPacketToOthers(session.getPlayer(), packet); + session.getPlayer().getScene().broadcastPacket(packet); } } diff --git a/src/main/java/emu/grasscutter/server/packet/recv/HandlerSetPlayerBornDataReq.java b/src/main/java/emu/grasscutter/server/packet/recv/HandlerSetPlayerBornDataReq.java index 2487df063..ddb2e8948 100644 --- a/src/main/java/emu/grasscutter/server/packet/recv/HandlerSetPlayerBornDataReq.java +++ b/src/main/java/emu/grasscutter/server/packet/recv/HandlerSetPlayerBornDataReq.java @@ -19,6 +19,8 @@ import emu.grasscutter.server.game.GameSession.SessionState; import java.util.Arrays; +import static emu.grasscutter.Configuration.*; + @Opcodes(PacketOpcodes.SetPlayerBornDataReq) public class HandlerSetPlayerBornDataReq extends PacketHandler { @@ -44,58 +46,39 @@ public class HandlerSetPlayerBornDataReq extends PacketHandler { return; } - String nickname = req.getNickName(); - if (nickname == null) { - nickname = "Traveler"; - } - - // Call creation event. - PlayerCreationEvent event = new PlayerCreationEvent(session, Player.class); event.call(); - // Create player instance from event. - Player player = event.getPlayerClass().getDeclaredConstructor(GameSession.class).newInstance(session); - player.setNickname(nickname); - - try { - // Save to db - DatabaseHelper.createPlayer(player, session.getAccount().getPlayerUid()); - - // Create avatar - if (player.getAvatars().getAvatarCount() == 0) { - Avatar mainCharacter = new Avatar(avatarId); - mainCharacter.setSkillDepot(GameData.getAvatarSkillDepotDataMap().get(startingSkillDepot)); - player.addAvatar(mainCharacter); - player.setMainCharacterId(avatarId); - player.setHeadImage(avatarId); - player.getTeamManager().getCurrentSinglePlayerTeamInfo().getAvatars().add(mainCharacter.getAvatarId()); - player.save(); // TODO save player team in different object - } - - // Save account - session.getAccount().setPlayerId(player.getUid()); - session.getAccount().save(); - - // Set character - session.setPlayer(player); - - // Login done - session.getPlayer().onLogin(); - session.setState(SessionState.ACTIVE); - - // Born resp packet - session.send(new BasePacket(PacketOpcodes.SetPlayerBornDataRsp)); + // Get player object + Player player = session.getPlayer(); + player.setNickname(req.getNickName()); - // Default mail - MailBuilder mailBuilder = new MailBuilder(player.getUid(), new Mail()); - mailBuilder.mail.mailContent.title = Grasscutter.getConfig().GameServer.WelcomeMailTitle; - mailBuilder.mail.mailContent.sender = Grasscutter.getConfig().GameServer.WelcomeMailSender; - // Please credit Grasscutter if changing something here. We don't condone commercial use of the project. - mailBuilder.mail.mailContent.content = Grasscutter.getConfig().GameServer.WelcomeMailContent + "\n"; - mailBuilder.mail.itemList.addAll(Arrays.asList(Grasscutter.getConfig().GameServer.WelcomeMailItems)); - mailBuilder.mail.importance = 1; - player.sendMail(mailBuilder.mail); - } catch (Exception e) { - Grasscutter.getLogger().error("Error creating player object: ", e); - session.close(); + // Create avatar + if (player.getAvatars().getAvatarCount() == 0) { + Avatar mainCharacter = new Avatar(avatarId); + mainCharacter.setSkillDepotData(GameData.getAvatarSkillDepotDataMap().get(startingSkillDepot)); + // Manually handle adding to team + player.addAvatar(mainCharacter, false); + player.setMainCharacterId(avatarId); + player.setHeadImage(avatarId); + player.getTeamManager().getCurrentSinglePlayerTeamInfo().getAvatars().add(mainCharacter.getAvatarId()); + player.save(); // TODO save player team in different object + } else { + return; } + + // Login done + session.getPlayer().onLogin(); + + // Born resp packet + session.send(new BasePacket(PacketOpcodes.SetPlayerBornDataRsp)); + + // Default mail + var welcomeMail = GAME_INFO.joinOptions.welcomeMail; + MailBuilder mailBuilder = new MailBuilder(player.getUid(), new Mail()); + mailBuilder.mail.mailContent.title = welcomeMail.title; + mailBuilder.mail.mailContent.sender = welcomeMail.sender; + // Please credit Grasscutter if changing something here. We don't condone commercial use of the project. + mailBuilder.mail.mailContent.content = welcomeMail.content + "\n"; + mailBuilder.mail.itemList.addAll(Arrays.asList(welcomeMail.items)); + mailBuilder.mail.importance = 1; + player.sendMail(mailBuilder.mail); } } diff --git a/src/main/java/emu/grasscutter/server/packet/recv/HandlerSetWidgetSlotReq.java b/src/main/java/emu/grasscutter/server/packet/recv/HandlerSetWidgetSlotReq.java new file mode 100644 index 000000000..9b478ff23 --- /dev/null +++ b/src/main/java/emu/grasscutter/server/packet/recv/HandlerSetWidgetSlotReq.java @@ -0,0 +1,37 @@ +package emu.grasscutter.server.packet.recv; + +import emu.grasscutter.game.player.Player; +import emu.grasscutter.net.packet.Opcodes; +import emu.grasscutter.net.packet.PacketHandler; +import emu.grasscutter.net.packet.PacketOpcodes; +import emu.grasscutter.net.proto.SetWidgetSlotReqOuterClass; +import emu.grasscutter.net.proto.WidgetSlotOpOuterClass; +import emu.grasscutter.server.game.GameSession; +import emu.grasscutter.server.packet.send.PacketSetWidgetSlotRsp; +import emu.grasscutter.server.packet.send.PacketWidgetSlotChangeNotify; + +@Opcodes(PacketOpcodes.SetWidgetSlotReq) +public class HandlerSetWidgetSlotReq extends PacketHandler { + + @Override + public void handle(GameSession session, byte[] header, byte[] payload) throws Exception { + SetWidgetSlotReqOuterClass.SetWidgetSlotReq req = SetWidgetSlotReqOuterClass.SetWidgetSlotReq.parseFrom(payload); + + Player player = session.getPlayer(); + player.setWidgetId(req.getMaterialId()); + + // WidgetSlotChangeNotify op & slot key + session.send(new PacketWidgetSlotChangeNotify(WidgetSlotOpOuterClass.WidgetSlotOp.WIDGET_SLOT_OP_DETACH)); + + //only attaching the widget can set it + if(req.getOp() == WidgetSlotOpOuterClass.WidgetSlotOp.WIDGET_SLOT_OP_ATTACH){ + // WidgetSlotChangeNotify slot + session.send(new PacketWidgetSlotChangeNotify(req.getMaterialId())); + } + + // SetWidgetSlotRsp + session.send(new PacketSetWidgetSlotRsp(req.getMaterialId())); + } + +} + diff --git a/src/main/java/emu/grasscutter/server/packet/recv/HandlerTakePlayerLevelRewardReq.java b/src/main/java/emu/grasscutter/server/packet/recv/HandlerTakePlayerLevelRewardReq.java index 8ee2d934b..8701a7479 100644 --- a/src/main/java/emu/grasscutter/server/packet/recv/HandlerTakePlayerLevelRewardReq.java +++ b/src/main/java/emu/grasscutter/server/packet/recv/HandlerTakePlayerLevelRewardReq.java @@ -1,48 +1,40 @@ package emu.grasscutter.server.packet.recv; -import java.util.LinkedList; import java.util.List; import java.util.Set; -import emu.grasscutter.Grasscutter; import emu.grasscutter.data.GameData; -import emu.grasscutter.data.common.RewardItemData; -import emu.grasscutter.game.inventory.GameItem; +import emu.grasscutter.data.common.ItemParamData; + +import emu.grasscutter.game.player.Player; import emu.grasscutter.game.props.ActionReason; import emu.grasscutter.net.packet.Opcodes; import emu.grasscutter.net.packet.PacketHandler; import emu.grasscutter.net.packet.PacketOpcodes; import emu.grasscutter.net.proto.TakePlayerLevelRewardReqOuterClass.TakePlayerLevelRewardReq; import emu.grasscutter.server.game.GameSession; -import emu.grasscutter.server.packet.send.PacketItemAddHintNotify; import emu.grasscutter.server.packet.send.PacketTakePlayerLevelRewardRsp; @Opcodes(PacketOpcodes.TakePlayerLevelRewardReq) public class HandlerTakePlayerLevelRewardReq extends PacketHandler { @Override public void handle(GameSession session, byte[] header, byte[] payload) throws Exception { - TakePlayerLevelRewardReq req = TakePlayerLevelRewardReq.parseFrom(payload); - - int level = req.getLevel(); - int rewardId = GameData.getPlayerLevelDataMap().get(level).getRewardId(); - - if (rewardId != 0) { - List rewardItems = GameData.getRewardDataMap().get(rewardId).getRewardItemList(); - List items = new LinkedList<>(); - for (RewardItemData rewardItem : rewardItems) { - if (rewardItem != null) { - if (rewardItem.getItemId() != 0) { - items.add(new GameItem(rewardItem.getItemId(), rewardItem.getItemCount())); - } + Player pl = session.getPlayer(); + synchronized (pl) { + TakePlayerLevelRewardReq req = TakePlayerLevelRewardReq.parseFrom(payload); + int level = req.getLevel(); + Set rewardedLevels = session.getPlayer().getRewardedLevels(); + if (!rewardedLevels.contains(level)) {// No duplicated reward + int rewardId = GameData.getPlayerLevelDataMap().get(level).getRewardId(); + if (rewardId != 0) { + List rewardItems = GameData.getRewardDataMap().get(rewardId).getRewardItemList(); + pl.getInventory().addItemParamDatas(rewardItems, ActionReason.PlayerUpgradeReward); + rewardedLevels.add(level); + pl.setRewardedLevels(rewardedLevels); + pl.save(); + session.send(new PacketTakePlayerLevelRewardRsp(level, rewardId)); } } - session.getPlayer().getInventory().addItems(items); - session.getPlayer().sendPacket(new PacketItemAddHintNotify(items, ActionReason.PlayerUpgradeReward)); - Set rewardedLevels = session.getPlayer().getRewardedLevels(); - rewardedLevels.add(level); - session.getPlayer().setRewardedLevels(rewardedLevels); } - - session.send(new PacketTakePlayerLevelRewardRsp(level, rewardId)); } } diff --git a/src/main/java/emu/grasscutter/server/packet/recv/HandlerTowerAllDataReq.java b/src/main/java/emu/grasscutter/server/packet/recv/HandlerTowerAllDataReq.java index 2a9ef2004..38462882f 100644 --- a/src/main/java/emu/grasscutter/server/packet/recv/HandlerTowerAllDataReq.java +++ b/src/main/java/emu/grasscutter/server/packet/recv/HandlerTowerAllDataReq.java @@ -11,7 +11,10 @@ public class HandlerTowerAllDataReq extends PacketHandler { @Override public void handle(GameSession session, byte[] header, byte[] payload) throws Exception { - session.send(new PacketTowerAllDataRsp()); + session.send(new PacketTowerAllDataRsp( + session.getServer().getTowerScheduleManager(), + session.getPlayer().getTowerManager() + )); } } diff --git a/src/main/java/emu/grasscutter/server/packet/recv/HandlerTryEnterHomeReq.java b/src/main/java/emu/grasscutter/server/packet/recv/HandlerTryEnterHomeReq.java new file mode 100644 index 000000000..5df106df2 --- /dev/null +++ b/src/main/java/emu/grasscutter/server/packet/recv/HandlerTryEnterHomeReq.java @@ -0,0 +1,42 @@ +package emu.grasscutter.server.packet.recv; + +import emu.grasscutter.data.GameData; +import emu.grasscutter.game.world.Scene; +import emu.grasscutter.net.packet.Opcodes; +import emu.grasscutter.net.packet.PacketHandler; +import emu.grasscutter.net.packet.PacketOpcodes; +import emu.grasscutter.net.proto.TryEnterHomeReqOuterClass; +import emu.grasscutter.scripts.data.SceneConfig; +import emu.grasscutter.server.game.GameSession; +import emu.grasscutter.server.packet.send.PacketTryEnterHomeRsp; +import emu.grasscutter.utils.Position; + +@Opcodes(PacketOpcodes.TryEnterHomeReq) +public class HandlerTryEnterHomeReq extends PacketHandler { + + @Override + public void handle(GameSession session, byte[] header, byte[] payload) throws Exception { + TryEnterHomeReqOuterClass.TryEnterHomeReq req = + TryEnterHomeReqOuterClass.TryEnterHomeReq.parseFrom(payload); + + if (req.getTargetUid() != session.getPlayer().getUid()) { + // I hope that tomorrow there will be a hero who can support multiplayer mode and write code like a poem + session.send(new PacketTryEnterHomeRsp()); + return; + } + + int realmId = 2000 + session.getPlayer().getCurrentRealmId(); + + Scene scene = session.getPlayer().getWorld().getSceneById(realmId); + Position pos = scene.getScriptManager().getConfig().born_pos; + + session.getPlayer().getWorld().transferPlayerToScene( + session.getPlayer(), + realmId, + pos + ); + + + session.send(new PacketTryEnterHomeRsp(req.getTargetUid())); + } +} diff --git a/src/main/java/emu/grasscutter/server/packet/recv/HandlerUnionCmdNotify.java b/src/main/java/emu/grasscutter/server/packet/recv/HandlerUnionCmdNotify.java index 1f4a9e7f3..2468f675f 100644 --- a/src/main/java/emu/grasscutter/server/packet/recv/HandlerUnionCmdNotify.java +++ b/src/main/java/emu/grasscutter/server/packet/recv/HandlerUnionCmdNotify.java @@ -15,5 +15,14 @@ public class HandlerUnionCmdNotify extends PacketHandler { for (UnionCmd cmd : req.getCmdListList()) { session.getServer().getPacketHandler().handle(session, cmd.getMessageId(), EMPTY_BYTE_ARRAY, cmd.getBody().toByteArray()); } + + // Update + session.getPlayer().getCombatInvokeHandler().update(session.getPlayer()); + session.getPlayer().getAbilityInvokeHandler().update(session.getPlayer()); + + // Handle attack results last + while (!session.getPlayer().getAttackResults().isEmpty()) { + session.getPlayer().getScene().handleAttack(session.getPlayer().getAttackResults().poll()); + } } } diff --git a/src/main/java/emu/grasscutter/server/packet/recv/HandlerVehicleInteractReq.java b/src/main/java/emu/grasscutter/server/packet/recv/HandlerVehicleInteractReq.java index 3baba9c5b..d45befa89 100644 --- a/src/main/java/emu/grasscutter/server/packet/recv/HandlerVehicleInteractReq.java +++ b/src/main/java/emu/grasscutter/server/packet/recv/HandlerVehicleInteractReq.java @@ -14,6 +14,7 @@ public class HandlerVehicleInteractReq extends PacketHandler { @Override public void handle(GameSession session, byte[] header, byte[] payload) throws Exception { VehicleInteractReqOuterClass.VehicleInteractReq req = VehicleInteractReqOuterClass.VehicleInteractReq.parseFrom(payload); + session.getPlayer().getStaminaManager().handleVehicleInteractReq(session, req.getEntityId(), req.getInteractType()); session.send(new PacketVehicleInteractRsp(session.getPlayer(), req.getEntityId(), req.getInteractType())); } } diff --git a/src/main/java/emu/grasscutter/server/packet/recv/HandlerVehicleSpawnReq.java b/src/main/java/emu/grasscutter/server/packet/recv/HandlerVehicleSpawnReq.java deleted file mode 100644 index 2d259f738..000000000 --- a/src/main/java/emu/grasscutter/server/packet/recv/HandlerVehicleSpawnReq.java +++ /dev/null @@ -1,21 +0,0 @@ -package emu.grasscutter.server.packet.recv; - -import emu.grasscutter.net.packet.Opcodes; -import emu.grasscutter.net.packet.PacketHandler; -import emu.grasscutter.net.packet.PacketOpcodes; -import emu.grasscutter.net.proto.VehicleSpawnReqOuterClass; - -import emu.grasscutter.server.game.GameSession; -import emu.grasscutter.server.packet.send.PacketVehicleSpawnRsp; - -import emu.grasscutter.utils.Position; - -@Opcodes(PacketOpcodes.VehicleSpawnReq) -public class HandlerVehicleSpawnReq extends PacketHandler { - - @Override - public void handle(GameSession session, byte[] header, byte[] payload) throws Exception { - VehicleSpawnReqOuterClass.VehicleSpawnReq req = VehicleSpawnReqOuterClass.VehicleSpawnReq.parseFrom(payload); - session.send(new PacketVehicleSpawnRsp(session.getPlayer(), req.getVehicleId(), req.getPointId(), new Position(req.getPos()), new Position(req.getRot()))); - } -} diff --git a/src/main/java/emu/grasscutter/server/packet/recv/HandlerWidgetDoBagReq.java b/src/main/java/emu/grasscutter/server/packet/recv/HandlerWidgetDoBagReq.java new file mode 100644 index 000000000..c73f98fbd --- /dev/null +++ b/src/main/java/emu/grasscutter/server/packet/recv/HandlerWidgetDoBagReq.java @@ -0,0 +1,57 @@ +package emu.grasscutter.server.packet.recv; + +import emu.grasscutter.Grasscutter; +import emu.grasscutter.data.GameData; +import emu.grasscutter.data.excels.GadgetData; +import emu.grasscutter.game.entity.EntityVehicle; +import emu.grasscutter.game.entity.GameEntity; +import emu.grasscutter.game.props.LifeState; +import emu.grasscutter.net.packet.Opcodes; +import emu.grasscutter.net.packet.PacketHandler; +import emu.grasscutter.net.packet.PacketOpcodes; +import emu.grasscutter.net.proto.*; +import emu.grasscutter.server.game.GameSession; +import emu.grasscutter.server.packet.send.PacketSceneEntityAppearNotify; +import emu.grasscutter.server.packet.send.PacketWidgetCoolDownNotify; +import emu.grasscutter.server.packet.send.PacketWidgetDoBagRsp; +import emu.grasscutter.server.packet.send.PacketWidgetGadgetDataNotify; +import emu.grasscutter.utils.Position; + +import java.util.List; + +@Opcodes(PacketOpcodes.WidgetDoBagReq) +public class HandlerWidgetDoBagReq extends PacketHandler { + + @Override + public void handle(GameSession session, byte[] header, byte[] payload) throws Exception { + WidgetDoBagReqOuterClass.WidgetDoBagReq req = WidgetDoBagReqOuterClass.WidgetDoBagReq.parseFrom(payload); + switch (req.getMaterialId()) { + case 220026 -> { + GadgetData gadgetData = GameData.getGadgetDataMap().get(70500025); + Position pos = new Position(req.getWidgetCreatorInfo().getLocationInfo().getPos()); + Position rot = new Position(req.getWidgetCreatorInfo().getLocationInfo().getRot()); + GameEntity entity = new EntityVehicle( + session.getPlayer().getScene(), + session.getPlayer(), + gadgetData.getId(), + 0, + pos, + rot + ); + + session.getPlayer().getScene().addEntity(entity); + + session.send(new PacketWidgetGadgetDataNotify(70500025, List.of(entity.getId()))); // ??? + session.send(new PacketWidgetCoolDownNotify(15, System.currentTimeMillis() + 5000L, true)); + session.send(new PacketWidgetCoolDownNotify(15, System.currentTimeMillis() + 5000L, true)); + // Send twice, and I don't know why, Ask mhy + session.send(new PacketWidgetDoBagRsp()); + } + default -> { + session.send(new PacketWidgetDoBagRsp()); + } + + } + } + +} diff --git a/src/main/java/emu/grasscutter/server/packet/send/PacketAllWidgetDataNotify.java b/src/main/java/emu/grasscutter/server/packet/send/PacketAllWidgetDataNotify.java new file mode 100644 index 000000000..8d3d42261 --- /dev/null +++ b/src/main/java/emu/grasscutter/server/packet/send/PacketAllWidgetDataNotify.java @@ -0,0 +1,59 @@ +package emu.grasscutter.server.packet.send; + +import emu.grasscutter.game.player.Player; +import emu.grasscutter.net.packet.BasePacket; +import emu.grasscutter.net.packet.PacketOpcodes; +import emu.grasscutter.net.proto.AllWidgetDataNotifyOuterClass.AllWidgetDataNotify; +import emu.grasscutter.net.proto.LunchBoxDataOuterClass; +import emu.grasscutter.net.proto.WidgetSlotDataOuterClass; +import emu.grasscutter.net.proto.WidgetSlotTagOuterClass; + +import java.util.List; +import java.util.Map; + +public class PacketAllWidgetDataNotify extends BasePacket { + + public PacketAllWidgetDataNotify(Player player) { + super(PacketOpcodes.AllWidgetDataNotify); + + // TODO: Implement this + + AllWidgetDataNotify.Builder proto = AllWidgetDataNotify.newBuilder() + // If you want to implement this, feel free to do so. :) + .setLunchBoxData( + LunchBoxDataOuterClass.LunchBoxData.newBuilder().build() + ) + // Maybe it's a little difficult, or it makes you upset :( + .addAllOneoffGatherPointDetectorDataList(List.of()) + // So, goodbye, and hopefully sometime in the future o(* ̄▽ ̄*)ブ + .addAllCoolDownGroupDataList(List.of()) + // I'll see your PR with a title that says (・∀・(・∀・(・∀・*) + .addAllAnchorPointList(List.of()) + // "Complete implementation of widget functionality" b( ̄▽ ̄)d  + .addAllClientCollectorDataList(List.of()) + // Good luck, my boy. + .addAllNormalCoolDownDataList(List.of()); + + if (player.getWidgetId() == null) { + proto.addAllSlotList(List.of()); + } else { + proto.addSlotList( + WidgetSlotDataOuterClass.WidgetSlotData.newBuilder() + .setIsActive(true) + .setMaterialId(player.getWidgetId()) + .build() + ); + + proto.addSlotList( + WidgetSlotDataOuterClass.WidgetSlotData.newBuilder() + .setTag(WidgetSlotTagOuterClass.WidgetSlotTag.WIDGET_SLOT_TAG_ATTACH_AVATAR) + .build() + ); + } + + AllWidgetDataNotify protoData = proto.build(); + + this.setData(protoData); + } +} + diff --git a/src/main/java/emu/grasscutter/server/packet/send/PacketBuyGoodsRsp.java b/src/main/java/emu/grasscutter/server/packet/send/PacketBuyGoodsRsp.java index 07371440d..2d9a5f597 100644 --- a/src/main/java/emu/grasscutter/server/packet/send/PacketBuyGoodsRsp.java +++ b/src/main/java/emu/grasscutter/server/packet/send/PacketBuyGoodsRsp.java @@ -11,7 +11,7 @@ public class PacketBuyGoodsRsp extends BasePacket { BuyGoodsRspOuterClass.BuyGoodsRsp buyGoodsRsp = BuyGoodsRspOuterClass.BuyGoodsRsp.newBuilder() .setShopType(shopType) - .setBoughtNum(boughtNum) + .setBuyCount(boughtNum) .addGoodsList(ShopGoodsOuterClass.ShopGoods.newBuilder() .mergeFrom(sg) .setBoughtNum(boughtNum) diff --git a/src/main/java/emu/grasscutter/server/packet/send/PacketCanUseSkillNotify.java b/src/main/java/emu/grasscutter/server/packet/send/PacketCanUseSkillNotify.java new file mode 100644 index 000000000..f8fe1314a --- /dev/null +++ b/src/main/java/emu/grasscutter/server/packet/send/PacketCanUseSkillNotify.java @@ -0,0 +1,19 @@ +package emu.grasscutter.server.packet.send; + +import emu.grasscutter.net.packet.BasePacket; +import emu.grasscutter.net.packet.PacketOpcodes; +import emu.grasscutter.net.proto.CanUseSkillNotifyOuterClass; + +public class PacketCanUseSkillNotify extends BasePacket { + + public PacketCanUseSkillNotify(boolean canUseSkill) { + super(PacketOpcodes.CanUseSkillNotify); + + CanUseSkillNotifyOuterClass.CanUseSkillNotify proto = CanUseSkillNotifyOuterClass.CanUseSkillNotify.newBuilder() + .setIsCanUseSkill(canUseSkill) + .build(); + + this.setData(proto); + } + +} diff --git a/src/main/java/emu/grasscutter/server/packet/send/PacketCodexDataFullNotify.java b/src/main/java/emu/grasscutter/server/packet/send/PacketCodexDataFullNotify.java new file mode 100644 index 000000000..771a0f581 --- /dev/null +++ b/src/main/java/emu/grasscutter/server/packet/send/PacketCodexDataFullNotify.java @@ -0,0 +1,94 @@ +package emu.grasscutter.server.packet.send; + +import java.util.Collections; + +import emu.grasscutter.data.GameData; +import emu.grasscutter.game.player.Player; +import emu.grasscutter.net.packet.BasePacket; +import emu.grasscutter.net.packet.PacketOpcodes; +import emu.grasscutter.net.proto.CodexDataFullNotifyOuterClass.CodexDataFullNotify; +import emu.grasscutter.net.proto.CodexTypeDataOuterClass.CodexTypeData; + +public class PacketCodexDataFullNotify extends BasePacket { + public PacketCodexDataFullNotify(Player player) { + super(PacketOpcodes.CodexDataFullNotify, true); + + //Quests + CodexTypeData.Builder questTypeData = CodexTypeData.newBuilder() + .setTypeValue(1); + + //Weapons + CodexTypeData.Builder weaponTypeData = CodexTypeData.newBuilder() + .setTypeValue(2); + + //Animals + CodexTypeData.Builder animalTypeData = CodexTypeData.newBuilder() + .setTypeValue(3); + + //Materials + CodexTypeData.Builder materialTypeData = CodexTypeData.newBuilder() + .setTypeValue(4); + + //Books + CodexTypeData.Builder bookTypeData = CodexTypeData.newBuilder() + .setTypeValue(5); + + //Tips + CodexTypeData.Builder pushTipsTypeData = CodexTypeData.newBuilder() + .setTypeValue(6); + + //Views + CodexTypeData.Builder viewTypeData = CodexTypeData.newBuilder() + .setTypeValue(7); + + //Reliquary + CodexTypeData.Builder reliquaryData = CodexTypeData.newBuilder() + .setTypeValue(8); + + player.getQuestManager().forEachMainQuest(mainQuest -> { + if(mainQuest.isFinished()){ + var codexQuest = GameData.getCodexQuestDataIdMap().get(mainQuest.getParentQuestId()); + if(codexQuest != null){ + questTypeData.addCodexIdList(codexQuest.getId()).addAllHaveViewedList(Collections.singleton(true)); + } + } + }); + + player.getCodex().getUnlockedWeapon().forEach(weapon -> { + var codexWeapon = GameData.getCodexWeaponDataIdMap().get(weapon); + if(codexWeapon != null){ + weaponTypeData.addCodexIdList(codexWeapon.getId()).addAllHaveViewedList(Collections.singleton(true)); + } + }); + + player.getCodex().getUnlockedAnimal().forEach((animal, amount) -> { + var codexAnimal = GameData.getCodexAnimalDataMap().get(animal); + if(codexAnimal != null){ + animalTypeData.addCodexIdList(codexAnimal.getId()).addAllHaveViewedList(Collections.singleton(true)); + } + }); + + player.getCodex().getUnlockedMaterial().forEach(material -> { + var codexMaterial = GameData.getCodexMaterialDataIdMap().get(material); + if(codexMaterial != null){ + materialTypeData.addCodexIdList(codexMaterial.getId()).addAllHaveViewedList(Collections.singleton(true)); + } + }); + + player.getCodex().getUnlockedReliquarySuitCodex().forEach(reliquarySuit -> { + reliquaryData.addCodexIdList(reliquarySuit).addAllHaveViewedList(Collections.singleton(true)); + }); + + CodexDataFullNotify.Builder proto = CodexDataFullNotify.newBuilder() + .addTypeDataList(questTypeData.build()) + .addTypeDataList(weaponTypeData) + .addTypeDataList(animalTypeData) + .addTypeDataList(materialTypeData) + .addTypeDataList(bookTypeData) + .addTypeDataList(pushTipsTypeData.build()) + .addTypeDataList(viewTypeData.build()) + .addTypeDataList(reliquaryData); + + this.setData(proto); + } +} diff --git a/src/main/java/emu/grasscutter/server/packet/send/PacketCodexDataUpdateNotify.java b/src/main/java/emu/grasscutter/server/packet/send/PacketCodexDataUpdateNotify.java new file mode 100644 index 000000000..e2c0045f7 --- /dev/null +++ b/src/main/java/emu/grasscutter/server/packet/send/PacketCodexDataUpdateNotify.java @@ -0,0 +1,30 @@ +package emu.grasscutter.server.packet.send; + +import emu.grasscutter.data.GameData; +import emu.grasscutter.game.quest.GameMainQuest; +import emu.grasscutter.net.packet.BasePacket; +import emu.grasscutter.net.packet.PacketOpcodes; +import emu.grasscutter.net.proto.CodexDataUpdateNotifyOuterClass.CodexDataUpdateNotify; + +public class PacketCodexDataUpdateNotify extends BasePacket { + public PacketCodexDataUpdateNotify(GameMainQuest quest) { + super(PacketOpcodes.CodexDataUpdateNotify, true); + var codexQuest = GameData.getCodexQuestDataIdMap().get(quest.getParentQuestId()); + if(codexQuest != null){ + CodexDataUpdateNotify proto = CodexDataUpdateNotify.newBuilder() + .setTypeValue(1) + .setId(codexQuest.getId()) + .build(); + this.setData(proto); + } + } + + public PacketCodexDataUpdateNotify(int typeValue, int codexId){ + super(PacketOpcodes.CodexDataUpdateNotify, true); + CodexDataUpdateNotify proto = CodexDataUpdateNotify.newBuilder() + .setTypeValue(typeValue) + .setId(codexId) + .build(); + this.setData(proto); + } +} diff --git a/src/main/java/emu/grasscutter/server/packet/send/PacketVehicleSpawnRsp.java b/src/main/java/emu/grasscutter/server/packet/send/PacketCreateVehicleRsp.java similarity index 79% rename from src/main/java/emu/grasscutter/server/packet/send/PacketVehicleSpawnRsp.java rename to src/main/java/emu/grasscutter/server/packet/send/PacketCreateVehicleRsp.java index fe8b2a1f1..8ff65c260 100644 --- a/src/main/java/emu/grasscutter/server/packet/send/PacketVehicleSpawnRsp.java +++ b/src/main/java/emu/grasscutter/server/packet/send/PacketCreateVehicleRsp.java @@ -1,6 +1,5 @@ package emu.grasscutter.server.packet.send; -import emu.grasscutter.Grasscutter; import emu.grasscutter.game.player.Player; import emu.grasscutter.game.entity.EntityVehicle; import emu.grasscutter.game.props.FightProperty; @@ -9,21 +8,19 @@ import emu.grasscutter.game.entity.GameEntity; import emu.grasscutter.net.packet.BasePacket; import emu.grasscutter.net.packet.PacketOpcodes; +import emu.grasscutter.net.proto.VehicleInteractTypeOuterClass; import emu.grasscutter.net.proto.VehicleMemberOuterClass.VehicleMember; -import emu.grasscutter.net.proto.VehicleSpawnRspOuterClass.VehicleSpawnRsp; +import emu.grasscutter.net.proto.CreateVehicleRspOuterClass.CreateVehicleRsp; import emu.grasscutter.utils.Position; import java.util.List; +public class PacketCreateVehicleRsp extends BasePacket { -import static emu.grasscutter.net.proto.VehicleInteractTypeOuterClass.VehicleInteractType.VEHICLE_INTERACT_OUT; - -public class PacketVehicleSpawnRsp extends BasePacket { - - public PacketVehicleSpawnRsp(Player player, int vehicleId, int pointId, Position pos, Position rot) { - super(PacketOpcodes.VehicleSpawnRsp); - VehicleSpawnRsp.Builder proto = VehicleSpawnRsp.newBuilder(); + public PacketCreateVehicleRsp(Player player, int vehicleId, int pointId, Position pos, Position rot) { + super(PacketOpcodes.CreateVehicleRsp); + CreateVehicleRsp.Builder proto = CreateVehicleRsp.newBuilder(); // Eject vehicle members and Kill previous vehicles if there are any List previousVehicles = player.getScene().getEntities().values().stream() @@ -36,7 +33,7 @@ public class PacketVehicleSpawnRsp extends BasePacket { List vehicleMembers = ((EntityVehicle) entity).getVehicleMembers().stream().toList(); vehicleMembers.stream().forEach(vehicleMember -> { - player.getScene().broadcastPacket(new PacketVehicleInteractRsp(((EntityVehicle) entity), vehicleMember, VEHICLE_INTERACT_OUT)); + player.getScene().broadcastPacket(new PacketVehicleInteractRsp(((EntityVehicle) entity), vehicleMember, VehicleInteractTypeOuterClass.VehicleInteractType.VEHICLE_INTERACT_TYPE_OUT)); }); player.getScene().killEntity(entity, 0); diff --git a/src/main/java/emu/grasscutter/server/packet/send/PacketDoGachaRsp.java b/src/main/java/emu/grasscutter/server/packet/send/PacketDoGachaRsp.java index 9144c0d8e..6d8b9ddd9 100644 --- a/src/main/java/emu/grasscutter/server/packet/send/PacketDoGachaRsp.java +++ b/src/main/java/emu/grasscutter/server/packet/send/PacketDoGachaRsp.java @@ -2,6 +2,7 @@ package emu.grasscutter.server.packet.send; import java.util.List; +import emu.grasscutter.data.common.ItemParamData; import emu.grasscutter.game.gacha.GachaBanner; import emu.grasscutter.net.packet.BasePacket; import emu.grasscutter.net.packet.PacketOpcodes; @@ -14,16 +15,18 @@ public class PacketDoGachaRsp extends BasePacket { public PacketDoGachaRsp(GachaBanner banner, List list) { super(PacketOpcodes.DoGachaRsp); + ItemParamData costItem = banner.getCost(1); + ItemParamData costItem10 = banner.getCost(10); DoGachaRsp p = DoGachaRsp.newBuilder() .setGachaType(banner.getGachaType()) .setGachaScheduleId(banner.getScheduleId()) .setGachaTimes(list.size()) .setNewGachaRandom(12345) .setLeftGachaTimes(Integer.MAX_VALUE) - .setCostItemId(banner.getCostItem()) - .setCostItemNum(1) - .setTenCostItemId(banner.getCostItem()) - .setTenCostItemNum(10) + .setCostItemId(costItem.getId()) + .setCostItemNum(costItem.getCount()) + .setTenCostItemId(costItem10.getId()) + .setTenCostItemNum(costItem10.getCount()) .addAllGachaItemList(list) .build(); diff --git a/src/main/java/emu/grasscutter/server/packet/send/PacketDungeonChallengeFinishNotify.java b/src/main/java/emu/grasscutter/server/packet/send/PacketDungeonChallengeFinishNotify.java index a44c16778..1036b564c 100644 --- a/src/main/java/emu/grasscutter/server/packet/send/PacketDungeonChallengeFinishNotify.java +++ b/src/main/java/emu/grasscutter/server/packet/send/PacketDungeonChallengeFinishNotify.java @@ -13,7 +13,7 @@ public class PacketDungeonChallengeFinishNotify extends BasePacket { DungeonChallengeFinishNotify proto = DungeonChallengeFinishNotify.newBuilder() .setChallengeIndex(challenge.getChallengeIndex()) .setIsSuccess(challenge.isSuccess()) - .setUnk1(2) + .setChallengeRecordType(2) .build(); this.setData(proto); diff --git a/src/main/java/emu/grasscutter/server/packet/send/PacketDungeonSettleNotify.java b/src/main/java/emu/grasscutter/server/packet/send/PacketDungeonSettleNotify.java index 479029243..ff860289b 100644 --- a/src/main/java/emu/grasscutter/server/packet/send/PacketDungeonSettleNotify.java +++ b/src/main/java/emu/grasscutter/server/packet/send/PacketDungeonSettleNotify.java @@ -29,10 +29,10 @@ public class PacketDungeonSettleNotify extends BasePacket { ) { super(PacketOpcodes.DungeonSettleNotify); - var continueStatus = TowerLevelEndNotify.ContinueStateType.CONTINUE_STATE_CAN_NOT_CONTINUE_VALUE; + var continueStatus = TowerLevelEndNotify.ContinueStateType.CONTINUE_STATE_TYPE_CAN_NOT_CONTINUE_VALUE; if(challenge.isSuccess() && canJump){ - continueStatus = hasNextLevel ? TowerLevelEndNotify.ContinueStateType.CONTINUE_STATE_CAN_ENTER_NEXT_LEVEL_VALUE - : TowerLevelEndNotify.ContinueStateType.CONTINUE_STATE_CAN_ENTER_NEXT_FLOOR_VALUE; + continueStatus = hasNextLevel ? TowerLevelEndNotify.ContinueStateType.CONTINUE_STATE_TYPE_CAN_ENTER_NEXT_LEVEL_VALUE + : TowerLevelEndNotify.ContinueStateType.CONTINUE_STATE_TYPE_CAN_ENTER_NEXT_FLOOR_VALUE; } var towerLevelEndNotify = TowerLevelEndNotify.newBuilder() @@ -46,7 +46,7 @@ public class PacketDungeonSettleNotify extends BasePacket { .setCount(1000) .build()) ; - if(nextFloorId > 0){ + if(nextFloorId > 0 && canJump){ towerLevelEndNotify.setNextFloorId(nextFloorId); } diff --git a/src/main/java/emu/grasscutter/server/packet/send/PacketEntityAiSyncNotify.java b/src/main/java/emu/grasscutter/server/packet/send/PacketEntityAiSyncNotify.java index a3e300c25..d78c4016b 100644 --- a/src/main/java/emu/grasscutter/server/packet/send/PacketEntityAiSyncNotify.java +++ b/src/main/java/emu/grasscutter/server/packet/send/PacketEntityAiSyncNotify.java @@ -13,7 +13,7 @@ public class PacketEntityAiSyncNotify extends BasePacket { EntityAiSyncNotify.Builder proto = EntityAiSyncNotify.newBuilder(); for (int monsterId : notify.getLocalAvatarAlertedMonsterListList()) { - proto.addInfoList(AiSyncInfo.newBuilder().setEntityId(monsterId)); + proto.addInfoList(AiSyncInfo.newBuilder().setEntityId(monsterId).setHasPathToTarget(true)); } this.setData(proto); diff --git a/src/main/java/emu/grasscutter/server/packet/send/PacketEntityFightPropChangeReasonNotify.java b/src/main/java/emu/grasscutter/server/packet/send/PacketEntityFightPropChangeReasonNotify.java index 5778f711a..366354a40 100644 --- a/src/main/java/emu/grasscutter/server/packet/send/PacketEntityFightPropChangeReasonNotify.java +++ b/src/main/java/emu/grasscutter/server/packet/send/PacketEntityFightPropChangeReasonNotify.java @@ -11,21 +11,27 @@ import emu.grasscutter.net.proto.PropChangeReasonOuterClass.PropChangeReason; import java.util.List; public class PacketEntityFightPropChangeReasonNotify extends BasePacket { + public PacketEntityFightPropChangeReasonNotify(GameEntity entity, FightProperty prop, Float value, List param, PropChangeReason reason, ChangeHpReason changeHpReason) { super(PacketOpcodes.EntityFightPropChangeReasonNotify); + EntityFightPropChangeReasonNotify.Builder proto = EntityFightPropChangeReasonNotify.newBuilder() .setEntityId(entity.getId()) .setPropType(prop.getId()) .setPropDelta(value) .setReason(reason) .setChangeHpReason(changeHpReason); - for(int p: param){ + + for(int p : param){ proto.addParamList(p); } + this.setData(proto); } + public PacketEntityFightPropChangeReasonNotify(GameEntity entity, FightProperty prop, Float value, PropChangeReason reason, ChangeHpReason changeHpReason) { super(PacketOpcodes.EntityFightPropChangeReasonNotify); + EntityFightPropChangeReasonNotify proto = EntityFightPropChangeReasonNotify.newBuilder() .setEntityId(entity.getId()) .setPropType(prop.getId()) @@ -33,6 +39,20 @@ public class PacketEntityFightPropChangeReasonNotify extends BasePacket { .setReason(reason) .setChangeHpReason(changeHpReason) .build(); + + this.setData(proto); + } + + public PacketEntityFightPropChangeReasonNotify(GameEntity entity, FightProperty prop, Float value, PropChangeReason reason) { + super(PacketOpcodes.EntityFightPropChangeReasonNotify); + + EntityFightPropChangeReasonNotify proto = EntityFightPropChangeReasonNotify.newBuilder() + .setEntityId(entity.getId()) + .setPropType(prop.getId()) + .setPropDelta(value) + .setReason(reason) + .build(); + this.setData(proto); } } diff --git a/src/main/java/emu/grasscutter/server/packet/send/PacketEvtAvatarStandUpNotify.java b/src/main/java/emu/grasscutter/server/packet/send/PacketEvtAvatarStandUpNotify.java index 07cdf9054..da23def9a 100644 --- a/src/main/java/emu/grasscutter/server/packet/send/PacketEvtAvatarStandUpNotify.java +++ b/src/main/java/emu/grasscutter/server/packet/send/PacketEvtAvatarStandUpNotify.java @@ -12,7 +12,7 @@ public class PacketEvtAvatarStandUpNotify extends BasePacket { EvtAvatarStandUpNotify proto = EvtAvatarStandUpNotify.newBuilder() .setEntityId(notify.getEntityId()) .setDirection(notify.getDirection()) - .setPerformID(notify.getPerformID()) + .setPerformId(notify.getPerformId()) .setChairId(notify.getChairId()) .build(); diff --git a/src/main/java/emu/grasscutter/server/packet/send/PacketFinishedParentQuestNotify.java b/src/main/java/emu/grasscutter/server/packet/send/PacketFinishedParentQuestNotify.java new file mode 100644 index 000000000..7d64da48f --- /dev/null +++ b/src/main/java/emu/grasscutter/server/packet/send/PacketFinishedParentQuestNotify.java @@ -0,0 +1,22 @@ +package emu.grasscutter.server.packet.send; + +import emu.grasscutter.game.player.Player; +import emu.grasscutter.game.quest.GameMainQuest; +import emu.grasscutter.net.packet.BasePacket; +import emu.grasscutter.net.packet.PacketOpcodes; +import emu.grasscutter.net.proto.FinishedParentQuestNotifyOuterClass.FinishedParentQuestNotify; + +public class PacketFinishedParentQuestNotify extends BasePacket { + + public PacketFinishedParentQuestNotify(Player player) { + super(PacketOpcodes.FinishedParentQuestNotify, true); + + FinishedParentQuestNotify.Builder proto = FinishedParentQuestNotify.newBuilder(); + + for (GameMainQuest mainQuest : player.getQuestManager().getQuests().values()) { + proto.addParentQuestList(mainQuest.toProto()); + } + + this.setData(proto); + } +} diff --git a/src/main/java/emu/grasscutter/server/packet/send/PacketFinishedParentQuestUpdateNotify.java b/src/main/java/emu/grasscutter/server/packet/send/PacketFinishedParentQuestUpdateNotify.java new file mode 100644 index 000000000..68eab7222 --- /dev/null +++ b/src/main/java/emu/grasscutter/server/packet/send/PacketFinishedParentQuestUpdateNotify.java @@ -0,0 +1,19 @@ +package emu.grasscutter.server.packet.send; + +import emu.grasscutter.game.quest.GameMainQuest; +import emu.grasscutter.net.packet.BasePacket; +import emu.grasscutter.net.packet.PacketOpcodes; +import emu.grasscutter.net.proto.FinishedParentQuestUpdateNotifyOuterClass.FinishedParentQuestUpdateNotify; + +public class PacketFinishedParentQuestUpdateNotify extends BasePacket { + + public PacketFinishedParentQuestUpdateNotify(GameMainQuest quest) { + super(PacketOpcodes.FinishedParentQuestUpdateNotify); + + FinishedParentQuestUpdateNotify proto = FinishedParentQuestUpdateNotify.newBuilder() + .addParentQuestList(quest.toProto()) + .build(); + + this.setData(proto); + } +} diff --git a/src/main/java/emu/grasscutter/server/packet/send/PacketForgeDataNotify.java b/src/main/java/emu/grasscutter/server/packet/send/PacketForgeDataNotify.java new file mode 100644 index 000000000..48ea79ba4 --- /dev/null +++ b/src/main/java/emu/grasscutter/server/packet/send/PacketForgeDataNotify.java @@ -0,0 +1,30 @@ +package emu.grasscutter.server.packet.send; + +import emu.grasscutter.game.player.Player; +import emu.grasscutter.net.packet.BasePacket; +import emu.grasscutter.net.packet.PacketOpcodes; +import emu.grasscutter.net.proto.ForgeDataNotifyOuterClass.ForgeDataNotify; + +public class PacketForgeDataNotify extends BasePacket { + + public PacketForgeDataNotify(Player player) { + super(PacketOpcodes.ForgeDataNotify); + + int adventureRank = player.getLevel(); + int numQueues = + (adventureRank >= 15) ? 4 + : (adventureRank >= 10) ? 3 + : (adventureRank >= 5) ? 2 + : 1; + + ForgeDataNotify proto = ForgeDataNotify.newBuilder() + .addAllForgeIdList(player.getUnlockedForgingBlueprints()) + .setMaxQueueNum(numQueues) + .build(); + + // ToDo: Add the information for the actual forging queues + // and ongoing forges. + + this.setData(proto); + } +} diff --git a/src/main/java/emu/grasscutter/server/packet/send/PacketForgeFormulaDataNotify.java b/src/main/java/emu/grasscutter/server/packet/send/PacketForgeFormulaDataNotify.java new file mode 100644 index 000000000..46f180280 --- /dev/null +++ b/src/main/java/emu/grasscutter/server/packet/send/PacketForgeFormulaDataNotify.java @@ -0,0 +1,19 @@ +package emu.grasscutter.server.packet.send; + +import emu.grasscutter.net.packet.BasePacket; +import emu.grasscutter.net.packet.PacketOpcodes; +import emu.grasscutter.net.proto.ForgeFormulaDataNotifyOuterClass.ForgeFormulaDataNotify; + +public class PacketForgeFormulaDataNotify extends BasePacket { + + public PacketForgeFormulaDataNotify(int itemId) { + super(PacketOpcodes.ForgeFormulaDataNotify); + + ForgeFormulaDataNotify proto = ForgeFormulaDataNotify.newBuilder() + .setForgeId(itemId) + .setIsLocked(false) + .build(); + + this.setData(proto); + } +} diff --git a/src/main/java/emu/grasscutter/server/packet/send/PacketGetAllMailRsp.java b/src/main/java/emu/grasscutter/server/packet/send/PacketGetAllMailRsp.java index 59af30f42..ff7f83721 100644 --- a/src/main/java/emu/grasscutter/server/packet/send/PacketGetAllMailRsp.java +++ b/src/main/java/emu/grasscutter/server/packet/send/PacketGetAllMailRsp.java @@ -5,6 +5,7 @@ import emu.grasscutter.game.mail.Mail; import emu.grasscutter.game.player.Player; import emu.grasscutter.net.packet.BasePacket; import emu.grasscutter.net.packet.PacketOpcodes; +import emu.grasscutter.net.proto.EquipParamOuterClass; import emu.grasscutter.net.proto.GetAllMailRspOuterClass.GetAllMailRsp; import emu.grasscutter.net.proto.ItemParamOuterClass; import emu.grasscutter.net.proto.MailDataOuterClass; @@ -23,9 +24,9 @@ public class PacketGetAllMailRsp extends BasePacket { GetAllMailRsp.Builder proto = GetAllMailRsp.newBuilder(); if (isGiftMail) { - proto.setIsGiftMail(true); + proto.setANKKGPJCINB(true); } else { - proto.setIsGiftMail(false); + proto.setANKKGPJCINB(false); if (player.getAllMail().size() != 0) { // Make sure the player has mail List mailDataList = new ArrayList(); @@ -43,10 +44,10 @@ public class PacketGetAllMailRsp extends BasePacket { for (Mail.MailItem item : message.itemList) { MailItemOuterClass.MailItem.Builder mailItem = MailItemOuterClass.MailItem.newBuilder(); - ItemParamOuterClass.ItemParam.Builder itemParam = ItemParamOuterClass.ItemParam.newBuilder(); + EquipParamOuterClass.EquipParam.Builder itemParam = EquipParamOuterClass.EquipParam.newBuilder(); itemParam.setItemId(item.itemId); - itemParam.setCount(item.itemCount); - mailItem.setItemParam(itemParam.build()); + itemParam.setItemNum(item.itemCount); + mailItem.setEquipParam(itemParam.build()); mailItems.add(mailItem.build()); } @@ -60,7 +61,7 @@ public class PacketGetAllMailRsp extends BasePacket { mailData.setImportance(message.importance); mailData.setIsRead(message.isRead); mailData.setIsAttachmentGot(message.isAttachmentGot); - mailData.setStateValue(1); + mailData.setBHCAHLJIKFFValue(1); mailDataList.add(mailData.build()); } diff --git a/src/main/java/emu/grasscutter/server/packet/send/PacketGetMailItemRsp.java b/src/main/java/emu/grasscutter/server/packet/send/PacketGetMailItemRsp.java index 37f753c17..cdf2e2349 100644 --- a/src/main/java/emu/grasscutter/server/packet/send/PacketGetMailItemRsp.java +++ b/src/main/java/emu/grasscutter/server/packet/send/PacketGetMailItemRsp.java @@ -18,52 +18,60 @@ public class PacketGetMailItemRsp extends BasePacket { public PacketGetMailItemRsp(Player player, List mailList) { super(PacketOpcodes.GetMailItemRsp); - List claimedMessages = new ArrayList<>(); List claimedItems = new ArrayList<>(); GetMailItemRsp.Builder proto = GetMailItemRsp.newBuilder(); - for (int mailId : mailList) { - Mail message = player.getMail(mailId); + synchronized (player) { + boolean modified = false; + for (int mailId : mailList) { + Mail message = player.getMail(mailId); + if (!message.isAttachmentGot) {//No duplicated item + for (Mail.MailItem mailItem : message.itemList) { + EquipParamOuterClass.EquipParam.Builder item = EquipParamOuterClass.EquipParam.newBuilder(); + int promoteLevel = 0; - for(Mail.MailItem mailItem : message.itemList) { - EquipParamOuterClass.EquipParam.Builder item = EquipParamOuterClass.EquipParam.newBuilder(); - int promoteLevel = 0; - if (mailItem.itemLevel > 20) { // 20/40 - promoteLevel = 1; - } else if (mailItem.itemLevel > 40) { // 40/50 - promoteLevel = 2; - } else if (mailItem.itemLevel > 50) { // 50/60 - promoteLevel = 3; - } else if (mailItem.itemLevel > 60) { // 60/70 - promoteLevel = 4; - } else if (mailItem.itemLevel > 70) { // 70/80 - promoteLevel = 5; - } else if (mailItem.itemLevel > 80) { // 80/90 - promoteLevel = 6; + if (mailItem.itemLevel > 80) { // 80/90 + promoteLevel = 6; + } else if (mailItem.itemLevel > 70) { // 70/80 + promoteLevel = 5; + } else if (mailItem.itemLevel > 60) { // 60/70 + promoteLevel = 4; + } else if (mailItem.itemLevel > 50) { // 50/60 + promoteLevel = 3; + } else if (mailItem.itemLevel > 40) { // 40/50 + promoteLevel = 2; + } else if (mailItem.itemLevel > 20) { // 20/40 + promoteLevel = 1; + } + + item.setItemId(mailItem.itemId); + item.setItemNum(mailItem.itemCount); + item.setItemLevel(mailItem.itemLevel); + item.setPromoteLevel(promoteLevel); + claimedItems.add(item.build()); + + GameItem gameItem = new GameItem(GameData.getItemDataMap().get(mailItem.itemId)); + gameItem.setCount(mailItem.itemCount); + gameItem.setLevel(mailItem.itemLevel); + gameItem.setPromoteLevel(promoteLevel); + player.getInventory().addItem(gameItem, ActionReason.MailAttachment); + } + + message.isAttachmentGot = true; + claimedMessages.add(message); + + player.replaceMailByIndex(mailId, message); + modified = true; } - - item.setItemId(mailItem.itemId); - item.setItemNum(mailItem.itemCount); - item.setItemLevel(mailItem.itemLevel); - item.setPromoteLevel(promoteLevel); - claimedItems.add(item.build()); - - GameItem gameItem = new GameItem(GameData.getItemDataMap().get(mailItem.itemId)); - gameItem.setCount(mailItem.itemCount); - gameItem.setLevel(mailItem.itemLevel); - gameItem.setPromoteLevel(promoteLevel); - player.getInventory().addItem(gameItem, ActionReason.MailAttachment); } - - message.isAttachmentGot = true; - claimedMessages.add(message); - - player.replaceMailByIndex(mailId, message); + if(modified) { + player.save(); + } } - proto.addAllMailIdList(claimedMessages.stream().map(message -> player.getMailId(message)).collect(Collectors.toList())); + proto.addAllMailIdList(claimedMessages.stream().map(player::getMailId).collect(Collectors.toList())); proto.addAllItemList(claimedItems); this.setData(proto.build()); diff --git a/src/main/java/emu/grasscutter/server/packet/send/PacketGetPlayerFriendListRsp.java b/src/main/java/emu/grasscutter/server/packet/send/PacketGetPlayerFriendListRsp.java index ff373140b..1ca159d57 100644 --- a/src/main/java/emu/grasscutter/server/packet/send/PacketGetPlayerFriendListRsp.java +++ b/src/main/java/emu/grasscutter/server/packet/send/PacketGetPlayerFriendListRsp.java @@ -1,7 +1,6 @@ package emu.grasscutter.server.packet.send; import emu.grasscutter.GameConstants; -import emu.grasscutter.Grasscutter; import emu.grasscutter.game.friends.Friendship; import emu.grasscutter.game.player.Player; import emu.grasscutter.net.packet.BasePacket; @@ -12,24 +11,27 @@ import emu.grasscutter.net.proto.GetPlayerFriendListRspOuterClass.GetPlayerFrien import emu.grasscutter.net.proto.ProfilePictureOuterClass.ProfilePicture; import emu.grasscutter.net.proto.PlatformTypeOuterClass; +import static emu.grasscutter.Configuration.*; + public class PacketGetPlayerFriendListRsp extends BasePacket { public PacketGetPlayerFriendListRsp(Player player) { super(PacketOpcodes.GetPlayerFriendListRsp); + var serverAccount = GAME_INFO.serverAccount; FriendBrief serverFriend = FriendBrief.newBuilder() .setUid(GameConstants.SERVER_CONSOLE_UID) - .setNickname(Grasscutter.getConfig().getGameServerOptions().ServerNickname) - .setLevel(Grasscutter.getConfig().getGameServerOptions().ServerLevel) - .setProfilePicture(ProfilePicture.newBuilder().setAvatarId(Grasscutter.getConfig().getGameServerOptions().ServerAvatarId)) - .setWorldLevel(Grasscutter.getConfig().getGameServerOptions().ServerWorldLevel) - .setSignature(Grasscutter.getConfig().getGameServerOptions().ServerSignature) + .setNickname(serverAccount.nickName) + .setLevel(serverAccount.adventureRank) + .setProfilePicture(ProfilePicture.newBuilder().setAvatarId(serverAccount.avatarId)) + .setWorldLevel(serverAccount.worldLevel) + .setSignature(serverAccount.signature) .setLastActiveTime((int) (System.currentTimeMillis() / 1000f)) - .setNameCardId(Grasscutter.getConfig().getGameServerOptions().ServerNameCardId) - .setOnlineState(FriendOnlineState.FRIEND_ONLINE) + .setNameCardId(serverAccount.nameCardId) + .setOnlineState(FriendOnlineState.FRIEND_ONLINE_STATE_ONLINE) .setParam(1) .setIsGameSource(true) - .setPlatformType(PlatformTypeOuterClass.PlatformType.PC) + .setPlatformType(PlatformTypeOuterClass.PlatformType.PLATFORM_TYPE_PC) .build(); GetPlayerFriendListRsp.Builder proto = GetPlayerFriendListRsp.newBuilder().addFriendList(serverFriend); @@ -37,10 +39,12 @@ public class PacketGetPlayerFriendListRsp extends BasePacket { for (Friendship friendship : player.getFriendsList().getFriends().values()) { proto.addFriendList(friendship.toProto()); } + for (Friendship friendship : player.getFriendsList().getPendingFriends().values()) { if (friendship.getAskerId() == player.getUid()) { continue; } + proto.addAskFriendList(friendship.toProto()); } diff --git a/src/main/java/emu/grasscutter/server/packet/send/PacketGetPlayerTokenRsp.java b/src/main/java/emu/grasscutter/server/packet/send/PacketGetPlayerTokenRsp.java index 3a5e0f08c..f48c40873 100644 --- a/src/main/java/emu/grasscutter/server/packet/send/PacketGetPlayerTokenRsp.java +++ b/src/main/java/emu/grasscutter/server/packet/send/PacketGetPlayerTokenRsp.java @@ -10,16 +10,16 @@ import emu.grasscutter.utils.Crypto; public class PacketGetPlayerTokenRsp extends BasePacket { - public PacketGetPlayerTokenRsp(GameSession session, boolean doesPlayerExist) { + public PacketGetPlayerTokenRsp(GameSession session) { super(PacketOpcodes.GetPlayerTokenRsp, true); this.setUseDispatchKey(true); GetPlayerTokenRsp p = GetPlayerTokenRsp.newBuilder() - .setUid(session.getAccount().getPlayerUid()) + .setUid(session.getPlayer().getUid()) .setToken(session.getAccount().getToken()) .setAccountType(1) - .setIsProficientPlayer(doesPlayerExist) // Not sure where this goes + .setIsProficientPlayer(session.getPlayer().getAvatars().getAvatarCount() > 0) // Not sure where this goes .setSecretKeySeed(Crypto.ENCRYPT_SEED) .setSecurityCmdBuffer(ByteString.copyFrom(Crypto.ENCRYPT_SEED_BUFFER)) .setPlatformType(3) diff --git a/src/main/java/emu/grasscutter/server/packet/send/PacketGetWidgetSlotRsp.java b/src/main/java/emu/grasscutter/server/packet/send/PacketGetWidgetSlotRsp.java new file mode 100644 index 000000000..8df189c94 --- /dev/null +++ b/src/main/java/emu/grasscutter/server/packet/send/PacketGetWidgetSlotRsp.java @@ -0,0 +1,41 @@ +package emu.grasscutter.server.packet.send; + +import emu.grasscutter.game.player.Player; +import emu.grasscutter.net.packet.BasePacket; +import emu.grasscutter.net.packet.PacketOpcodes; +import emu.grasscutter.net.proto.GetWidgetSlotRspOuterClass; +import emu.grasscutter.net.proto.WidgetSlotDataOuterClass; +import emu.grasscutter.net.proto.WidgetSlotTagOuterClass; + +import java.util.List; + +public class PacketGetWidgetSlotRsp extends BasePacket { + + public PacketGetWidgetSlotRsp(Player player) { + super(PacketOpcodes.GetWidgetSlotRsp); + + GetWidgetSlotRspOuterClass.GetWidgetSlotRsp.Builder proto = + GetWidgetSlotRspOuterClass.GetWidgetSlotRsp.newBuilder(); + + if (player.getWidgetId() == null) { + proto.addAllSlotList(List.of()); + } else { + proto.addSlotList( + WidgetSlotDataOuterClass.WidgetSlotData.newBuilder() + .setIsActive(true) + .setMaterialId(player.getWidgetId()) + .build() + ); + + proto.addSlotList( + WidgetSlotDataOuterClass.WidgetSlotData.newBuilder() + .setTag(WidgetSlotTagOuterClass.WidgetSlotTag.WIDGET_SLOT_TAG_ATTACH_AVATAR) + .build() + ); + } + + GetWidgetSlotRspOuterClass.GetWidgetSlotRsp protoData = proto.build(); + + this.setData(protoData); + } +} diff --git a/src/main/java/emu/grasscutter/server/packet/send/PacketHomeChooseModuleRsp.java b/src/main/java/emu/grasscutter/server/packet/send/PacketHomeChooseModuleRsp.java new file mode 100644 index 000000000..e7b3ff1ea --- /dev/null +++ b/src/main/java/emu/grasscutter/server/packet/send/PacketHomeChooseModuleRsp.java @@ -0,0 +1,19 @@ +package emu.grasscutter.server.packet.send; + +import emu.grasscutter.net.packet.BasePacket; +import emu.grasscutter.net.packet.PacketOpcodes; +import emu.grasscutter.net.proto.HomeChooseModuleRspOuterClass; + +public class PacketHomeChooseModuleRsp extends BasePacket { + + public PacketHomeChooseModuleRsp(int moduleId) { + super(PacketOpcodes.HomeChooseModuleRsp); + + HomeChooseModuleRspOuterClass.HomeChooseModuleRsp proto = HomeChooseModuleRspOuterClass.HomeChooseModuleRsp.newBuilder() + .setRetcode(0) + .setModuleId(moduleId) + .build(); + + this.setData(proto); + } +} diff --git a/src/main/java/emu/grasscutter/server/packet/send/PacketHomeComfortInfoNotify.java b/src/main/java/emu/grasscutter/server/packet/send/PacketHomeComfortInfoNotify.java new file mode 100644 index 000000000..47e46dfdb --- /dev/null +++ b/src/main/java/emu/grasscutter/server/packet/send/PacketHomeComfortInfoNotify.java @@ -0,0 +1,40 @@ +package emu.grasscutter.server.packet.send; + +import emu.grasscutter.game.player.Player; +import emu.grasscutter.net.packet.BasePacket; +import emu.grasscutter.net.packet.PacketOpcodes; +import emu.grasscutter.net.proto.HomeComfortInfoNotifyOuterClass; +import emu.grasscutter.net.proto.HomeModuleComfortInfoOuterClass; + +import java.util.ArrayList; +import java.util.List; + +public class PacketHomeComfortInfoNotify extends BasePacket { + + public PacketHomeComfortInfoNotify(Player player) { + super(PacketOpcodes.HomeComfortInfoNotify); + + if (player.getRealmList() == null) { + // Do not send + return; + } + + List comfortInfoList = new ArrayList<>(); + + for (int moduleId : player.getRealmList()) { + comfortInfoList.add( + HomeModuleComfortInfoOuterClass.HomeModuleComfortInfo.newBuilder() + .setModuleId(moduleId) + .build() + ); + } + + HomeComfortInfoNotifyOuterClass.HomeComfortInfoNotify proto = HomeComfortInfoNotifyOuterClass.HomeComfortInfoNotify + .newBuilder() + .addAllModuleInfoList(comfortInfoList) + .build(); + + + this.setData(proto); + } +} diff --git a/src/main/java/emu/grasscutter/server/packet/send/PacketMailChangeNotify.java b/src/main/java/emu/grasscutter/server/packet/send/PacketMailChangeNotify.java index c4b2b229e..0a43a1c61 100644 --- a/src/main/java/emu/grasscutter/server/packet/send/PacketMailChangeNotify.java +++ b/src/main/java/emu/grasscutter/server/packet/send/PacketMailChangeNotify.java @@ -37,10 +37,10 @@ public class PacketMailChangeNotify extends BasePacket { for (Mail.MailItem item : message.itemList) { MailItemOuterClass.MailItem.Builder mailItem = MailItemOuterClass.MailItem.newBuilder(); - ItemParamOuterClass.ItemParam.Builder itemParam = ItemParamOuterClass.ItemParam.newBuilder(); + EquipParamOuterClass.EquipParam.Builder itemParam = EquipParamOuterClass.EquipParam.newBuilder(); itemParam.setItemId(item.itemId); - itemParam.setCount(item.itemCount); - mailItem.setItemParam(itemParam.build()); + itemParam.setItemNum(item.itemCount); + mailItem.setEquipParam(itemParam.build()); mailItems.add(mailItem.build()); } @@ -54,7 +54,7 @@ public class PacketMailChangeNotify extends BasePacket { mailData.setImportance(message.importance); mailData.setIsRead(message.isRead); mailData.setIsAttachmentGot(message.isAttachmentGot); - mailData.setStateValue(message.stateValue); + mailData.setBHCAHLJIKFFValue(message.stateValue); proto.addMailList(mailData.build()); } diff --git a/src/main/java/emu/grasscutter/server/packet/send/PacketMarkMapRsp.java b/src/main/java/emu/grasscutter/server/packet/send/PacketMarkMapRsp.java index d7ee20d61..21ccbe74e 100644 --- a/src/main/java/emu/grasscutter/server/packet/send/PacketMarkMapRsp.java +++ b/src/main/java/emu/grasscutter/server/packet/send/PacketMarkMapRsp.java @@ -10,7 +10,7 @@ import java.util.*; public class PacketMarkMapRsp extends BasePacket { - public PacketMarkMapRsp(Player player, HashMap mapMarks) { + public PacketMarkMapRsp(HashMap mapMarks) { super(PacketOpcodes.MarkMapRsp); MarkMapRspOuterClass.MarkMapRsp.Builder proto = MarkMapRspOuterClass.MarkMapRsp.newBuilder(); diff --git a/src/main/java/emu/grasscutter/server/packet/send/PacketPlayerEnterSceneNotify.java b/src/main/java/emu/grasscutter/server/packet/send/PacketPlayerEnterSceneNotify.java index 42fe3a12f..953bb679c 100644 --- a/src/main/java/emu/grasscutter/server/packet/send/PacketPlayerEnterSceneNotify.java +++ b/src/main/java/emu/grasscutter/server/packet/send/PacketPlayerEnterSceneNotify.java @@ -23,7 +23,7 @@ public class PacketPlayerEnterSceneNotify extends BasePacket { .setSceneId(player.getSceneId()) .setPos(player.getPos().toProto()) .setSceneBeginTime(System.currentTimeMillis()) - .setType(EnterType.ENTER_SELF) + .setType(EnterType.ENTER_TYPE_SELF) .setTargetUid(player.getUid()) .setEnterSceneToken(player.getEnterSceneToken()) .setWorldLevel(player.getWorldLevel()) diff --git a/src/main/java/emu/grasscutter/server/packet/send/PacketPlayerHomeCompInfoNotify.java b/src/main/java/emu/grasscutter/server/packet/send/PacketPlayerHomeCompInfoNotify.java new file mode 100644 index 000000000..29a6964b5 --- /dev/null +++ b/src/main/java/emu/grasscutter/server/packet/send/PacketPlayerHomeCompInfoNotify.java @@ -0,0 +1,32 @@ +package emu.grasscutter.server.packet.send; + +import emu.grasscutter.game.player.Player; +import emu.grasscutter.net.packet.BasePacket; +import emu.grasscutter.net.packet.PacketOpcodes; +import emu.grasscutter.net.proto.PlayerHomeCompInfoNotifyOuterClass; +import emu.grasscutter.net.proto.PlayerHomeCompInfoOuterClass; + +import java.util.List; + +public class PacketPlayerHomeCompInfoNotify extends BasePacket { + + public PacketPlayerHomeCompInfoNotify(Player player) { + super(PacketOpcodes.PlayerHomeCompInfoNotify); + + if (player.getRealmList() == null) { + // Do not send + return; + } + + PlayerHomeCompInfoNotifyOuterClass.PlayerHomeCompInfoNotify proto = PlayerHomeCompInfoNotifyOuterClass.PlayerHomeCompInfoNotify.newBuilder() + .setCompInfo( + PlayerHomeCompInfoOuterClass.PlayerHomeCompInfo.newBuilder() + .addAllUnlockedModuleIdList(player.getRealmList()) + .addAllLevelupRewardGotLevelList(List.of(1)) // Hardcoded + .build() + ) + .build(); + + this.setData(proto); + } +} diff --git a/src/main/java/emu/grasscutter/server/packet/send/PacketPlayerLoginRsp.java b/src/main/java/emu/grasscutter/server/packet/send/PacketPlayerLoginRsp.java index 6407e9412..52a487d55 100644 --- a/src/main/java/emu/grasscutter/server/packet/send/PacketPlayerLoginRsp.java +++ b/src/main/java/emu/grasscutter/server/packet/send/PacketPlayerLoginRsp.java @@ -2,7 +2,6 @@ package emu.grasscutter.server.packet.send; import com.google.protobuf.ByteString; import emu.grasscutter.Grasscutter; -import emu.grasscutter.Grasscutter.ServerDebugMode; import emu.grasscutter.Grasscutter.ServerRunMode; import emu.grasscutter.net.packet.BasePacket; import emu.grasscutter.net.packet.PacketOpcodes; @@ -10,11 +9,15 @@ import emu.grasscutter.net.proto.PlayerLoginRspOuterClass.PlayerLoginRsp; import emu.grasscutter.net.proto.QueryCurrRegionHttpRspOuterClass; import emu.grasscutter.net.proto.RegionInfoOuterClass.RegionInfo; import emu.grasscutter.server.game.GameSession; +import emu.grasscutter.server.http.dispatch.RegionHandler; +import emu.grasscutter.utils.Crypto; import emu.grasscutter.utils.FileUtils; import java.io.File; -import java.net.URL; import java.util.Base64; +import java.util.Objects; + +import static emu.grasscutter.Configuration.*; public class PacketPlayerLoginRsp extends BasePacket { @@ -27,27 +30,17 @@ public class PacketPlayerLoginRsp extends BasePacket { RegionInfo info; - if (Grasscutter.getConfig().RunMode == ServerRunMode.GAME_ONLY) { + if (SERVER.runMode == ServerRunMode.GAME_ONLY) { if (regionCache == null) { try { - File file = new File(Grasscutter.getConfig().DATA_FOLDER + "query_cur_region.txt"); - String query_cur_region = ""; - if (file.exists()) { - query_cur_region = new String(FileUtils.read(file)); - } else { - Grasscutter.getLogger().warn("query_cur_region not found! Using default current region."); - } - - byte[] decodedCurRegion = Base64.getDecoder().decode(query_cur_region); - QueryCurrRegionHttpRspOuterClass.QueryCurrRegionHttpRsp regionQuery = QueryCurrRegionHttpRspOuterClass.QueryCurrRegionHttpRsp.parseFrom(decodedCurRegion); - - RegionInfo serverRegion = regionQuery.getRegionInfo().toBuilder() - .setGateserverIp((Grasscutter.getConfig().getGameServerOptions().PublicIp.isEmpty() ? Grasscutter.getConfig().getGameServerOptions().Ip : Grasscutter.getConfig().getGameServerOptions().PublicIp)) - .setGateserverPort(Grasscutter.getConfig().getGameServerOptions().PublicPort != 0 ? Grasscutter.getConfig().getGameServerOptions().PublicPort : Grasscutter.getConfig().getGameServerOptions().Port) - .setSecretKey(ByteString.copyFrom(FileUtils.read(Grasscutter.getConfig().KEY_FOLDER + "dispatchSeed.bin"))) + // todo: we might want to push custom config to client + RegionInfo serverRegion = RegionInfo.newBuilder() + .setGateserverIp(lr(GAME_INFO.accessAddress, GAME_INFO.bindAddress)) + .setGateserverPort(lr(GAME_INFO.accessPort, GAME_INFO.bindPort)) + .setSecretKey(ByteString.copyFrom(Crypto.DISPATCH_SEED)) .build(); - regionCache = regionQuery.toBuilder().setRegionInfo(serverRegion).build(); + regionCache = QueryCurrRegionHttpRspOuterClass.QueryCurrRegionHttpRsp.newBuilder().setRegionInfo(serverRegion).build(); } catch (Exception e) { Grasscutter.getLogger().error("Error while initializing region cache!", e); } @@ -55,7 +48,7 @@ public class PacketPlayerLoginRsp extends BasePacket { info = regionCache.getRegionInfo(); } else { - info = Grasscutter.getDispatchServer().getCurrRegion().getRegionInfo(); + info = Objects.requireNonNull(RegionHandler.getCurrentRegion()).getRegionInfo(); } PlayerLoginRsp p = PlayerLoginRsp.newBuilder() diff --git a/src/main/java/emu/grasscutter/server/packet/send/PacketPlayerStoreNotify.java b/src/main/java/emu/grasscutter/server/packet/send/PacketPlayerStoreNotify.java index a3a6ff7b8..2a61aeda9 100644 --- a/src/main/java/emu/grasscutter/server/packet/send/PacketPlayerStoreNotify.java +++ b/src/main/java/emu/grasscutter/server/packet/send/PacketPlayerStoreNotify.java @@ -1,7 +1,5 @@ package emu.grasscutter.server.packet.send; -import emu.grasscutter.GameConstants; -import emu.grasscutter.Grasscutter; import emu.grasscutter.game.inventory.GameItem; import emu.grasscutter.game.player.Player; import emu.grasscutter.net.packet.BasePacket; @@ -10,6 +8,8 @@ import emu.grasscutter.net.proto.ItemOuterClass.Item; import emu.grasscutter.net.proto.PlayerStoreNotifyOuterClass.PlayerStoreNotify; import emu.grasscutter.net.proto.StoreTypeOuterClass.StoreType; +import static emu.grasscutter.Configuration.*; + public class PacketPlayerStoreNotify extends BasePacket { public PacketPlayerStoreNotify(Player player) { @@ -18,8 +18,8 @@ public class PacketPlayerStoreNotify extends BasePacket { this.buildHeader(2); PlayerStoreNotify.Builder p = PlayerStoreNotify.newBuilder() - .setStoreType(StoreType.STORE_PACK) - .setWeightLimit(Grasscutter.getConfig().getGameServerOptions().InventoryLimitAll); + .setStoreType(StoreType.STORE_TYPE_PACK) + .setWeightLimit(GAME_OPTIONS.inventoryLimits.all); for (GameItem item : player.getInventory()) { Item itemProto = item.toProto(); diff --git a/src/main/java/emu/grasscutter/server/packet/send/PacketPlayerWorldSceneInfoListNotify.java b/src/main/java/emu/grasscutter/server/packet/send/PacketPlayerWorldSceneInfoListNotify.java new file mode 100644 index 000000000..bf9b029a7 --- /dev/null +++ b/src/main/java/emu/grasscutter/server/packet/send/PacketPlayerWorldSceneInfoListNotify.java @@ -0,0 +1,63 @@ +package emu.grasscutter.server.packet.send; + +import emu.grasscutter.net.packet.BasePacket; +import emu.grasscutter.net.packet.PacketOpcodes; +import emu.grasscutter.net.proto.PlayerWorldSceneInfoListNotifyOuterClass.PlayerWorldSceneInfoListNotify; +import emu.grasscutter.net.proto.PlayerWorldSceneInfoOuterClass; +import emu.grasscutter.net.proto.SceneUnlockInfoOuterClass.SceneUnlockInfo; + +import static emu.grasscutter.net.proto.PlayerWorldSceneInfoOuterClass.*; + +public class PacketPlayerWorldSceneInfoListNotify extends BasePacket { + + public PacketPlayerWorldSceneInfoListNotify() { + super(PacketOpcodes.PlayerWorldSceneInfoListNotify); // Rename opcode later + + PlayerWorldSceneInfoListNotify proto = PlayerWorldSceneInfoListNotify.newBuilder() + .addInfoList( + PlayerWorldSceneInfo.newBuilder() + .setSceneId(1) + .setIsLocked(false) + .build() + ) + .addInfoList( + PlayerWorldSceneInfo.newBuilder() + .setSceneId(3) + .setIsLocked(false) + .addSceneTagIdList(102) + .addSceneTagIdList(113) + .addSceneTagIdList(117) + .build() + ) + .addInfoList( + PlayerWorldSceneInfo.newBuilder() + .setSceneId(4) + .setIsLocked(false) + .addSceneTagIdList(106) + .addSceneTagIdList(109) + .addSceneTagIdList(117) + .build() + ) + .addInfoList( + PlayerWorldSceneInfo.newBuilder() + .setSceneId(5) + .setIsLocked(false) + .build() + ) + .addInfoList( + PlayerWorldSceneInfo.newBuilder() + .setSceneId(6) + .setIsLocked(false) + .build() + ) + .addInfoList( + PlayerWorldSceneInfo.newBuilder() + .setSceneId(7) + .setIsLocked(false) + .build() + ) + .build(); + + this.setData(proto); + } +} diff --git a/src/main/java/emu/grasscutter/server/packet/send/PacketPullRecentChatRsp.java b/src/main/java/emu/grasscutter/server/packet/send/PacketPullRecentChatRsp.java index 871534b53..0e757d11b 100644 --- a/src/main/java/emu/grasscutter/server/packet/send/PacketPullRecentChatRsp.java +++ b/src/main/java/emu/grasscutter/server/packet/send/PacketPullRecentChatRsp.java @@ -1,42 +1,41 @@ package emu.grasscutter.server.packet.send; -import emu.grasscutter.Config.GameServerOptions; import emu.grasscutter.game.player.Player; import emu.grasscutter.GameConstants; -import emu.grasscutter.Grasscutter; import emu.grasscutter.net.packet.BasePacket; import emu.grasscutter.net.packet.PacketOpcodes; import emu.grasscutter.net.proto.ChatInfoOuterClass.ChatInfo; import emu.grasscutter.net.proto.PullRecentChatRspOuterClass.PullRecentChatRsp; import emu.grasscutter.utils.Utils; +import static emu.grasscutter.Configuration.*; + public class PacketPullRecentChatRsp extends BasePacket { public PacketPullRecentChatRsp(Player player) { super(PacketOpcodes.PullRecentChatRsp); - GameServerOptions serverOptions = Grasscutter.getConfig().getGameServerOptions(); + var joinOptions = GAME_INFO.joinOptions; PullRecentChatRsp.Builder proto = PullRecentChatRsp.newBuilder(); - if (serverOptions.WelcomeEmotes != null && serverOptions.WelcomeEmotes.length > 0) { + if (joinOptions.welcomeEmotes != null && joinOptions.welcomeEmotes.length > 0) { ChatInfo welcomeEmote = ChatInfo.newBuilder() .setTime((int) (System.currentTimeMillis() / 1000)) .setUid(GameConstants.SERVER_CONSOLE_UID) .setToUid(player.getUid()) - .setIcon(serverOptions.WelcomeEmotes[Utils.randomRange(0, serverOptions.WelcomeEmotes.length - 1)]) + .setIcon(joinOptions.welcomeEmotes[Utils.randomRange(0, joinOptions.welcomeEmotes.length - 1)]) .build(); proto.addChatInfo(welcomeEmote); } - if (serverOptions.WelcomeMotd != null && serverOptions.WelcomeMotd.length() > 0) { - ChatInfo welcomeMotd = ChatInfo.newBuilder() + if (joinOptions.welcomeMessage != null && joinOptions.welcomeMessage.length() > 0) { + ChatInfo welcomeMessage = ChatInfo.newBuilder() .setTime((int) (System.currentTimeMillis() / 1000)) .setUid(GameConstants.SERVER_CONSOLE_UID) .setToUid(player.getUid()) - .setText(Grasscutter.getConfig().getGameServerOptions().WelcomeMotd) + .setText(joinOptions.welcomeMessage) .build(); - - proto.addChatInfo(welcomeMotd); + proto.addChatInfo(welcomeMessage); } this.setData(proto); diff --git a/src/main/java/emu/grasscutter/server/packet/send/PacketQueryCodexMonsterBeKilledNumRsp.java b/src/main/java/emu/grasscutter/server/packet/send/PacketQueryCodexMonsterBeKilledNumRsp.java new file mode 100644 index 000000000..8da5c4fe2 --- /dev/null +++ b/src/main/java/emu/grasscutter/server/packet/send/PacketQueryCodexMonsterBeKilledNumRsp.java @@ -0,0 +1,26 @@ +package emu.grasscutter.server.packet.send; + +import emu.grasscutter.game.player.Player; +import emu.grasscutter.net.packet.BasePacket; +import emu.grasscutter.net.packet.PacketOpcodes; +import emu.grasscutter.net.proto.QueryCodexMonsterBeKilledNumRspOuterClass.QueryCodexMonsterBeKilledNumRsp; + +import java.util.List; + +public class PacketQueryCodexMonsterBeKilledNumRsp extends BasePacket { + + public PacketQueryCodexMonsterBeKilledNumRsp(Player player, List codexList) { + super(PacketOpcodes.QueryCodexMonsterBeKilledNumRsp); + QueryCodexMonsterBeKilledNumRsp.Builder proto = QueryCodexMonsterBeKilledNumRsp.newBuilder(); + + codexList.forEach(animal -> { + if(player.getCodex().getUnlockedAnimal().containsKey(animal)){ + proto.addCodexIdList(animal) + .addBeKilledNumList(player.getCodex().getUnlockedAnimal().get(animal)) + .addCHPBKCLKPCJ(0); + } + }); + + this.setData(proto); + } +} diff --git a/src/main/java/emu/grasscutter/server/packet/send/PacketQuestListNotify.java b/src/main/java/emu/grasscutter/server/packet/send/PacketQuestListNotify.java new file mode 100644 index 000000000..ccf0d765a --- /dev/null +++ b/src/main/java/emu/grasscutter/server/packet/send/PacketQuestListNotify.java @@ -0,0 +1,23 @@ +package emu.grasscutter.server.packet.send; + +import emu.grasscutter.game.player.Player; +import emu.grasscutter.game.quest.GameMainQuest; +import emu.grasscutter.game.quest.QuestManager; +import emu.grasscutter.net.packet.BasePacket; +import emu.grasscutter.net.packet.PacketOpcodes; +import emu.grasscutter.net.proto.QuestListNotifyOuterClass.QuestListNotify; + +public class PacketQuestListNotify extends BasePacket { + + public PacketQuestListNotify(Player player) { + super(PacketOpcodes.QuestListNotify, true); + + QuestListNotify.Builder proto = QuestListNotify.newBuilder(); + + player.getQuestManager().forEachQuest(quest -> { + proto.addQuestList(quest.toProto()); + }); + + this.setData(proto); + } +} diff --git a/src/main/java/emu/grasscutter/server/packet/send/PacketQuestListUpdateNotify.java b/src/main/java/emu/grasscutter/server/packet/send/PacketQuestListUpdateNotify.java new file mode 100644 index 000000000..adc0767a8 --- /dev/null +++ b/src/main/java/emu/grasscutter/server/packet/send/PacketQuestListUpdateNotify.java @@ -0,0 +1,20 @@ +package emu.grasscutter.server.packet.send; + +import emu.grasscutter.game.quest.GameMainQuest; +import emu.grasscutter.game.quest.GameQuest; +import emu.grasscutter.net.packet.BasePacket; +import emu.grasscutter.net.packet.PacketOpcodes; +import emu.grasscutter.net.proto.QuestListUpdateNotifyOuterClass.QuestListUpdateNotify; + +public class PacketQuestListUpdateNotify extends BasePacket { + + public PacketQuestListUpdateNotify(GameQuest quest) { + super(PacketOpcodes.QuestListUpdateNotify); + + QuestListUpdateNotify proto = QuestListUpdateNotify.newBuilder() + .addQuestList(quest.toProto()) + .build(); + + this.setData(proto); + } +} diff --git a/src/main/java/emu/grasscutter/server/packet/send/PacketQuestProgressUpdateNotify.java b/src/main/java/emu/grasscutter/server/packet/send/PacketQuestProgressUpdateNotify.java new file mode 100644 index 000000000..76ee56316 --- /dev/null +++ b/src/main/java/emu/grasscutter/server/packet/send/PacketQuestProgressUpdateNotify.java @@ -0,0 +1,30 @@ +package emu.grasscutter.server.packet.send; + +import emu.grasscutter.game.quest.GameMainQuest; +import emu.grasscutter.game.quest.GameQuest; +import emu.grasscutter.net.packet.BasePacket; +import emu.grasscutter.net.packet.PacketOpcodes; +import emu.grasscutter.net.proto.QuestProgressUpdateNotifyOuterClass.QuestProgressUpdateNotify; + +public class PacketQuestProgressUpdateNotify extends BasePacket { + + public PacketQuestProgressUpdateNotify(GameQuest quest) { + super(PacketOpcodes.QuestProgressUpdateNotify); + + QuestProgressUpdateNotify.Builder proto = QuestProgressUpdateNotify.newBuilder().setQuestId(quest.getQuestId()); + + if (quest.getFinishProgressList() != null) { + for (int i : quest.getFinishProgressList()) { + proto.addFinishProgressList(i); + } + } + + if (quest.getFailProgressList() != null) { + for (int i : quest.getFailProgressList()) { + proto.addFailProgressList(i); + } + } + + this.setData(proto); + } +} diff --git a/src/main/java/emu/grasscutter/server/packet/send/PacketSceneEntityAppearNotify.java b/src/main/java/emu/grasscutter/server/packet/send/PacketSceneEntityAppearNotify.java index 78c17a5d5..5e8bfa58e 100644 --- a/src/main/java/emu/grasscutter/server/packet/send/PacketSceneEntityAppearNotify.java +++ b/src/main/java/emu/grasscutter/server/packet/send/PacketSceneEntityAppearNotify.java @@ -15,7 +15,7 @@ public class PacketSceneEntityAppearNotify extends BasePacket { super(PacketOpcodes.SceneEntityAppearNotify, true); SceneEntityAppearNotify.Builder proto = SceneEntityAppearNotify.newBuilder() - .setAppearType(VisionType.VISION_BORN) + .setAppearType(VisionType.VISION_TYPE_BORN) .addEntityList(entity.toProto()); this.setData(proto.build()); diff --git a/src/main/java/emu/grasscutter/server/packet/send/PacketSceneTransToPointRsp.java b/src/main/java/emu/grasscutter/server/packet/send/PacketSceneTransToPointRsp.java index b421dfe40..17350c628 100644 --- a/src/main/java/emu/grasscutter/server/packet/send/PacketSceneTransToPointRsp.java +++ b/src/main/java/emu/grasscutter/server/packet/send/PacketSceneTransToPointRsp.java @@ -1,7 +1,7 @@ package emu.grasscutter.server.packet.send; import emu.grasscutter.data.GameData; -import emu.grasscutter.data.custom.ScenePointEntry; +import emu.grasscutter.data.binout.ScenePointEntry; import emu.grasscutter.game.player.Player; import emu.grasscutter.net.packet.BasePacket; import emu.grasscutter.net.packet.PacketOpcodes; diff --git a/src/main/java/emu/grasscutter/server/packet/send/PacketSceneUnlockInfoNotify.java b/src/main/java/emu/grasscutter/server/packet/send/PacketSceneUnlockInfoNotify.java deleted file mode 100644 index 223efc776..000000000 --- a/src/main/java/emu/grasscutter/server/packet/send/PacketSceneUnlockInfoNotify.java +++ /dev/null @@ -1,24 +0,0 @@ -package emu.grasscutter.server.packet.send; - -import emu.grasscutter.net.packet.BasePacket; -import emu.grasscutter.net.packet.PacketOpcodes; -import emu.grasscutter.net.proto.SceneUnlockInfoNotifyOuterClass.SceneUnlockInfoNotify; -import emu.grasscutter.net.proto.SceneUnlockInfoOuterClass.SceneUnlockInfo; - -public class PacketSceneUnlockInfoNotify extends BasePacket { - - public PacketSceneUnlockInfoNotify() { - super(PacketOpcodes.SceneUnlockInfoNotify); // Rename opcode later - - SceneUnlockInfoNotify proto = SceneUnlockInfoNotify.newBuilder() - .addUnlockInfos(SceneUnlockInfo.newBuilder().setSceneId(1)) - .addUnlockInfos(SceneUnlockInfo.newBuilder().setSceneId(3).addSceneTagIdList(102).addSceneTagIdList(113).addSceneTagIdList(117)) - .addUnlockInfos(SceneUnlockInfo.newBuilder().setSceneId(4).addSceneTagIdList(106).addSceneTagIdList(109)) - .addUnlockInfos(SceneUnlockInfo.newBuilder().setSceneId(5)) - .addUnlockInfos(SceneUnlockInfo.newBuilder().setSceneId(6)) - .addUnlockInfos(SceneUnlockInfo.newBuilder().setSceneId(7)) - .build(); - - this.setData(proto); - } -} diff --git a/src/main/java/emu/grasscutter/server/packet/send/PacketServerCondMeetQuestListUpdateNotify.java b/src/main/java/emu/grasscutter/server/packet/send/PacketServerCondMeetQuestListUpdateNotify.java new file mode 100644 index 000000000..278d4b04b --- /dev/null +++ b/src/main/java/emu/grasscutter/server/packet/send/PacketServerCondMeetQuestListUpdateNotify.java @@ -0,0 +1,36 @@ +package emu.grasscutter.server.packet.send; + +import emu.grasscutter.game.player.Player; +import emu.grasscutter.game.quest.GameQuest; +import emu.grasscutter.net.packet.BasePacket; +import emu.grasscutter.net.packet.PacketOpcodes; +import emu.grasscutter.net.proto.ServerCondMeetQuestListUpdateNotifyOuterClass.ServerCondMeetQuestListUpdateNotify; + +public class PacketServerCondMeetQuestListUpdateNotify extends BasePacket { + + public PacketServerCondMeetQuestListUpdateNotify(Player player) { + super(PacketOpcodes.ServerCondMeetQuestListUpdateNotify); + + ServerCondMeetQuestListUpdateNotify.Builder proto = ServerCondMeetQuestListUpdateNotify.newBuilder(); + + /* + player.getQuestManager().forEachQuest(quest -> { + if (quest.getState().getValue() <= 2) { + proto.addAddQuestIdList(quest.getQuestId()); + } + }); + */ + + this.setData(proto); + } + + public PacketServerCondMeetQuestListUpdateNotify(GameQuest quest) { + super(PacketOpcodes.ServerCondMeetQuestListUpdateNotify); + + ServerCondMeetQuestListUpdateNotify proto = ServerCondMeetQuestListUpdateNotify.newBuilder() + //.addAddQuestIdList(quest.getQuestId()) + .build(); + + this.setData(proto); + } +} diff --git a/src/main/java/emu/grasscutter/server/packet/send/PacketSetWidgetSlotRsp.java b/src/main/java/emu/grasscutter/server/packet/send/PacketSetWidgetSlotRsp.java new file mode 100644 index 000000000..0f81afa85 --- /dev/null +++ b/src/main/java/emu/grasscutter/server/packet/send/PacketSetWidgetSlotRsp.java @@ -0,0 +1,18 @@ +package emu.grasscutter.server.packet.send; + +import emu.grasscutter.net.packet.BasePacket; +import emu.grasscutter.net.packet.PacketOpcodes; +import emu.grasscutter.net.proto.SetWidgetSlotRspOuterClass; + +public class PacketSetWidgetSlotRsp extends BasePacket { + + public PacketSetWidgetSlotRsp(int materialId) { + super(PacketOpcodes.SetWidgetSlotRsp); + + SetWidgetSlotRspOuterClass.SetWidgetSlotRsp proto = SetWidgetSlotRspOuterClass.SetWidgetSlotRsp.newBuilder() + .setMaterialId(materialId) + .build(); + + this.setData(proto); + } +} diff --git a/src/main/java/emu/grasscutter/server/packet/send/PacketStoreItemChangeNotify.java b/src/main/java/emu/grasscutter/server/packet/send/PacketStoreItemChangeNotify.java index 4d951c0d6..4ad82c1b4 100644 --- a/src/main/java/emu/grasscutter/server/packet/send/PacketStoreItemChangeNotify.java +++ b/src/main/java/emu/grasscutter/server/packet/send/PacketStoreItemChangeNotify.java @@ -18,7 +18,7 @@ public class PacketStoreItemChangeNotify extends BasePacket { this(); StoreItemChangeNotify.Builder proto = StoreItemChangeNotify.newBuilder() - .setStoreType(StoreType.STORE_PACK) + .setStoreType(StoreType.STORE_TYPE_PACK) .addItemList(item.toProto()); this.setData(proto); @@ -28,7 +28,7 @@ public class PacketStoreItemChangeNotify extends BasePacket { this(); StoreItemChangeNotify.Builder proto = StoreItemChangeNotify.newBuilder() - .setStoreType(StoreType.STORE_PACK); + .setStoreType(StoreType.STORE_TYPE_PACK); items.forEach(item -> proto.addItemList(item.toProto())); diff --git a/src/main/java/emu/grasscutter/server/packet/send/PacketStoreItemDelNotify.java b/src/main/java/emu/grasscutter/server/packet/send/PacketStoreItemDelNotify.java index 197d4a32a..04a62ebf4 100644 --- a/src/main/java/emu/grasscutter/server/packet/send/PacketStoreItemDelNotify.java +++ b/src/main/java/emu/grasscutter/server/packet/send/PacketStoreItemDelNotify.java @@ -18,7 +18,7 @@ public class PacketStoreItemDelNotify extends BasePacket { this(); StoreItemDelNotify.Builder proto = StoreItemDelNotify.newBuilder() - .setStoreType(StoreType.STORE_PACK) + .setStoreType(StoreType.STORE_TYPE_PACK) .addGuidList(item.getGuid()); this.setData(proto); @@ -28,7 +28,7 @@ public class PacketStoreItemDelNotify extends BasePacket { this(); StoreItemDelNotify.Builder proto = StoreItemDelNotify.newBuilder() - .setStoreType(StoreType.STORE_PACK); + .setStoreType(StoreType.STORE_TYPE_PACK); items.stream().forEach(item -> proto.addGuidList(item.getGuid())); diff --git a/src/main/java/emu/grasscutter/server/packet/send/PacketStoreWeightLimitNotify.java b/src/main/java/emu/grasscutter/server/packet/send/PacketStoreWeightLimitNotify.java index 61b51948b..17600932b 100644 --- a/src/main/java/emu/grasscutter/server/packet/send/PacketStoreWeightLimitNotify.java +++ b/src/main/java/emu/grasscutter/server/packet/send/PacketStoreWeightLimitNotify.java @@ -1,23 +1,24 @@ package emu.grasscutter.server.packet.send; -import emu.grasscutter.Grasscutter; import emu.grasscutter.net.packet.BasePacket; import emu.grasscutter.net.packet.PacketOpcodes; import emu.grasscutter.net.proto.StoreTypeOuterClass.StoreType; import emu.grasscutter.net.proto.StoreWeightLimitNotifyOuterClass.StoreWeightLimitNotify; +import static emu.grasscutter.Configuration.*; + public class PacketStoreWeightLimitNotify extends BasePacket { public PacketStoreWeightLimitNotify() { super(PacketOpcodes.StoreWeightLimitNotify); StoreWeightLimitNotify p = StoreWeightLimitNotify.newBuilder() - .setStoreType(StoreType.STORE_PACK) - .setWeightLimit(Grasscutter.getConfig().getGameServerOptions().InventoryLimitAll) - .setWeaponCountLimit(Grasscutter.getConfig().getGameServerOptions().InventoryLimitWeapon) - .setReliquaryCountLimit(Grasscutter.getConfig().getGameServerOptions().InventoryLimitRelic) - .setMaterialCountLimit(Grasscutter.getConfig().getGameServerOptions().InventoryLimitMaterial) - .setFurnitureCountLimit(Grasscutter.getConfig().getGameServerOptions().InventoryLimitFurniture) + .setStoreType(StoreType.STORE_TYPE_PACK) + .setWeightLimit(INVENTORY_LIMITS.all) + .setWeaponCountLimit(INVENTORY_LIMITS.weapons) + .setReliquaryCountLimit(INVENTORY_LIMITS.relics) + .setMaterialCountLimit(INVENTORY_LIMITS.materials) + .setFurnitureCountLimit(INVENTORY_LIMITS.furniture) .build(); this.setData(p); diff --git a/src/main/java/emu/grasscutter/server/packet/send/PacketTowerAllDataRsp.java b/src/main/java/emu/grasscutter/server/packet/send/PacketTowerAllDataRsp.java index d2d2376e6..654aa4a07 100644 --- a/src/main/java/emu/grasscutter/server/packet/send/PacketTowerAllDataRsp.java +++ b/src/main/java/emu/grasscutter/server/packet/send/PacketTowerAllDataRsp.java @@ -1,37 +1,64 @@ package emu.grasscutter.server.packet.send; -import emu.grasscutter.data.GameData; -import emu.grasscutter.data.def.TowerFloorData; +import emu.grasscutter.game.tower.TowerManager; +import emu.grasscutter.game.tower.TowerScheduleManager; import emu.grasscutter.net.packet.BasePacket; import emu.grasscutter.net.packet.PacketOpcodes; import emu.grasscutter.net.proto.TowerAllDataRspOuterClass.TowerAllDataRsp; import emu.grasscutter.net.proto.TowerCurLevelRecordOuterClass.TowerCurLevelRecord; import emu.grasscutter.net.proto.TowerFloorRecordOuterClass.TowerFloorRecord; +import emu.grasscutter.net.proto.TowerLevelRecordOuterClass; +import emu.grasscutter.utils.DateHelper; +import java.util.List; +import java.util.Map; import java.util.stream.Collectors; +import java.util.stream.IntStream; public class PacketTowerAllDataRsp extends BasePacket { - public PacketTowerAllDataRsp() { + public PacketTowerAllDataRsp(TowerScheduleManager towerScheduleManager, TowerManager towerManager) { super(PacketOpcodes.TowerAllDataRsp); - var list = GameData.getTowerFloorDataMap().values().stream() - .map(TowerFloorData::getFloorId) - .map(id -> TowerFloorRecord.newBuilder().setFloorId(id).build()) - .collect(Collectors.toList()); + var recordList = towerManager.getRecordMap().values().stream() + .map(rec -> TowerFloorRecord.newBuilder() + .setFloorId(rec.getFloorId()) + .setFloorStarRewardProgress(rec.getFloorStarRewardProgress()) + .putAllPassedLevelMap(rec.getPassedLevelMap()) + .addAllPassedLevelRecordList(buildFromPassedLevelMap(rec.getPassedLevelMap())) + .build() + ) + .toList(); + + var openTimeMap = towerScheduleManager.getScheduleFloors().stream() + .collect(Collectors.toMap(x -> x, + y -> DateHelper.getUnixTime(towerScheduleManager.getTowerScheduleConfig() + .getScheduleStartTime())) + ); TowerAllDataRsp proto = TowerAllDataRsp.newBuilder() - .setTowerScheduleId(29) - .addAllTowerFloorRecordList(list) + .setTowerScheduleId(towerScheduleManager.getCurrentTowerScheduleData().getScheduleId()) + .addAllTowerFloorRecordList(recordList) .setCurLevelRecord(TowerCurLevelRecord.newBuilder().setIsEmpty(true)) - .setNextScheduleChangeTime(Integer.MAX_VALUE) - .putFloorOpenTimeMap(1024, 1630486800) - .putFloorOpenTimeMap(1025, 1630486800) - .putFloorOpenTimeMap(1026, 1630486800) - .putFloorOpenTimeMap(1027, 1630486800) - .setScheduleStartTime(1630486800) + .setScheduleStartTime(DateHelper.getUnixTime(towerScheduleManager.getTowerScheduleConfig() + .getScheduleStartTime())) + .setNextScheduleChangeTime(DateHelper.getUnixTime(towerScheduleManager.getTowerScheduleConfig() + .getNextScheduleChangeTime())) + .putAllFloorOpenTimeMap(openTimeMap) + .setIsFinishedEntranceFloor(towerManager.canEnterScheduleFloor()) .build(); this.setData(proto); } + + private List buildFromPassedLevelMap(Map map){ + return map.entrySet().stream() + .map(item -> TowerLevelRecordOuterClass.TowerLevelRecord.newBuilder() + .setLevelId(item.getKey()) + .addAllSatisfiedCondList(IntStream.range(1, item.getValue() + 1).boxed().toList()) + .build()) + .toList(); + + } + } diff --git a/src/main/java/emu/grasscutter/server/packet/send/PacketTowerFloorRecordChangeNotify.java b/src/main/java/emu/grasscutter/server/packet/send/PacketTowerFloorRecordChangeNotify.java index c0ed414a8..5ab091901 100644 --- a/src/main/java/emu/grasscutter/server/packet/send/PacketTowerFloorRecordChangeNotify.java +++ b/src/main/java/emu/grasscutter/server/packet/send/PacketTowerFloorRecordChangeNotify.java @@ -8,13 +8,13 @@ import emu.grasscutter.net.proto.TowerLevelRecordOuterClass.TowerLevelRecord; public class PacketTowerFloorRecordChangeNotify extends BasePacket { - public PacketTowerFloorRecordChangeNotify(int floorId) { + public PacketTowerFloorRecordChangeNotify(int floorId, int stars, boolean canEnterScheduleFloor) { super(PacketOpcodes.TowerFloorRecordChangeNotify); TowerFloorRecordChangeNotify proto = TowerFloorRecordChangeNotify.newBuilder() .addTowerFloorRecordList(TowerFloorRecord.newBuilder() .setFloorId(floorId) - .setFloorStarRewardProgress(3) + .setFloorStarRewardProgress(stars) .addPassedLevelRecordList(TowerLevelRecord.newBuilder() .setLevelId(1) .addSatisfiedCondList(1) @@ -22,7 +22,7 @@ public class PacketTowerFloorRecordChangeNotify extends BasePacket { .addSatisfiedCondList(3) .build()) .build()) - .setIsFinishedEntranceFloor(true) + .setIsFinishedEntranceFloor(canEnterScheduleFloor) .build(); this.setData(proto); diff --git a/src/main/java/emu/grasscutter/server/packet/send/PacketTowerLevelStarCondNotify.java b/src/main/java/emu/grasscutter/server/packet/send/PacketTowerLevelStarCondNotify.java new file mode 100644 index 000000000..c2c301e4e --- /dev/null +++ b/src/main/java/emu/grasscutter/server/packet/send/PacketTowerLevelStarCondNotify.java @@ -0,0 +1,32 @@ +package emu.grasscutter.server.packet.send; + +import emu.grasscutter.net.packet.BasePacket; +import emu.grasscutter.net.packet.PacketOpcodes; +import emu.grasscutter.net.proto.TowerLevelStarCondDataOuterClass.TowerLevelStarCondData; +import emu.grasscutter.net.proto.TowerLevelStarCondNotifyOuterClass.TowerLevelStarCondNotify; + +public class PacketTowerLevelStarCondNotify extends BasePacket { + + public PacketTowerLevelStarCondNotify(int floorId, int levelIndex) { + super(PacketOpcodes.TowerLevelStarCondNotify); + + TowerLevelStarCondNotify proto = TowerLevelStarCondNotify.newBuilder() + .setFloorId(floorId) + .setLevelIndex(levelIndex) + .addCondDataList(TowerLevelStarCondData.newBuilder() + .setCondValue(1) + .build() + ) + .addCondDataList(TowerLevelStarCondData.newBuilder() + .setCondValue(2) + .build() + ) + .addCondDataList(TowerLevelStarCondData.newBuilder() + .setCondValue(3) + .build() + ) + .build(); + + this.setData(proto); + } +} diff --git a/src/main/java/emu/grasscutter/server/packet/send/PacketTowerMiddleLevelChangeTeamNotify.java b/src/main/java/emu/grasscutter/server/packet/send/PacketTowerMiddleLevelChangeTeamNotify.java new file mode 100644 index 000000000..f778c68aa --- /dev/null +++ b/src/main/java/emu/grasscutter/server/packet/send/PacketTowerMiddleLevelChangeTeamNotify.java @@ -0,0 +1,18 @@ +package emu.grasscutter.server.packet.send; + +import emu.grasscutter.net.packet.BasePacket; +import emu.grasscutter.net.packet.PacketOpcodes; +import emu.grasscutter.net.proto.TowerMiddleLevelChangeTeamNotifyOuterClass; + +public class PacketTowerMiddleLevelChangeTeamNotify extends BasePacket { + + public PacketTowerMiddleLevelChangeTeamNotify() { + super(PacketOpcodes.TowerMiddleLevelChangeTeamNotify); + + TowerMiddleLevelChangeTeamNotifyOuterClass.TowerMiddleLevelChangeTeamNotify proto = + TowerMiddleLevelChangeTeamNotifyOuterClass.TowerMiddleLevelChangeTeamNotify.newBuilder() + .build(); + + this.setData(proto); + } +} diff --git a/src/main/java/emu/grasscutter/server/packet/send/PacketTryEnterHomeRsp.java b/src/main/java/emu/grasscutter/server/packet/send/PacketTryEnterHomeRsp.java new file mode 100644 index 000000000..369c44140 --- /dev/null +++ b/src/main/java/emu/grasscutter/server/packet/send/PacketTryEnterHomeRsp.java @@ -0,0 +1,30 @@ +package emu.grasscutter.server.packet.send; + +import emu.grasscutter.net.packet.BasePacket; +import emu.grasscutter.net.packet.PacketOpcodes; +import emu.grasscutter.net.proto.RetcodeOuterClass; +import emu.grasscutter.net.proto.TryEnterHomeRspOuterClass; + +public class PacketTryEnterHomeRsp extends BasePacket { + + public PacketTryEnterHomeRsp() { + super(PacketOpcodes.TryEnterHomeRsp); + + TryEnterHomeRspOuterClass.TryEnterHomeRsp proto = TryEnterHomeRspOuterClass.TryEnterHomeRsp.newBuilder() + .setRetcode(RetcodeOuterClass.Retcode.RET_SVR_ERROR_VALUE) + .build(); + + this.setData(proto); + } + + public PacketTryEnterHomeRsp(int uid) { + super(PacketOpcodes.TryEnterHomeRsp); + + TryEnterHomeRspOuterClass.TryEnterHomeRsp proto = TryEnterHomeRspOuterClass.TryEnterHomeRsp.newBuilder() + .setRetcode(0) + .setTargetUid(uid) + .build(); + + this.setData(proto); + } +} diff --git a/src/main/java/emu/grasscutter/server/packet/send/PacketVehicleInteractRsp.java b/src/main/java/emu/grasscutter/server/packet/send/PacketVehicleInteractRsp.java index 989aa3876..d6863cc4e 100644 --- a/src/main/java/emu/grasscutter/server/packet/send/PacketVehicleInteractRsp.java +++ b/src/main/java/emu/grasscutter/server/packet/send/PacketVehicleInteractRsp.java @@ -31,10 +31,10 @@ public class PacketVehicleInteractRsp extends BasePacket { proto.setMember(vehicleMember); switch(interactType){ - case VEHICLE_INTERACT_IN -> { + case VEHICLE_INTERACT_TYPE_IN -> { ((EntityVehicle) vehicle).getVehicleMembers().add(vehicleMember); } - case VEHICLE_INTERACT_OUT -> { + case VEHICLE_INTERACT_TYPE_OUT -> { ((EntityVehicle) vehicle).getVehicleMembers().remove(vehicleMember); } default -> {} @@ -53,10 +53,10 @@ public class PacketVehicleInteractRsp extends BasePacket { proto.setMember(vehicleMember); switch(interactType){ - case VEHICLE_INTERACT_IN -> { + case VEHICLE_INTERACT_TYPE_IN -> { vehicle.getVehicleMembers().add(vehicleMember); } - case VEHICLE_INTERACT_OUT -> { + case VEHICLE_INTERACT_TYPE_OUT -> { vehicle.getVehicleMembers().remove(vehicleMember); } default -> {} diff --git a/src/main/java/emu/grasscutter/server/packet/send/PacketVehicleStaminaNotify.java b/src/main/java/emu/grasscutter/server/packet/send/PacketVehicleStaminaNotify.java new file mode 100644 index 000000000..0a6a315e3 --- /dev/null +++ b/src/main/java/emu/grasscutter/server/packet/send/PacketVehicleStaminaNotify.java @@ -0,0 +1,18 @@ +package emu.grasscutter.server.packet.send; + +import emu.grasscutter.net.packet.BasePacket; +import emu.grasscutter.net.packet.PacketOpcodes; +import emu.grasscutter.net.proto.VehicleStaminaNotifyOuterClass.VehicleStaminaNotify; + +public class PacketVehicleStaminaNotify extends BasePacket { + + public PacketVehicleStaminaNotify(int vehicleId, float newStamina) { + super(PacketOpcodes.VehicleStaminaNotify); + VehicleStaminaNotify.Builder proto = VehicleStaminaNotify.newBuilder(); + + proto.setEntityId(vehicleId); + proto.setCurStamina(newStamina); + + this.setData(proto.build()); + } +} diff --git a/src/main/java/emu/grasscutter/server/packet/send/PacketWidgetCoolDownNotify.java b/src/main/java/emu/grasscutter/server/packet/send/PacketWidgetCoolDownNotify.java new file mode 100644 index 000000000..a73187020 --- /dev/null +++ b/src/main/java/emu/grasscutter/server/packet/send/PacketWidgetCoolDownNotify.java @@ -0,0 +1,25 @@ +package emu.grasscutter.server.packet.send; + +import emu.grasscutter.net.packet.BasePacket; +import emu.grasscutter.net.packet.PacketOpcodes; +import emu.grasscutter.net.proto.WidgetCoolDownDataOuterClass; +import emu.grasscutter.net.proto.WidgetCoolDownNotifyOuterClass; + +public class PacketWidgetCoolDownNotify extends BasePacket { + + public PacketWidgetCoolDownNotify(int id, long coolDownTime, boolean isSuccess) { + super(PacketOpcodes.WidgetCoolDownNotify); + + WidgetCoolDownNotifyOuterClass.WidgetCoolDownNotify proto = WidgetCoolDownNotifyOuterClass.WidgetCoolDownNotify.newBuilder() + .addGroupCoolDownDataList( + WidgetCoolDownDataOuterClass.WidgetCoolDownData.newBuilder() + .setId(id) + .setCoolDownTime(coolDownTime) + .setIsSuccess(isSuccess) + .build() + ) + .build(); + + this.setData(proto); + } +} diff --git a/src/main/java/emu/grasscutter/server/packet/send/PacketWidgetDoBagRsp.java b/src/main/java/emu/grasscutter/server/packet/send/PacketWidgetDoBagRsp.java new file mode 100644 index 000000000..7ce5065ea --- /dev/null +++ b/src/main/java/emu/grasscutter/server/packet/send/PacketWidgetDoBagRsp.java @@ -0,0 +1,28 @@ +package emu.grasscutter.server.packet.send; + +import emu.grasscutter.net.packet.BasePacket; +import emu.grasscutter.net.packet.PacketOpcodes; +import emu.grasscutter.net.proto.WidgetDoBagRspOuterClass; + +public class PacketWidgetDoBagRsp extends BasePacket { + + public PacketWidgetDoBagRsp(int materialId) { + super(PacketOpcodes.WidgetDoBagRsp); + + WidgetDoBagRspOuterClass.WidgetDoBagRsp proto = WidgetDoBagRspOuterClass.WidgetDoBagRsp.newBuilder() + .setMaterialId(materialId) + .setRetcode(0) + .build(); + + this.setData(proto); + } + + public PacketWidgetDoBagRsp() { + super(PacketOpcodes.WidgetDoBagRsp); + + WidgetDoBagRspOuterClass.WidgetDoBagRsp proto = WidgetDoBagRspOuterClass.WidgetDoBagRsp.newBuilder() + .build(); + + this.setData(proto); + } +} diff --git a/src/main/java/emu/grasscutter/server/packet/send/PacketWidgetGadgetAllDataNotify.java b/src/main/java/emu/grasscutter/server/packet/send/PacketWidgetGadgetAllDataNotify.java new file mode 100644 index 000000000..b0000efb7 --- /dev/null +++ b/src/main/java/emu/grasscutter/server/packet/send/PacketWidgetGadgetAllDataNotify.java @@ -0,0 +1,16 @@ +package emu.grasscutter.server.packet.send; + +import emu.grasscutter.net.packet.BasePacket; +import emu.grasscutter.net.packet.PacketOpcodes; +import emu.grasscutter.net.proto.WidgetGadgetAllDataNotifyOuterClass.WidgetGadgetAllDataNotify; + +public class PacketWidgetGadgetAllDataNotify extends BasePacket { + + public PacketWidgetGadgetAllDataNotify() { + super(PacketOpcodes.AllWidgetDataNotify); + + WidgetGadgetAllDataNotify proto = WidgetGadgetAllDataNotify.newBuilder().build(); + + this.setData(proto); + } +} diff --git a/src/main/java/emu/grasscutter/server/packet/send/PacketWidgetGadgetDataNotify.java b/src/main/java/emu/grasscutter/server/packet/send/PacketWidgetGadgetDataNotify.java new file mode 100644 index 000000000..f94c6c10e --- /dev/null +++ b/src/main/java/emu/grasscutter/server/packet/send/PacketWidgetGadgetDataNotify.java @@ -0,0 +1,26 @@ +package emu.grasscutter.server.packet.send; + +import emu.grasscutter.net.packet.BasePacket; +import emu.grasscutter.net.packet.PacketOpcodes; +import emu.grasscutter.net.proto.WidgetGadgetDataNotifyOuterClass; +import emu.grasscutter.net.proto.WidgetGadgetDataOuterClass; + +import java.io.IOException; +import java.util.List; + +public class PacketWidgetGadgetDataNotify extends BasePacket { + public PacketWidgetGadgetDataNotify(int gadgetId, List gadgetEntityIdList) throws IOException { + super(PacketOpcodes.WidgetGadgetDataNotify); + + WidgetGadgetDataNotifyOuterClass.WidgetGadgetDataNotify proto = WidgetGadgetDataNotifyOuterClass.WidgetGadgetDataNotify.newBuilder() + .setWidgetGadgetData( + WidgetGadgetDataOuterClass.WidgetGadgetData.newBuilder() + .setGadgetId(gadgetId) + .addAllGadgetEntityIdList(gadgetEntityIdList) + .build() + ) + .build(); + + this.setData(proto); + } +} diff --git a/src/main/java/emu/grasscutter/server/packet/send/PacketWidgetSlotChangeNotify.java b/src/main/java/emu/grasscutter/server/packet/send/PacketWidgetSlotChangeNotify.java new file mode 100644 index 000000000..ab0ace7eb --- /dev/null +++ b/src/main/java/emu/grasscutter/server/packet/send/PacketWidgetSlotChangeNotify.java @@ -0,0 +1,47 @@ +package emu.grasscutter.server.packet.send; + +import emu.grasscutter.net.packet.BasePacket; +import emu.grasscutter.net.packet.PacketOpcodes; +import emu.grasscutter.net.proto.WidgetSlotChangeNotifyOuterClass; +import emu.grasscutter.net.proto.WidgetSlotDataOuterClass; +import emu.grasscutter.net.proto.WidgetSlotOpOuterClass; + +public class PacketWidgetSlotChangeNotify extends BasePacket { + + public PacketWidgetSlotChangeNotify(WidgetSlotChangeNotifyOuterClass.WidgetSlotChangeNotify proto) { + super(PacketOpcodes.WidgetSlotChangeNotify); + + this.setData(proto); + } + + public PacketWidgetSlotChangeNotify(WidgetSlotOpOuterClass.WidgetSlotOp op) { + super(PacketOpcodes.WidgetSlotChangeNotify); + + WidgetSlotChangeNotifyOuterClass.WidgetSlotChangeNotify proto = WidgetSlotChangeNotifyOuterClass.WidgetSlotChangeNotify.newBuilder() + .setOp(op) + .setSlot( + WidgetSlotDataOuterClass.WidgetSlotData.newBuilder() + .setIsActive(true) + .build() + ) + .build(); + + this.setData(proto); + } + + public PacketWidgetSlotChangeNotify(int materialId) { + super(PacketOpcodes.WidgetSlotChangeNotify); + + WidgetSlotChangeNotifyOuterClass.WidgetSlotChangeNotify proto = WidgetSlotChangeNotifyOuterClass.WidgetSlotChangeNotify.newBuilder() + .setSlot( + WidgetSlotDataOuterClass.WidgetSlotData.newBuilder() + .setIsActive(true) + .setMaterialId(materialId) + .build() + ) + .build(); + + this.setData(proto); + } + +} diff --git a/src/main/java/emu/grasscutter/task/Task.java b/src/main/java/emu/grasscutter/task/Task.java index 1f35d16ce..2c930c0e3 100644 --- a/src/main/java/emu/grasscutter/task/Task.java +++ b/src/main/java/emu/grasscutter/task/Task.java @@ -1,7 +1,5 @@ package emu.grasscutter.task; -import org.quartz.JobDataMap; - import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; diff --git a/src/main/java/emu/grasscutter/task/TaskMap.java b/src/main/java/emu/grasscutter/task/TaskMap.java index fe067e795..a4b2ff02f 100644 --- a/src/main/java/emu/grasscutter/task/TaskMap.java +++ b/src/main/java/emu/grasscutter/task/TaskMap.java @@ -67,6 +67,40 @@ public final class TaskMap { return this; } + public boolean pauseTask(String taskName) { + try { + Scheduler scheduler = schedulerFactory.getScheduler(); + scheduler.pauseJob(new JobKey(taskName)); + } catch (SchedulerException e) { + e.printStackTrace(); + return false; + } + return true; + } + + public boolean resumeTask(String taskName) { + try { + Scheduler scheduler = schedulerFactory.getScheduler(); + scheduler.resumeJob(new JobKey(taskName)); + } catch (SchedulerException e) { + e.printStackTrace(); + return false; + } + return true; + } + + public boolean cancelTask(String taskName) { + Task task = this.annotations.get(taskName); + if (task == null) return false; + try { + this.unregisterTask(this.tasks.get(taskName)); + } catch (Exception e) { + e.printStackTrace(); + return false; + } + return true; + } + public TaskMap registerTask(String taskName, TaskHandler task) { Task annotation = task.getClass().getAnnotation(Task.class); this.annotations.put(taskName, annotation); @@ -116,7 +150,7 @@ public final class TaskMap { classes.forEach(annotated -> { try { Task taskData = annotated.getAnnotation(Task.class); - Object object = annotated.newInstance(); + Object object = annotated.getDeclaredConstructor().newInstance(); if (object instanceof TaskHandler) { this.registerTask(taskData.taskName(), (TaskHandler) object); if (taskData.executeImmediatelyAfterReset()) { diff --git a/src/main/java/emu/grasscutter/tools/Tools.java b/src/main/java/emu/grasscutter/tools/Tools.java index d9923a656..1a1ac2196 100644 --- a/src/main/java/emu/grasscutter/tools/Tools.java +++ b/src/main/java/emu/grasscutter/tools/Tools.java @@ -1,12 +1,8 @@ package emu.grasscutter.tools; -import java.io.BufferedReader; import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; -import java.io.FileReader; -import java.io.FileWriter; -import java.io.FilenameFilter; import java.io.InputStreamReader; import java.io.OutputStreamWriter; import java.io.PrintWriter; @@ -14,7 +10,6 @@ import java.nio.charset.StandardCharsets; import java.time.LocalDateTime; import java.time.format.DateTimeFormatter; import java.util.*; -import java.util.stream.Collectors; import com.google.gson.reflect.TypeToken; @@ -24,12 +19,17 @@ import emu.grasscutter.command.Command; import emu.grasscutter.command.CommandMap; import emu.grasscutter.data.GameData; import emu.grasscutter.data.ResourceLoader; -import emu.grasscutter.data.def.AvatarData; -import emu.grasscutter.data.def.ItemData; -import emu.grasscutter.data.def.MonsterData; -import emu.grasscutter.data.def.SceneData; +import emu.grasscutter.data.binout.MainQuestData; +import emu.grasscutter.data.excels.AvatarData; +import emu.grasscutter.data.excels.ItemData; +import emu.grasscutter.data.excels.MonsterData; +import emu.grasscutter.data.excels.QuestData; +import emu.grasscutter.data.excels.SceneData; import emu.grasscutter.utils.Utils; +import static emu.grasscutter.utils.Language.translate; +import static emu.grasscutter.Configuration.*; + public final class Tools { public static void createGmHandbook() throws Exception { ToolsWithLanguageOption.createGmHandbook(getLanguageOption()); @@ -39,50 +39,45 @@ public final class Tools { ToolsWithLanguageOption.createGachaMapping(location, getLanguageOption()); } - public static List getAvailableLanguage() throws Exception { - File textMapFolder = new File(Grasscutter.getConfig().RESOURCE_FOLDER + "TextMap"); - List availableLangList = new ArrayList(); - for (String textMapFileName : textMapFolder.list(new FilenameFilter() { - @Override - public boolean accept(File dir, String name) { - if (name.startsWith("TextMap") && name.endsWith(".json")){ - return true; - } - return false; - } - })) { - availableLangList.add(textMapFileName.replace("TextMap","").replace(".json","").toLowerCase()); - } - return availableLangList; + public static List getAvailableLanguage() { + File textMapFolder = new File(RESOURCE("TextMap")); + List availableLangList = new ArrayList<>(); + for (String textMapFileName : Objects.requireNonNull(textMapFolder.list((dir, name) -> name.startsWith("TextMap") && name.endsWith(".json")))) { + availableLangList.add(textMapFileName.replace("TextMap", "").replace(".json", "").toLowerCase()); + } return availableLangList; } - public static String getLanguageOption() throws Exception { + public static String getLanguageOption() { List availableLangList = getAvailableLanguage(); // Use system out for better format if (availableLangList.size() == 1) { return availableLangList.get(0).toUpperCase(); } - String stagedMessage = ""; - stagedMessage += "The following languages mappings are available, please select one: [default: EN]\n"; - String groupedLangList = ">\t"; + StringBuilder stagedMessage = new StringBuilder(); + stagedMessage.append("The following languages mappings are available, please select one: [default: EN] \n"); + + StringBuilder groupedLangList = new StringBuilder(">\t"); String input; int groupedLangCount = 0; - String input = ""; + for (String availableLanguage: availableLangList){ groupedLangCount++; - groupedLangList = groupedLangList + "" + availableLanguage + "\t"; + groupedLangList.append(availableLanguage).append("\t"); + if (groupedLangCount == 6) { - stagedMessage += groupedLangList + "\n"; + stagedMessage.append(groupedLangList).append("\n"); groupedLangCount = 0; - groupedLangList = ">\t"; + groupedLangList = new StringBuilder(">\t"); } } - if (groupedLangCount > 0) { - stagedMessage += groupedLangList + "\n"; - } - stagedMessage += "\nYour choice:[EN] "; - input = Grasscutter.getConsole().readLine(stagedMessage); + if (groupedLangCount > 0) { + stagedMessage.append(groupedLangList).append("\n"); + } + + stagedMessage.append("\nYour choice:[EN] "); + + input = Grasscutter.getConsole().readLine(stagedMessage.toString()); if (availableLangList.contains(input.toLowerCase())) { return input.toUpperCase(); } @@ -95,10 +90,10 @@ public final class Tools { final class ToolsWithLanguageOption { @SuppressWarnings("deprecation") public static void createGmHandbook(String language) throws Exception { - ResourceLoader.loadResources(); + ResourceLoader.loadAll(); Map map; - try (InputStreamReader fileReader = new InputStreamReader(new FileInputStream(Utils.toFilePath(Grasscutter.getConfig().RESOURCE_FOLDER + "TextMap/TextMap"+language+".json")), StandardCharsets.UTF_8)) { + try (InputStreamReader fileReader = new InputStreamReader(new FileInputStream(Utils.toFilePath(RESOURCE("TextMap/TextMap"+language+".json"))), StandardCharsets.UTF_8)) { map = Grasscutter.getGsonFactory().fromJson(fileReader, new TypeToken>() {}.getType()); } @@ -116,13 +111,12 @@ final class ToolsWithLanguageOption { writer.println("// Commands"); for (Command cmd : cmdList) { - String cmdName = cmd.label(); + StringBuilder cmdName = new StringBuilder(cmd.label()); while (cmdName.length() <= 15) { - cmdName = " " + cmdName; + cmdName.insert(0, " "); } - writer.println(cmdName + " : " + cmd.description()); + writer.println(cmdName + " : " + translate(cmd.description())); } - writer.println(); list = new ArrayList<>(GameData.getAvatarDataMap().keySet()); @@ -158,6 +152,18 @@ final class ToolsWithLanguageOption { writer.println(); + writer.println("// Quests"); + list = new ArrayList<>(GameData.getQuestDataMap().keySet()); + Collections.sort(list); + + for (Integer id : list) { + QuestData data = GameData.getQuestDataMap().get(id); + MainQuestData mainQuest = GameData.getMainQuestDataMap().get(data.getMainId()); + writer.println(data.getId() + " : " + map.get(mainQuest.getTitleTextMapHash()) + " - " + map.get(data.getDescTextMapHash())); + } + + writer.println(); + writer.println("// Monsters"); list = new ArrayList<>(GameData.getMonsterDataMap().keySet()); Collections.sort(list); @@ -176,16 +182,13 @@ final class ToolsWithLanguageOption { ResourceLoader.loadResources(); Map map; - try (InputStreamReader fileReader = new InputStreamReader(new FileInputStream(Utils.toFilePath(Grasscutter.getConfig().RESOURCE_FOLDER + "TextMap/TextMap"+language+".json")), StandardCharsets.UTF_8)) { + try (InputStreamReader fileReader = new InputStreamReader(new FileInputStream(Utils.toFilePath(RESOURCE("TextMap/TextMap" + language + ".json"))), StandardCharsets.UTF_8)) { map = Grasscutter.getGsonFactory().fromJson(fileReader, new TypeToken>() {}.getType()); } List list; - String fileName = location; - - try (PrintWriter writer = new PrintWriter(new OutputStreamWriter(new FileOutputStream(fileName), StandardCharsets.UTF_8), false)) { - + try (PrintWriter writer = new PrintWriter(new OutputStreamWriter(new FileOutputStream(location), StandardCharsets.UTF_8), false)) { list = new ArrayList<>(GameData.getAvatarDataMap().keySet()); Collections.sort(list); @@ -207,18 +210,11 @@ final class ToolsWithLanguageOption { } else { writer.print(","); } - String color; - switch (data.getQualityType()){ - case "QUALITY_PURPLE": - color = "purple"; - break; - case "QUALITY_ORANGE": - color = "yellow"; - break; - case "QUALITY_BLUE": - default: - color = "blue"; - } + String color = switch (data.getQualityType()) { + case "QUALITY_PURPLE" -> "purple"; + case "QUALITY_ORANGE" -> "yellow"; + default -> "blue"; + }; // Got the magic number 4233146695 from manually search in the json file writer.println( "\"" + (avatarID % 1000 + 1000) + "\" : [\"" diff --git a/src/main/java/emu/grasscutter/utils/ConfigContainer.java b/src/main/java/emu/grasscutter/utils/ConfigContainer.java new file mode 100644 index 000000000..a61f5f42e --- /dev/null +++ b/src/main/java/emu/grasscutter/utils/ConfigContainer.java @@ -0,0 +1,259 @@ +package emu.grasscutter.utils; + +import com.google.gson.JsonObject; +import emu.grasscutter.Grasscutter; +import emu.grasscutter.Grasscutter.ServerDebugMode; +import emu.grasscutter.Grasscutter.ServerRunMode; + +import java.io.FileReader; +import java.lang.reflect.Field; +import java.util.Arrays; +import java.util.Locale; + +import static emu.grasscutter.Grasscutter.config; + +/** + * *when your JVM fails* + */ +public class ConfigContainer { + private static int version() { + return 3; + } + + /** + * Attempts to update the server's existing configuration to the latest + */ + public static void updateConfig() { + try { // Check if the server is using a legacy config. + JsonObject configObject = Grasscutter.getGsonFactory() + .fromJson(new FileReader(Grasscutter.configFile), JsonObject.class); + if(!configObject.has("version")) { + Grasscutter.getLogger().info("Updating legacy .."); + Grasscutter.saveConfig(null); + } + } catch (Exception ignored) { } + + var existing = config.version; + var latest = version(); + + if(existing == latest) + return; + + // Create a new configuration instance. + ConfigContainer updated = new ConfigContainer(); + // Update all configuration fields. + Field[] fields = ConfigContainer.class.getDeclaredFields(); + Arrays.stream(fields).forEach(field -> { + try { + field.set(updated, field.get(config)); + } catch (Exception exception) { + Grasscutter.getLogger().error("Failed to update a configuration field.", exception); + } + }); updated.version = version(); + + try { // Save configuration & reload. + Grasscutter.saveConfig(updated); + Grasscutter.loadConfig(); + } catch (Exception exception) { + Grasscutter.getLogger().warn("Failed to inject the updated ", exception); + } + } + + public Structure folderStructure = new Structure(); + public Database databaseInfo = new Database(); + public Language language = new Language(); + public Account account = new Account(); + public Server server = new Server(); + + // DO NOT. TOUCH. THE VERSION NUMBER. + public int version = version(); + + /* Option containers. */ + + public static class Database { + public DataStore server = new DataStore(); + public DataStore game = new DataStore(); + + public static class DataStore { + public String connectionUri = "mongodb://localhost:27017"; + public String collection = "grasscutter"; + } + } + + public static class Structure { + public String resources = "./resources/"; + public String data = "./data/"; + public String packets = "./packets/"; + public String scripts = "./resources/Scripts/"; + public String plugins = "./plugins/"; + + // UNUSED (potentially added later?) + // public String dumps = "./dumps/"; + } + + public static class Server { + public ServerDebugMode debugLevel = ServerDebugMode.NONE; + public ServerRunMode runMode = ServerRunMode.HYBRID; + + public HTTP http = new HTTP(); + public Game game = new Game(); + + public Dispatch dispatch = new Dispatch(); + } + + public static class Language { + public Locale language = Locale.getDefault(); + public Locale fallback = Locale.US; + public String document = "EN"; + } + + public static class Account { + public boolean autoCreate = false; + public String[] defaultPermissions = {}; + public int maxPlayer = -1; + } + + /* Server options. */ + + public static class HTTP { + public String bindAddress = "0.0.0.0"; + /* This is the address used in URLs. */ + public String accessAddress = "127.0.0.1"; + + public int bindPort = 443; + /* This is the port used in URLs. */ + public int accessPort = 0; + + public Encryption encryption = new Encryption(); + public Policies policies = new Policies(); + public Files files = new Files(); + } + + public static class Game { + public String bindAddress = "0.0.0.0"; + /* This is the address used in the default region. */ + public String accessAddress = "127.0.0.1"; + + public int bindPort = 22102; + /* This is the port used in the default region. */ + public int accessPort = 0; + public boolean enableConsole = true; + public GameOptions gameOptions = new GameOptions(); + public JoinOptions joinOptions = new JoinOptions(); + public ConsoleAccount serverAccount = new ConsoleAccount(); + } + + /* Data containers. */ + + public static class Dispatch { + public Region[] regions = {}; + + public String defaultName = "Grasscutter"; + } + + public static class Encryption { + public boolean useEncryption = true; + /* Should 'https' be appended to URLs? */ + public boolean useInRouting = true; + public String keystore = "./keystore.p12"; + public String keystorePassword = "123456"; + } + + public static class Policies { + public Policies.CORS cors = new Policies.CORS(); + + public static class CORS { + public boolean enabled = false; + public String[] allowedOrigins = new String[]{"*"}; + } + } + + public static class GameOptions { + public InventoryLimits inventoryLimits = new InventoryLimits(); + public AvatarLimits avatarLimits = new AvatarLimits(); + public int sceneEntityLimit = 1000; // Unenforced. TODO: Implement. + + public boolean watchGachaConfig = false; + public boolean enableShopItems = true; + public boolean staminaUsage = true; + public boolean energyUsage = false; + public Rates rates = new Rates(); + + public static class InventoryLimits { + public int weapons = 2000; + public int relics = 2000; + public int materials = 2000; + public int furniture = 2000; + public int all = 30000; + } + + public static class AvatarLimits { + public int singlePlayerTeam = 4; + public int multiplayerTeam = 4; + } + + public static class Rates { + public float adventureExp = 1.0f; + public float mora = 1.0f; + public float leyLines = 1.0f; + } + } + + public static class JoinOptions { + public int[] welcomeEmotes = {2007, 1002, 4010}; + public String welcomeMessage = "Welcome to a Grasscutter server."; + public JoinOptions.Mail welcomeMail = new JoinOptions.Mail(); + + public static class Mail { + public String title = "Welcome to Grasscutter!"; + public String content = """ + Hi there!\r + First of all, welcome to Grasscutter. If you have any issues, please let us know so that Lawnmower can help you! \r + \r + Check out our:\r + + """; + public String sender = "Lawnmower"; + public emu.grasscutter.game.mail.Mail.MailItem[] items = { + new emu.grasscutter.game.mail.Mail.MailItem(13509, 1, 1), + new emu.grasscutter.game.mail.Mail.MailItem(201, 99999, 1) + }; + } + } + + public static class ConsoleAccount { + public int avatarId = 10000007; + public int nameCardId = 210001; + public int adventureRank = 1; + public int worldLevel = 0; + + public String nickName = "Server"; + public String signature = "Welcome to Grasscutter!"; + } + + public static class Files { + public String indexFile = "./index.html"; + public String errorFile = "./404.html"; + } + + /* Objects. */ + + public static class Region { + public Region() { } + + public Region( + String name, String title, + String address, int port + ) { + this.Name = name; + this.Title = title; + this.Ip = address; + this.Port = port; + } + + public String Name = "os_usa"; + public String Title = "Grasscutter"; + public String Ip = "127.0.0.1"; + public int Port = 22102; + } +} diff --git a/src/main/java/emu/grasscutter/utils/Crypto.java b/src/main/java/emu/grasscutter/utils/Crypto.java index e6d260e94..3bde63aa7 100644 --- a/src/main/java/emu/grasscutter/utils/Crypto.java +++ b/src/main/java/emu/grasscutter/utils/Crypto.java @@ -7,18 +7,24 @@ import emu.grasscutter.Grasscutter; import emu.grasscutter.net.proto.GetPlayerTokenRspOuterClass.GetPlayerTokenRsp; import emu.grasscutter.net.proto.QueryCurrRegionHttpRspOuterClass.QueryCurrRegionHttpRsp; +import static emu.grasscutter.Configuration.*; + public final class Crypto { private static final SecureRandom secureRandom = new SecureRandom(); - public static final long ENCRYPT_SEED = Long.parseUnsignedLong("11468049314633205968"); - public static byte[] ENCRYPT_SEED_BUFFER = new byte[0]; public static byte[] DISPATCH_KEY; + public static byte[] DISPATCH_SEED; + public static byte[] ENCRYPT_KEY; + public static long ENCRYPT_SEED = Long.parseUnsignedLong("11468049314633205968"); + public static byte[] ENCRYPT_SEED_BUFFER = new byte[0]; public static void loadKeys() { - DISPATCH_KEY = FileUtils.read(Grasscutter.getConfig().KEY_FOLDER + "dispatchKey.bin"); - ENCRYPT_KEY = FileUtils.read(Grasscutter.getConfig().KEY_FOLDER + "secretKey.bin"); - ENCRYPT_SEED_BUFFER = FileUtils.read(Grasscutter.getConfig().KEY_FOLDER + "secretKeyBuffer.bin"); + DISPATCH_KEY = FileUtils.readResource("/keys/dispatchKey.bin"); + DISPATCH_SEED = FileUtils.readResource("/keys/dispatchSeed.bin"); + + ENCRYPT_KEY = FileUtils.readResource("/keys/secretKey.bin"); + ENCRYPT_SEED_BUFFER = FileUtils.readResource("/keys/secretKeyBuffer.bin"); } public static void xor(byte[] packet, byte[] key) { @@ -31,28 +37,9 @@ public final class Crypto { } } - public static void extractSecretKeyBuffer(byte[] data) { - try { - GetPlayerTokenRsp p = GetPlayerTokenRsp.parseFrom(data); - FileUtils.write(Grasscutter.getConfig().KEY_FOLDER + "secretKeyBuffer.bin", p.getSecretKeyBytes().toByteArray()); - Grasscutter.getLogger().info("Secret Key: " + p.getSecretKey()); - } catch (Exception e) { - Grasscutter.getLogger().error("Crypto error.", e); - } - } - - public static void extractDispatchSeed(String data) { - try { - QueryCurrRegionHttpRsp p = QueryCurrRegionHttpRsp.parseFrom(Base64.getDecoder().decode(data)); - FileUtils.write(Grasscutter.getConfig().KEY_FOLDER + "dispatchSeed.bin", p.getRegionInfo().getSecretKey().toByteArray()); - } catch (Exception e) { - Grasscutter.getLogger().error("Crypto error.", e); - } - } - public static byte[] createSessionKey(int length) { byte[] bytes = new byte[length]; secureRandom.nextBytes(bytes); - return bytes; + return bytes; } } diff --git a/src/main/java/emu/grasscutter/utils/DateHelper.java b/src/main/java/emu/grasscutter/utils/DateHelper.java index 7005d9457..1f1393760 100644 --- a/src/main/java/emu/grasscutter/utils/DateHelper.java +++ b/src/main/java/emu/grasscutter/utils/DateHelper.java @@ -1,7 +1,7 @@ package emu.grasscutter.utils; -import java.util.Date; import java.util.Calendar; +import java.util.Date; public final class DateHelper { public static Date onlyYearMonthDay(Date now) { @@ -13,4 +13,8 @@ public final class DateHelper { calendar.set(Calendar.MILLISECOND, 0); return calendar.getTime(); } + + public static int getUnixTime(Date localDateTime){ + return (int)(localDateTime.getTime() / 1000L); + } } diff --git a/src/main/java/emu/grasscutter/utils/FileUtils.java b/src/main/java/emu/grasscutter/utils/FileUtils.java index 06e6087b3..ae0be71ca 100644 --- a/src/main/java/emu/grasscutter/utils/FileUtils.java +++ b/src/main/java/emu/grasscutter/utils/FileUtils.java @@ -4,9 +4,15 @@ import emu.grasscutter.Grasscutter; import java.io.File; import java.io.IOException; -import java.nio.file.Files; -import java.nio.file.Path; -import java.nio.file.Paths; +import java.io.InputStream; +import java.net.URI; +import java.net.URISyntaxException; +import java.nio.charset.StandardCharsets; +import java.nio.file.*; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; +import java.util.stream.Collectors; public final class FileUtils { public static void write(String dest, byte[] bytes) { @@ -32,10 +38,34 @@ public final class FileUtils { return new byte[0]; } + + public static InputStream readResourceAsStream(String resourcePath) { + return Grasscutter.class.getResourceAsStream(resourcePath); + } + + public static byte[] readResource(String resourcePath) { + try (InputStream is = Grasscutter.class.getResourceAsStream(resourcePath)) { + return is.readAllBytes(); + } catch (Exception exception) { + Grasscutter.getLogger().warn("Failed to read resource: " + resourcePath); + exception.printStackTrace(); + } + + return new byte[0]; + } public static byte[] read(File file) { return read(file.getPath()); } + + public static void copyResource(String resourcePath, String destination) { + try { + byte[] resource = FileUtils.readResource(resourcePath); + FileUtils.write(destination, resource); + } catch (Exception exception) { + Grasscutter.getLogger().warn("Failed to copy resource: " + resourcePath + "\n" + exception); + } + } public static String getFilenameWithoutPath(String fileName) { if (fileName.indexOf(".") > 0) { @@ -44,4 +74,43 @@ public final class FileUtils { return fileName; } } + + // From https://mkyong.com/java/java-read-a-file-from-resources-folder/ + public static List getPathsFromResource(String folder) throws URISyntaxException { + List result = null; + + // Get pathUri of the current running JAR + URI pathUri = Grasscutter.class.getProtectionDomain() + .getCodeSource() + .getLocation() + .toURI(); + + try { + // file walks JAR + URI uri = URI.create("jar:file:" + pathUri.getRawPath()); + try (FileSystem fs = FileSystems.newFileSystem(uri, Collections.emptyMap())) { + result = Files.walk(fs.getPath(folder)) + .filter(Files::isRegularFile) + .collect(Collectors.toList()); + } + } catch (Exception e) { + // Eclipse puts resources in its bin folder + File f = new File(System.getProperty("user.dir") + folder); + + if (!f.exists() || f.listFiles().length == 0) { + return null; + } + + result = Arrays.stream(f.listFiles()).map(File::toPath).toList(); + } + + return result; + } + + @SuppressWarnings("ResultOfMethodCallIgnored") + public static String readToString(InputStream file) throws IOException { + byte[] content = file.readAllBytes(); + + return new String(content, StandardCharsets.UTF_8); + } } diff --git a/src/main/java/emu/grasscutter/utils/Language.java b/src/main/java/emu/grasscutter/utils/Language.java index 70e32e658..c343e949e 100644 --- a/src/main/java/emu/grasscutter/utils/Language.java +++ b/src/main/java/emu/grasscutter/utils/Language.java @@ -3,15 +3,21 @@ package emu.grasscutter.utils; import com.google.gson.JsonElement; import com.google.gson.JsonObject; import emu.grasscutter.Grasscutter; +import emu.grasscutter.game.player.Player; import javax.annotation.Nullable; import java.io.InputStream; -import java.util.HashMap; +import java.util.concurrent.ConcurrentHashMap; import java.util.Map; +import static emu.grasscutter.Configuration.*; + public final class Language { + private static final Map cachedLanguages = new ConcurrentHashMap<>(); + private final JsonObject languageData; - private final Map cachedTranslations = new HashMap<>(); + private final String languageCode; + private final Map cachedTranslations = new ConcurrentHashMap<>(); /** * Creates a language instance from a code. @@ -19,7 +25,24 @@ public final class Language { * @return A language instance. */ public static Language getLanguage(String langCode) { - return new Language(langCode + ".json", Grasscutter.getConfig().DefaultLanguage.toLanguageTag() + ".json"); + if (cachedLanguages.containsKey(langCode)) { + return cachedLanguages.get(langCode); + } + + var fallbackLanguageCode = Utils.getLanguageCode(FALLBACK_LANGUAGE); + var description = getLanguageFileDescription(langCode, fallbackLanguageCode); + var actualLanguageCode = description.getLanguageCode(); + + Language languageInst; + if (description.getLanguageFile() != null) { + languageInst = new Language(description); + cachedLanguages.put(actualLanguageCode, languageInst); + } else { + languageInst = cachedLanguages.get(actualLanguageCode); + cachedLanguages.put(langCode, languageInst); + } + + return languageInst; } /** @@ -40,33 +63,89 @@ public final class Language { } /** - * Reads a file and creates a language instance. - * @param fileName The name of the language file. + * Returns the translated value from the key while substituting arguments. + * @param player Target player + * @param key The key of the translated value to return. + * @param args The arguments to substitute. + * @return A translated value with arguments substituted. */ - private Language(String fileName, String fallback) { - @Nullable JsonObject languageData = null; + public static String translate(Player player, String key, Object... args) { + if (player == null) { + return translate(key, args); + } - InputStream file = Grasscutter.class.getResourceAsStream("/languages/" + fileName); - if (file == null) { // Provided fallback language. - file = Grasscutter.class.getResourceAsStream("/languages/" + fallback); - Grasscutter.getLogger().warn("Failed to load language file: " + fileName + ", falling back to: " + fallback); - } - if(file == null) { // Fallback the fallback language. - file = Grasscutter.class.getResourceAsStream("/languages/en-US.json"); - Grasscutter.getLogger().warn("Failed to load language file: " + fallback + ", falling back to: en-US.json"); - } - if(file == null) - throw new RuntimeException("Unable to load the primary, fallback, and 'en-US' language files."); + var langCode = Utils.getLanguageCode(player.getAccount().getLocale()); + String translated = Grasscutter.getLanguage(langCode).get(key); try { - languageData = Grasscutter.getGsonFactory().fromJson(Utils.readFromInputStream(file), JsonObject.class); + return translated.formatted(args); } catch (Exception exception) { - Grasscutter.getLogger().warn("Failed to load language file: " + fileName, exception); + Grasscutter.getLogger().error("Failed to format string: " + key, exception); + return translated; + } + } + + /** + * get language code + */ + public String getLanguageCode() { + return languageCode; + } + + /** + * Reads a file and creates a language instance. + */ + private Language(LanguageStreamDescription description) { + @Nullable JsonObject languageData = null; + languageCode = description.getLanguageCode(); + + try { + languageData = Grasscutter.getGsonFactory().fromJson(Utils.readFromInputStream(description.getLanguageFile()), JsonObject.class); + } catch (Exception exception) { + Grasscutter.getLogger().warn("Failed to load language file: " + description.getLanguageCode(), exception); } this.languageData = languageData; } + /** + * create a LanguageStreamDescription + * @param languageCode The name of the language code. + * @param fallbackLanguageCode The name of the fallback language code. + */ + private static LanguageStreamDescription getLanguageFileDescription(String languageCode, String fallbackLanguageCode) { + var fileName = languageCode + ".json"; + var fallback = fallbackLanguageCode + ".json"; + + String actualLanguageCode = languageCode; + InputStream file = Grasscutter.class.getResourceAsStream("/languages/" + fileName); + + if (file == null) { // Provided fallback language. + Grasscutter.getLogger().warn("Failed to load language file: " + fileName + ", falling back to: " + fallback); + actualLanguageCode = fallbackLanguageCode; + if (cachedLanguages.containsKey(actualLanguageCode)) { + return new LanguageStreamDescription(actualLanguageCode, null); + } + + file = Grasscutter.class.getResourceAsStream("/languages/" + fallback); + } + + if(file == null) { // Fallback the fallback language. + Grasscutter.getLogger().warn("Failed to load language file: " + fallback + ", falling back to: en-US.json"); + actualLanguageCode = "en-US"; + if (cachedLanguages.containsKey(actualLanguageCode)) { + return new LanguageStreamDescription(actualLanguageCode, null); + } + + file = Grasscutter.class.getResourceAsStream("/languages/en-US.json"); + } + + if(file == null) + throw new RuntimeException("Unable to load the primary, fallback, and 'en-US' language files."); + + return new LanguageStreamDescription(actualLanguageCode, file); + } + /** * Returns the value (as a string) from a nested key. * @param key The key to look for. @@ -81,7 +160,9 @@ public final class Language { JsonObject object = this.languageData; int index = 0; - String result = "This value does not exist. Please report this to the Discord: " + key; + String valueNotFoundPattern = "This value does not exist. Please report this to the Discord: "; + String result = valueNotFoundPattern + key; + boolean isValueFound = false; while (true) { if(index == keys.length) break; @@ -92,11 +173,37 @@ public final class Language { if(element.isJsonObject()) object = element.getAsJsonObject(); else { + isValueFound = true; result = element.getAsString(); break; } } else break; } + + if (!isValueFound && !languageCode.equals("en-US")) { + var englishValue = Grasscutter.getLanguage("en-US").get(key); + if (!englishValue.contains(valueNotFoundPattern)) { + result += "\nhere is english version:\n" + englishValue; + } + } this.cachedTranslations.put(key, result); return result; } + + private static class LanguageStreamDescription { + private final String languageCode; + private final InputStream languageFile; + + public LanguageStreamDescription(String languageCode, InputStream languageFile) { + this.languageCode = languageCode; + this.languageFile = languageFile; + } + + public String getLanguageCode() { + return languageCode; + } + + public InputStream getLanguageFile() { + return languageFile; + } + } } diff --git a/src/main/java/emu/grasscutter/utils/Position.java b/src/main/java/emu/grasscutter/utils/Position.java index f2ecb6915..b9b75e25b 100644 --- a/src/main/java/emu/grasscutter/utils/Position.java +++ b/src/main/java/emu/grasscutter/utils/Position.java @@ -2,6 +2,8 @@ package emu.grasscutter.utils; import java.io.Serializable; +import com.google.gson.annotations.SerializedName; + import dev.morphia.annotations.Entity; import emu.grasscutter.net.proto.VectorOuterClass.Vector; @@ -9,8 +11,13 @@ import emu.grasscutter.net.proto.VectorOuterClass.Vector; public class Position implements Serializable { private static final long serialVersionUID = -2001232313615923575L; + @SerializedName(value="x", alternate={"_x", "X"}) private float x; + + @SerializedName(value="y", alternate={"_y", "Y"}) private float y; + + @SerializedName(value="z", alternate={"_z", "Z"}) private float z; public Position() { diff --git a/src/main/java/emu/grasscutter/utils/ServerLogEventAppender.java b/src/main/java/emu/grasscutter/utils/ServerLogEventAppender.java new file mode 100644 index 000000000..a64ecd5ad --- /dev/null +++ b/src/main/java/emu/grasscutter/utils/ServerLogEventAppender.java @@ -0,0 +1,31 @@ +package emu.grasscutter.utils; + +import ch.qos.logback.classic.spi.ILoggingEvent; +import ch.qos.logback.core.AppenderBase; +import ch.qos.logback.core.encoder.Encoder; +import ch.qos.logback.core.spi.DeferredProcessingAware; +import ch.qos.logback.core.status.ErrorStatus; +import emu.grasscutter.server.event.internal.ServerLogEvent; +import emu.grasscutter.server.event.types.ServerEvent; + +import java.io.IOException; +import java.nio.charset.StandardCharsets; + +public class ServerLogEventAppender extends AppenderBase { + protected Encoder encoder; + + @Override + protected void append(E event) { + byte[] byteArray = this.encoder.encode(event); + ServerLogEvent sle = new ServerLogEvent(ServerEvent.Type.GAME, (ILoggingEvent) event, new String(byteArray, StandardCharsets.UTF_8)); + sle.call(); + } + + public Encoder getEncoder() { + return encoder; + } + + public void setEncoder(Encoder encoder) { + this.encoder = encoder; + } +} diff --git a/src/main/java/emu/grasscutter/utils/Utils.java b/src/main/java/emu/grasscutter/utils/Utils.java index 6d11822f0..80449ea0f 100644 --- a/src/main/java/emu/grasscutter/utils/Utils.java +++ b/src/main/java/emu/grasscutter/utils/Utils.java @@ -6,18 +6,20 @@ import java.nio.file.Files; import java.nio.file.StandardCopyOption; import java.time.*; import java.time.temporal.TemporalAdjusters; -import java.util.HashMap; -import java.util.Map; -import java.util.Random; +import java.util.*; -import emu.grasscutter.Config; import emu.grasscutter.Grasscutter; +import emu.grasscutter.data.DataLoader; import io.netty.buffer.ByteBuf; import io.netty.buffer.ByteBufUtil; import io.netty.buffer.Unpooled; +import it.unimi.dsi.fastutil.ints.IntArrayList; +import it.unimi.dsi.fastutil.ints.IntList; import org.slf4j.Logger; +import javax.annotation.Nullable; + import static emu.grasscutter.utils.Language.translate; @SuppressWarnings({"UnusedReturnValue", "BooleanMethodIsAlwaysInverted"}) @@ -172,12 +174,12 @@ public final class Utils { * Checks for required files and folders before startup. */ public static void startupCheck() { - Config config = Grasscutter.getConfig(); + ConfigContainer config = Grasscutter.getConfig(); Logger logger = Grasscutter.getLogger(); boolean exit = false; - String resourcesFolder = config.RESOURCE_FOLDER; - String dataFolder = config.DATA_FOLDER; + String resourcesFolder = config.folderStructure.resources; + String dataFolder = config.folderStructure.data; // Check for resources folder. if(!fileExists(resourcesFolder)) { @@ -197,6 +199,9 @@ public final class Utils { if(!fileExists(dataFolder)) createFolder(dataFolder); + // Make sure the data folder is populated, if there are any missing files copy them from resources + DataLoader.CheckAllFiles(); + if(exit) System.exit(1); } @@ -253,7 +258,9 @@ public final class Utils { * @param stream The input stream. * @return The string. */ - public static String readFromInputStream(InputStream stream) { + public static String readFromInputStream(@Nullable InputStream stream) { + if(stream == null) return "empty"; + StringBuilder stringBuilder = new StringBuilder(); try (BufferedReader reader = new BufferedReader(new InputStreamReader(stream, StandardCharsets.UTF_8))) { String line; while ((line = reader.readLine()) != null) { @@ -261,43 +268,113 @@ public final class Utils { } stream.close(); } catch (IOException e) { Grasscutter.getLogger().warn("Failed to read from input stream."); + } catch (NullPointerException ignored) { + return "empty"; } return stringBuilder.toString(); } /** - * Switch properties from upper case to lower case? + * Performs a linear interpolation using a table of fixed points to create an effective piecewise f(x) = y function. + * @param x + * @param xyArray Array of points in [[x0,y0], ... [xN, yN]] format + * @return f(x) = y */ - public static Map switchPropertiesUpperLowerCase(Map objMap, Class cls) { - Map map = new HashMap<>(objMap.size()); - for (String key : objMap.keySet()) { - try { - char c = key.charAt(0); - if (c >= 'a' && c <= 'z') { - try { - cls.getDeclaredField(key); - map.put(key, objMap.get(key)); - } catch (NoSuchFieldException e) { - String s1 = String.valueOf(c).toUpperCase(); - String after = key.length() > 1 ? s1 + key.substring(1) : s1; - cls.getDeclaredField(after); - map.put(after, objMap.get(key)); - } - } else if (c >= 'A' && c <= 'Z') { - try { - cls.getDeclaredField(key); - map.put(key, objMap.get(key)); - } catch (NoSuchFieldException e) { - String s1 = String.valueOf(c).toLowerCase(); - String after = key.length() > 1 ? s1 + key.substring(1) : s1; - cls.getDeclaredField(after); - map.put(after, objMap.get(key)); - } + public static int lerp(int x, int[][] xyArray) { + try { + if (x <= xyArray[0][0]){ // Clamp to first point + return xyArray[0][1]; + } else if (x >= xyArray[xyArray.length-1][0]) { // Clamp to last point + return xyArray[xyArray.length-1][1]; + } + // At this point we're guaranteed to have two lerp points, and pity be somewhere between them. + for (int i=0; i < xyArray.length-1; i++) { + if (x == xyArray[i+1][0]) { + return xyArray[i+1][1]; } - } catch (NoSuchFieldException e) { - map.put(key, objMap.get(key)); + if (x < xyArray[i+1][0]) { + // We are between [i] and [i+1], interpolation time! + // Using floats would be slightly cleaner but we can just as easily use ints if we're careful with order of operations. + int position = x - xyArray[i][0]; + int fullDist = xyArray[i+1][0] - xyArray[i][0]; + int prevValue = xyArray[i][1]; + int fullDelta = xyArray[i+1][1] - prevValue; + return prevValue + ( (position * fullDelta) / fullDist ); + } + } + } catch (IndexOutOfBoundsException e) { + Grasscutter.getLogger().error("Malformed lerp point array. Must be of form [[x0, y0], ..., [xN, yN]]."); + } + return 0; + } + + /** + * Checks if an int is in an int[] + * @param key int to look for + * @param array int[] to look in + * @return key in array + */ + public static boolean intInArray(int key, int[] array) { + for (int i : array) { + if (i == key) { + return true; } } + return false; + } - return map; + /** + * Return a copy of minuend without any elements found in subtrahend. + * @param minuend The array we want elements from + * @param subtrahend The array whose elements we don't want + * @return The array with only the elements we want, in the order that minuend had them + */ + public static int[] setSubtract(int[] minuend, int[] subtrahend) { + IntList temp = new IntArrayList(); + for (int i : minuend) { + if (!intInArray(i, subtrahend)) { + temp.add(i); + } + } + return temp.toIntArray(); + } + + /** + * Gets the language code from a given locale. + * @param locale A locale. + * @return A string in the format of 'XX-XX'. + */ + public static String getLanguageCode(Locale locale) { + return String.format("%s-%s", locale.getLanguage(), locale.getCountry()); + } + + /** + * Base64 encodes a given byte array. + * @param toEncode An array of bytes. + * @return A base64 encoded string. + */ + public static String base64Encode(byte[] toEncode) { + return Base64.getEncoder().encodeToString(toEncode); + } + + /** + * Base64 decodes a given string. + * @param toDecode A base64 encoded string. + * @return An array of bytes. + */ + public static byte[] base64Decode(String toDecode) { + return Base64.getDecoder().decode(toDecode); + } + + /** + * Safely JSON decodes a given string. + * @param jsonData The JSON-encoded data. + * @return JSON decoded data, or null if an exception occurred. + */ + public static T jsonDecode(String jsonData, Class classType) { + try { + return Grasscutter.getGsonFactory().fromJson(jsonData, classType); + } catch (Exception ignored) { + return null; + } } } diff --git a/src/main/resources/defaults/data/Banners.json b/src/main/resources/defaults/data/Banners.json new file mode 100644 index 000000000..0242cae54 --- /dev/null +++ b/src/main/resources/defaults/data/Banners.json @@ -0,0 +1,71 @@ +[ + { + "gachaType": 200, + "scheduleId": 893, + "bannerType": "STANDARD", + "prefabPath": "GachaShowPanel_A022", + "previewPrefabPath": "UI_Tab_GachaShowPanel_A022", + "titlePath": "UI_GACHA_SHOW_PANEL_A022_TITLE", + "costItemId": 224, + "costItemAmount": 1, + "costItemAmount10": 10, + "beginTime": 0, + "endTime": 1924992000, + "sortId": 1000, + "fallbackItems4Pool1": [1006, 1014, 1015, 1020, 1021, 1023, 1024, 1025, 1027, 1031, 1032, 1034, 1036, 1039, 1043, 1044, 1045, 1048, 1053, 1055, 1056, 1064], + "weights4": [[1,510], [8,510], [10,10000]], + "weights5": [[1,75], [73,150], [90,10000]] + }, + { + "gachaType": 301, + "scheduleId": 903, + "bannerType": "EVENT", + "prefabPath": "GachaShowPanel_A081", + "previewPrefabPath": "UI_Tab_GachaShowPanel_A081", + "titlePath": "UI_GACHA_SHOW_PANEL_A081_TITLE", + "costItemId": 223, + "beginTime": 0, + "endTime": 1924992000, + "sortId": 9998, + "rateUpItems4": [1034, 1014, 1048], + "rateUpItems5": [1060], + "fallbackItems5Pool2": [], + "weights5": [[1,80], [73,80], [90,10000]] + }, + { + "gachaType": 400, + "scheduleId": 923, + "bannerType": "EVENT", + "prefabPath": "GachaShowPanel_A082", + "previewPrefabPath": "UI_Tab_GachaShowPanel_A082", + "titlePath": "UI_GACHA_SHOW_PANEL_A031_TITLE", + "costItemId": 223, + "beginTime": 0, + "endTime": 1924992000, + "sortId": 9998, + "rateUpItems4": [1034, 1014, 1048], + "rateUpItems5": [1026], + "fallbackItems5Pool2": [], + "weights5": [[1,80], [73,80], [90,10000]] + }, + { + "gachaType": 302, + "scheduleId": 913, + "bannerType": "WEAPON", + "prefabPath": "GachaShowPanel_A083", + "previewPrefabPath": "UI_Tab_GachaShowPanel_A083", + "titlePath": "UI_GACHA_SHOW_PANEL_A021_TITLE", + "costItemId": 223, + "beginTime": 0, + "endTime": 1924992000, + "sortId": 9997, + "eventChance": 75, + "softPity": 80, + "hardPity": 80, + "rateUpItems4": [11403, 12401, 13406, 14409, 15403], + "rateUpItems5": [15508, 13505], + "fallbackItems5Pool1": [], + "weights4": [[1,600], [7,600], [8, 6600], [10,12600]], + "weights5": [[1,100], [62,100], [73, 7800], [80,10000]] + } +] diff --git a/data/Drop.json b/src/main/resources/defaults/data/Drop.json similarity index 99% rename from data/Drop.json rename to src/main/resources/defaults/data/Drop.json index d1600aae2..9e002c95e 100644 --- a/data/Drop.json +++ b/src/main/resources/defaults/data/Drop.json @@ -1,4 +1,44 @@ [ + {"monsterId":28040101,"dropDataList":[{"itemId":100084,"minCount":1,"maxCount":1,"minWeight":0,"maxWeight":10000}]}, + {"monsterId":28040102,"dropDataList":[{"itemId":100084,"minCount":1,"maxCount":1,"minWeight":0,"maxWeight":10000}]}, + {"monsterId":28040103,"dropDataList":[{"itemId":100084,"minCount":1,"maxCount":1,"minWeight":0,"maxWeight":10000}]}, + {"monsterId":28040104,"dropDataList":[{"itemId":100084,"minCount":1,"maxCount":1,"minWeight":0,"maxWeight":10000}]}, + {"monsterId":28040105,"dropDataList":[{"itemId":100084,"minCount":1,"maxCount":1,"minWeight":0,"maxWeight":10000}]}, + {"monsterId":28040106,"dropDataList":[{"itemId":100084,"minCount":1,"maxCount":1,"minWeight":0,"maxWeight":10000}]}, + {"monsterId":28040107,"dropDataList":[{"itemId":100084,"minCount":1,"maxCount":1,"minWeight":0,"maxWeight":10000}]}, + {"monsterId":28040108,"dropDataList":[{"itemId":100084,"minCount":1,"maxCount":1,"minWeight":0,"maxWeight":10000}]}, + {"monsterId":28020301,"dropDataList":[{"itemId":100061,"minCount":2,"maxCount":2,"minWeight":0,"maxWeight":10000}]}, + {"monsterId":28020302,"dropDataList":[{"itemId":100061,"minCount":1,"maxCount":1,"minWeight":0,"maxWeight":10000}]}, + {"monsterId":28020101,"dropDataList":[{"itemId":100061,"minCount":1,"maxCount":1,"minWeight":0,"maxWeight":10000}]}, + {"monsterId":28020102,"dropDataList":[{"itemId":100061,"minCount":1,"maxCount":1,"minWeight":0,"maxWeight":10000}]}, + {"monsterId":28020103,"dropDataList":[{"itemId":100061,"minCount":1,"maxCount":1,"minWeight":0,"maxWeight":10000}]}, + {"monsterId":28020104,"dropDataList":[{"itemId":100061,"minCount":1,"maxCount":1,"minWeight":0,"maxWeight":10000}]}, + {"monsterId":28020105,"dropDataList":[{"itemId":100061,"minCount":1,"maxCount":1,"minWeight":0,"maxWeight":10000}]}, + {"monsterId":28020106,"dropDataList":[{"itemId":100061,"minCount":1,"maxCount":1,"minWeight":0,"maxWeight":10000}]}, + {"monsterId":28020701,"dropDataList":[{"itemId":100061,"minCount":1,"maxCount":1,"minWeight":0,"maxWeight":10000}]}, + {"monsterId":28020702,"dropDataList":[{"itemId":100061,"minCount":1,"maxCount":1,"minWeight":0,"maxWeight":10000}]}, + {"monsterId":28020303,"dropDataList":[{"itemId":100094,"minCount":1,"maxCount":1,"minWeight":0,"maxWeight":10000}]}, + {"monsterId":28020304,"dropDataList":[{"itemId":100094,"minCount":2,"maxCount":3,"minWeight":0,"maxWeight":10000}]}, + {"monsterId":28030401,"dropDataList":[{"itemId":100064,"minCount":1,"maxCount":1,"minWeight":0,"maxWeight":10000}]}, + {"monsterId":28030402,"dropDataList":[{"itemId":100064,"minCount":1,"maxCount":1,"minWeight":0,"maxWeight":10000}]}, + {"monsterId":28030403,"dropDataList":[{"itemId":100064,"minCount":1,"maxCount":1,"minWeight":0,"maxWeight":10000}]}, + {"monsterId":28030404,"dropDataList":[{"itemId":100064,"minCount":1,"maxCount":1,"minWeight":0,"maxWeight":10000}]}, + {"monsterId":28030405,"dropDataList":[{"itemId":100064,"minCount":1,"maxCount":1,"minWeight":0,"maxWeight":10000}]}, + {"monsterId":28030406,"dropDataList":[{"itemId":100064,"minCount":1,"maxCount":1,"minWeight":0,"maxWeight":10000}]}, + {"monsterId":28030407,"dropDataList":[{"itemId":100064,"minCount":1,"maxCount":1,"minWeight":0,"maxWeight":10000}]}, + {"monsterId":28030408,"dropDataList":[{"itemId":100064,"minCount":1,"maxCount":1,"minWeight":0,"maxWeight":10000}]}, + {"monsterId":28030409,"dropDataList":[{"itemId":100064,"minCount":1,"maxCount":1,"minWeight":0,"maxWeight":10000}]}, + {"monsterId":28030301,"dropDataList":[{"itemId":100064,"minCount":1,"maxCount":1,"minWeight":0,"maxWeight":10000}]}, + {"monsterId":28030302,"dropDataList":[{"itemId":100064,"minCount":1,"maxCount":1,"minWeight":0,"maxWeight":10000}]}, + {"monsterId":28030303,"dropDataList":[{"itemId":100064,"minCount":1,"maxCount":1,"minWeight":0,"maxWeight":10000}]}, + {"monsterId":28030304,"dropDataList":[{"itemId":100064,"minCount":1,"maxCount":1,"minWeight":0,"maxWeight":10000}]}, + {"monsterId":28030305,"dropDataList":[{"itemId":100064,"minCount":1,"maxCount":1,"minWeight":0,"maxWeight":10000}]}, + {"monsterId":28030306,"dropDataList":[{"itemId":100064,"minCount":1,"maxCount":1,"minWeight":0,"maxWeight":10000}]}, + {"monsterId":28030307,"dropDataList":[{"itemId":100064,"minCount":1,"maxCount":1,"minWeight":0,"maxWeight":10000}]}, + {"monsterId":28030308,"dropDataList":[{"itemId":100064,"minCount":1,"maxCount":1,"minWeight":0,"maxWeight":10000}]}, + {"monsterId":28030309,"dropDataList":[{"itemId":100064,"minCount":1,"maxCount":1,"minWeight":0,"maxWeight":10000}]}, + {"monsterId":28030310,"dropDataList":[{"itemId":100064,"minCount":1,"maxCount":1,"minWeight":0,"maxWeight":10000}]}, + {"monsterId":28030311,"dropDataList":[{"itemId":100064,"minCount":1,"maxCount":1,"minWeight":0,"maxWeight":10000}]}, { "monsterId": 21010101, "dropDataList": [ @@ -132754,21 +132794,21 @@ "maxWeight": 1 }, { - "itemId": 112047, + "itemId": 112023, "minCount": 1, "maxCount": 3, "minWeight": 2000, "maxWeight": 6000 }, { - "itemId": 112048, + "itemId": 112024, "minCount": 1, "maxCount": 1, "minWeight": 8001, "maxWeight": 9500 }, { - "itemId": 112049, + "itemId": 112025, "minCount": 1, "maxCount": 1, "minWeight": 9501, @@ -132815,21 +132855,21 @@ "maxWeight": 1 }, { - "itemId": 112047, + "itemId": 112023, "minCount": 1, "maxCount": 3, "minWeight": 2000, "maxWeight": 6000 }, { - "itemId": 112048, + "itemId": 112024, "minCount": 1, "maxCount": 1, "minWeight": 8001, "maxWeight": 9500 }, { - "itemId": 112049, + "itemId": 112025, "minCount": 1, "maxCount": 1, "minWeight": 9501, @@ -132876,21 +132916,21 @@ "maxWeight": 1 }, { - "itemId": 112047, + "itemId": 112023, "minCount": 1, "maxCount": 3, "minWeight": 2000, "maxWeight": 6000 }, { - "itemId": 112048, + "itemId": 112024, "minCount": 1, "maxCount": 1, "minWeight": 8001, "maxWeight": 9500 }, { - "itemId": 112049, + "itemId": 112025, "minCount": 1, "maxCount": 1, "minWeight": 9501, @@ -132937,21 +132977,21 @@ "maxWeight": 1 }, { - "itemId": 112047, + "itemId": 112023, "minCount": 1, "maxCount": 3, "minWeight": 2000, "maxWeight": 6000 }, { - "itemId": 112048, + "itemId": 112024, "minCount": 1, "maxCount": 1, "minWeight": 8001, "maxWeight": 9500 }, { - "itemId": 112049, + "itemId": 112025, "minCount": 1, "maxCount": 1, "minWeight": 9501, @@ -132998,21 +133038,21 @@ "maxWeight": 1 }, { - "itemId": 112047, + "itemId": 112023, "minCount": 1, "maxCount": 3, "minWeight": 2000, "maxWeight": 6000 }, { - "itemId": 112048, + "itemId": 112024, "minCount": 1, "maxCount": 1, "minWeight": 8001, "maxWeight": 9500 }, { - "itemId": 112049, + "itemId": 112025, "minCount": 1, "maxCount": 1, "minWeight": 9501, @@ -133059,21 +133099,21 @@ "maxWeight": 1 }, { - "itemId": 112047, + "itemId": 112023, "minCount": 1, "maxCount": 3, "minWeight": 2000, "maxWeight": 6000 }, { - "itemId": 112048, + "itemId": 112024, "minCount": 1, "maxCount": 1, "minWeight": 8001, "maxWeight": 9500 }, { - "itemId": 112049, + "itemId": 112025, "minCount": 1, "maxCount": 1, "minWeight": 9501, diff --git a/src/main/resources/defaults/data/EnergyDrop.json b/src/main/resources/defaults/data/EnergyDrop.json new file mode 100644 index 000000000..6cd7e8d85 --- /dev/null +++ b/src/main/resources/defaults/data/EnergyDrop.json @@ -0,0 +1,185 @@ +[ + { + "dropId": 22010010, + "dropList": [ + { "ballId": 2024, "count": 1 } + ] + }, + { + "dropId": 22010030, + "dropList": [ + { "ballId": 2008, "count": 1 } + ] + }, + { + "dropId": 22010050, + "dropList": [ + { "ballId": 2024, "count": 3 }, + { "ballId": 2008, "count": 1 } + ] + }, + { + "dropId": 22010013, + "dropList": [ + { "ballId": 2019, "count": 1 } + ] + }, + { + "dropId": 22010033, + "dropList": [ + { "ballId": 2003, "count": 1 } + ] + }, + { + "dropId": 22010015, + "dropList": [ + { "ballId": 2021, "count": 1 } + ] + }, + { + "dropId": 22010035, + "dropList": [ + { "ballId": 2005, "count": 1 } + ] + }, + { + "dropId": 22010034, + "dropList": [ + { "ballId": 2004, "count": 1 } + ] + }, + { + "dropId": 22010037, + "dropList": [ + { "ballId": 2007, "count": 1 } + ] + }, + { + "dropId": 22010032, + "dropList": [ + { "ballId": 2002, "count": 1 } + ] + }, + { + "dropId": 22010022, + "dropList": [ + { "ballId": 2018, "count": 1 } + ] + }, + { + "dropId": 22010036, + "dropList": [ + { "ballId": 2006, "count": 1 } + ] + }, + { + "dropId": 22010026, + "dropList": [ + { "ballId": 2022, "count": 1 } + ] + }, + { + "dropId": 22010031, + "dropList": [ + { "ballId": 2001, "count": 1 } + ] + }, + + { + "dropId": 22010014, + "dropList": [ + { "ballId": 2020, "count": 1 } + ] + }, + { + "dropId": 22010016, + "dropList": [ + { "ballId": 2022, "count": 1 } + ] + }, + { + "dropId": 22010012, + "dropList": [ + { "ballId": 2018, "count": 1 } + ] + }, + { + "dropId": 22010024, + "dropList": [ + { "ballId": 2004, "count": 1 } + ] + }, + { + "dropId": 22010011, + "dropList": [ + { "ballId": 2017, "count": 1 } + ] + }, + { + "dropId": 22010017, + "dropList": [ + { "ballId": 2023, "count": 1 } + ] + }, + { + "dropId": 22010021, + "dropList": [ + { "ballId": 2017, "count": 1 } + ] + }, + { + "dropId": 22010027, + "dropList": [ + { "ballId": 2007, "count": 1 } + ] + }, + { + "dropId": 22010040, + "dropList": [ + { "ballId": 2024, "count": 1 }, + { "ballId": 2008, "count": 1 } + ] + }, + { + "dropId": 22010025, + "dropList": [ + { "ballId": 2021, "count": 2 } + ] + }, + { + "dropId": 22010020, + "dropList": [ + { "ballId": 2024, "count": 1 } + ] + }, + { + "dropId": 22003100, + "dropList": [ + ] + }, + { + "dropId": 22001000, + "dropList": [ + ] + }, + { + "dropId": 22000100, + "dropList": [ + ] + }, + { + "dropId": 22003000, + "dropList": [ + ] + }, + { + "dropId": 22001100, + "dropList": [ + ] + }, + { + "dropId": 22000000, + "dropList": [ + ] + } +] \ No newline at end of file diff --git a/data/ExpeditionReward.json b/src/main/resources/defaults/data/ExpeditionReward.json similarity index 100% rename from data/ExpeditionReward.json rename to src/main/resources/defaults/data/ExpeditionReward.json diff --git a/src/main/resources/defaults/data/GameAnnouncement.json b/src/main/resources/defaults/data/GameAnnouncement.json new file mode 100644 index 000000000..57cd72c8e --- /dev/null +++ b/src/main/resources/defaults/data/GameAnnouncement.json @@ -0,0 +1,22 @@ +{ + "t": "{{SYSTEM_TIME}}", + "list": [ + { + "ann_id": 1, + "title": "Welcome to Grasscutter!", + "subtitle": "Welcome!", + "banner": "{{DISPATCH_PUBLIC}}/hk4e/announcement/assets/banner/1.jpg", + "content": "

Hi there!

First of all, welcome to Grasscutter. If you have any issues, please let us know so that Lawnmower can help you!


〓Discord〓

https://discord.gg/T5vZU6UyeG

〓GitHub〓https://github.com/Grasscutters/Grasscutter", + "lang": "en-US" + }, + { + "ann_id": 2, + "title": "How to use announcements", + "subtitle": "How to use announcements", + "banner": "{{DISPATCH_PUBLIC}}/hk4e/announcement/assets/banner/2.jpg", + "content": "

Announcement content uses HTML. The specific content of the announcement is stored in the program directory GameAnnouncement.json, while GameAnnouncementList.json stores the announcement list data.

GameAnnouncement

ParameterDescription
ann_idUnique ID
titleTitle shown at the top of the content
subtitleShort title shown on the left
bannerImage to display between content and title
contentContent body in HTML
langLanguage code for this entry

GameAnnouncementList

If you want to add an announcement, please add the list data in the announcement type corresponding to GameAnnouncementList, and finally add the announcement content in GameAnnouncement.

", + "lang": "en-US" + } + ], + "total": 2 +} \ No newline at end of file diff --git a/src/main/resources/defaults/data/GameAnnouncementList.json b/src/main/resources/defaults/data/GameAnnouncementList.json new file mode 100644 index 000000000..3697703a3 --- /dev/null +++ b/src/main/resources/defaults/data/GameAnnouncementList.json @@ -0,0 +1,62 @@ +{ + "t": "{{SYSTEM_TIME}}", + "list": [ + { + "list": [ + { + "ann_id": 1, + "title": "Welcome to Grasscutter!", + "subtitle": "Welcome!", + "banner": "{{DISPATCH_PUBLIC}}/hk4e/announcement/assets/banner/1.jpg", + "tag_icon": "{{DISPATCH_PUBLIC}}/hk4e/announcement/assets/tag_icon.png", + "type": 2, + "type_label": "System", + "lang": "en-US", + "start_time": "2020-09-25 04:05:30", + "end_time": "2030-10-30 11:00:00", + "content": "", + "has_content": true + }, + { + "ann_id": 2, + "title": "How to use announcements", + "subtitle": "How to use announcements", + "banner": "{{DISPATCH_PUBLIC}}/hk4e/announcement/assets/banner/2.jpg", + "tag_icon": "{{DISPATCH_PUBLIC}}/hk4e/announcement/assets/tag_icon.png", + "type": 2, + "type_label": "System", + "lang": "en-US", + "start_time": "2020-09-25 04:05:30", + "end_time": "2030-10-30 11:00:00", + "content": "", + "has_content": true + } + ], + "type_id": 2, + "type_label": "System" + }, + { + "list": [ + {} + ], + "type_id": 3, + "type_label": "Events" + } + ], + "total": 2, + "type_list": [ + { + "id": 2, + "name": "游戏系统公告", + "mi18n_name": "System" + }, + { + "id": 1, + "name": "活动公告", + "mi18n_name": "Activity" + } + ], + "timezone": -5, + "alert": false, + "alert_id": 0 +} \ No newline at end of file diff --git a/data/Shop.json b/src/main/resources/defaults/data/Shop.json similarity index 78% rename from data/Shop.json rename to src/main/resources/defaults/data/Shop.json index 9670b0bfc..0d7c37fc1 100644 --- a/data/Shop.json +++ b/src/main/resources/defaults/data/Shop.json @@ -5,8 +5,8 @@ { "goodsId": 1004202, "goodsItem": { - "Id": 202, - "Count": 1000000 + "id": 202, + "count": 1000000 }, "scoin": 1, "buyLimit": 500, @@ -16,17 +16,18 @@ "maxLevel": 99, "costItemList": [ { - "Id": 223, - "Count": 100 + "id": 223, + "count": 100 } ] }, { "goodsId": 10048006, "goodsItem": { - "Id": 108006, - "Count": 20 + "id": 108006, + "count": 20 }, + "costItemList": [], "scoin": 100, "hcoin": 100, "mcoin": 100, @@ -39,9 +40,10 @@ { "goodsId": 10048033, "goodsItem": { - "Id": 108033, - "Count": 20 + "id": 108033, + "count": 20 }, + "costItemList": [], "scoin": 1, "buyLimit": 50000, "beginTime": 1575129600, diff --git a/data/ShopChest.json b/src/main/resources/defaults/data/ShopChest.json similarity index 100% rename from data/ShopChest.json rename to src/main/resources/defaults/data/ShopChest.json diff --git a/data/ShopChestBatchUse.json b/src/main/resources/defaults/data/ShopChestBatchUse.json similarity index 100% rename from data/ShopChestBatchUse.json rename to src/main/resources/defaults/data/ShopChestBatchUse.json diff --git a/src/main/resources/defaults/data/SkillParticleGeneration.json b/src/main/resources/defaults/data/SkillParticleGeneration.json new file mode 100644 index 000000000..f6610ac88 --- /dev/null +++ b/src/main/resources/defaults/data/SkillParticleGeneration.json @@ -0,0 +1,576 @@ +[ + { + "avatarId": 10000002, + "name": "Kamisato Ayaka", + "amountList": [ + { + "value": 4, + "chance": 50 + }, + { + "value": 5, + "chance": 50 + } + ] + }, + { + "avatarId": 10000003, + "name": "Jean", + "amountList": [ + { + "value": 2, + "chance": 33 + }, + { + "value": 3, + "chance": 67 + } + ] + }, + { + "avatarId": 10000005, + "name": "Traveler", + "amountList": [ + { + "value": 3, + "chance": 67 + }, + { + "value": 4, + "chance": 33 + } + ] + }, + { + "avatarId": 10000006, + "name": "Lisa", + "amountList": [ + { + "value": 5, + "chance": 100 + } + ] + }, + { + "avatarId": 10000007, + "name": "Traveler", + "amountList": [ + { + "value": 3, + "chance": 67 + }, + { + "value": 4, + "chance": 33 + } + ] + }, + { + "avatarId": 10000014, + "name": "Barbara", + "amountList": [ + { + "value": 0, + "chance": 100 + } + ] + }, + { + "avatarId": 10000015, + "name": "Kaeya", + "amountList": [ + { + "value": 2, + "chance": 33 + }, + { + "value": 3, + "chance": 67 + } + ] + }, + { + "avatarId": 10000016, + "name": "Diluc", + "amountList": [ + { + "value": 1, + "chance": 33 + }, + { + "value": 2, + "chance": 67 + } + ] + }, + { + "avatarId": 10000020, + "name": "Razor", + "amountList": [ + { + "value": 3, + "chance": 100 + } + ] + }, + { + "avatarId": 10000021, + "name": "Amber", + "amountList": [ + { + "value": 4, + "chance": 100 + } + ] + }, + { + "avatarId": 10000022, + "name": "Venti", + "amountList": [ + { + "value": 3, + "chance": 100 + } + ] + }, + { + "avatarId": 10000023, + "name": "Xiangling", + "amountList": [ + { + "value": 1, + "chance": 100 + } + ] + }, + { + "avatarId": 10000024, + "name": "Beidou", + "amountList": [ + { + "value": 2, + "chance": 100 + } + ] + }, + { + "avatarId": 10000025, + "name": "Xingqiu", + "amountList": [ + { + "value": 5, + "chance": 100 + } + ] + }, + { + "avatarId": 10000026, + "name": "Xiao", + "amountList": [ + { + "value": 3, + "chance": 100 + } + ] + }, + { + "avatarId": 10000027, + "name": "Ningguang", + "amountList": [ + { + "value": 3, + "chance": 33 + }, + { + "value": 4, + "chance": 67 + } + ] + }, + { + "avatarId": 10000029, + "name": "Klee", + "amountList": [ + { + "value": 4, + "chance": 100 + } + ] + }, + { + "avatarId": 10000030, + "name": "Zhongli", + "amountList": [ + { + "value": 1, + "chance": 100 + } + ] + }, + { + "avatarId": 10000031, + "name": "Fischl", + "amountList": [ + { + "value": 1, + "chance": 100 + } + ] + }, + { + "avatarId": 10000032, + "name": "Bennett", + "amountList": [ + { + "value": 2, + "chance": 75 + }, + { + "value": 3, + "chance": 25 + } + ] + }, + { + "avatarId": 10000033, + "name": "Tartaglia", + "amountList": [ + { + "value": 1, + "chance": 100 + } + ] + }, + { + "avatarId": 10000034, + "name": "Noelle", + "amountList": [ + { + "value": 0, + "chance": 100 + } + ] + }, + { + "avatarId": 10000035, + "name": "Qiqi", + "amountList": [ + { + "value": 0, + "chance": 100 + } + ] + }, + { + "avatarId": 10000036, + "name": "Chongyun", + "amountList": [ + { + "value": 4, + "chance": 100 + } + ] + }, + { + "avatarId": 10000037, + "name": "Ganyu", + "amountList": [ + { + "value": 2, + "chance": 100 + } + ] + }, + { + "avatarId": 10000038, + "name": "Albedo", + "amountList": [ + { + "value": 1, + "chance": 100 + } + ] + }, + { + "avatarId": 10000039, + "name": "Diona", + "amountList": [ + { + "value": 1, + "chance": 100 + } + ] + }, + { + "avatarId": 10000041, + "name": "Mona", + "amountList": [ + { + "value": 3, + "chance": 67 + }, + { + "value": 4, + "chance": 33 + } + ] + }, + { + "avatarId": 10000042, + "name": "Keqing", + "amountList": [ + { + "value": 2, + "chance": 50 + }, + { + "value": 3, + "chance": 50 + } + ] + }, + { + "avatarId": 10000043, + "name": "Sucrose", + "amountList": [ + { + "value": 4, + "chance": 100 + } + ] + }, + { + "avatarId": 10000044, + "name": "Xinyan", + "amountList": [ + { + "value": 4, + "chance": 100 + } + ] + }, + { + "avatarId": 10000045, + "name": "Rosaria", + "amountList": [ + { + "value": 3, + "chance": 100 + } + ] + }, + { + "avatarId": 10000046, + "name": "Hu Tao", + "amountList": [ + { + "value": 2, + "chance": 50 + }, + { + "value": 3, + "chance": 50 + } + ] + }, + { + "avatarId": 10000047, + "name": "Kaedehara Kazuha", + "amountList": [ + { + "value": 3, + "chance": 50 + }, + { + "value": 4, + "chance": 50 + } + ] + }, + { + "avatarId": 10000048, + "name": "Yanfei", + "amountList": [ + { + "value": 3, + "chance": 100 + } + ] + }, + { + "avatarId": 10000049, + "name": "Yoimiya", + "amountList": [ + { + "value": 1, + "chance": 100 + } + ] + }, + { + "avatarId": 10000050, + "name": "Thoma", + "amountList": [ + { + "value": 3, + "chance": 50 + }, + { + "value": 4, + "chance": 50 + } + ] + }, + { + "avatarId": 10000051, + "name": "Eula", + "amountList": [ + { + "value": 1, + "chance": 50 + }, + { + "value": 2, + "chance": 50 + } + ] + }, + { + "avatarId": 10000052, + "name": "Raiden Shogun", + "amountList": [ + { + "value": 1, + "chance": 100 + } + ] + }, + { + "avatarId": 10000053, + "name": "Sayu", + "amountList": [ + { + "value": 2, + "chance": 100 + } + ] + }, + { + "avatarId": 10000054, + "name": "Sangonomiya Kokomi", + "amountList": [ + { + "value": 1, + "chance": 100 + } + ] + }, + { + "avatarId": 10000055, + "name": "Gorou", + "amountList": [ + { + "value": 2, + "chance": 100 + } + ] + }, + { + "avatarId": 10000056, + "name": "Kujou Sara", + "amountList": [ + { + "value": 3, + "chance": 100 + } + ] + }, + { + "avatarId": 10000057, + "name": "Arataki Itto", + "amountList": [ + { + "value": 3, + "chance": 50 + }, + { + "value": 4, + "chance": 50 + } + ] + }, + { + "avatarId": 10000058, + "name": "Yae Miko", + "amountList": [ + { + "value": 1, + "chance": 100 + } + ] + }, + { + "avatarId": 10000060, + "name": "Yelan", + "amountList": [ + { + "value": 4, + "chance": 100 + } + ] + }, + { + "avatarId": 10000062, + "name": "Aloy", + "amountList": [ + { + "value": 5, + "chance": 100 + } + ] + }, + { + "avatarId": 10000063, + "name": "Shenhe", + "amountList": [ + { + "value": 3, + "chance": 100 + } + ] + }, + { + "avatarId": 10000064, + "name": "Yun Jin", + "amountList": [ + { + "value": 2, + "chance": 100 + } + ] + }, + { + "avatarId": 10000065, + "name": "Kuki Shinobu", + "amountList": [ + { + "value": 1, + "chance": 100 + } + ] + }, + { + "avatarId": 10000066, + "name": "Kamisato Ayato", + "amountList": [ + { + "value": 1, + "chance": 50 + }, + { + "value": 2, + "chance": 50 + } + ] + } +] diff --git a/data/Spawns.json b/src/main/resources/defaults/data/Spawns.json similarity index 100% rename from data/Spawns.json rename to src/main/resources/defaults/data/Spawns.json diff --git a/src/main/resources/defaults/data/TowerSchedule.json b/src/main/resources/defaults/data/TowerSchedule.json new file mode 100644 index 000000000..e10416a20 --- /dev/null +++ b/src/main/resources/defaults/data/TowerSchedule.json @@ -0,0 +1,5 @@ +{ + "scheduleId" : 45, + "scheduleStartTime" : "2022-05-01T00:00:00+08:00", + "nextScheduleChangeTime" : "2022-05-30T00:00:00+08:00" +} \ No newline at end of file diff --git a/src/main/resources/defaults/data/documentation/handbook.html b/src/main/resources/defaults/data/documentation/handbook.html new file mode 100644 index 000000000..6a77d3806 --- /dev/null +++ b/src/main/resources/defaults/data/documentation/handbook.html @@ -0,0 +1,162 @@ + + + + + + + + + GM Handbook + + +
+
+

{{TITLE}}

+ +

{{TITLE_COMMANDS}}

+
+ + + + + + + + {{COMMANDS_TABLE}} +
{{HEADER_COMMAND}}{{HEADER_DESCRIPTION}}
+ +

{{TITLE_AVATARS}}

+
+ + + + + + + + {{AVATARS_TABLE}} +
{{HEADER_ID}}{{HEADER_AVATAR}}
+ +

{{TITLE_ITEMS}}

+
+ + + + + + + + {{ITEMS_TABLE}} +
{{HEADER_ID}}{{HEADER_ITEM}}
+ + +

{{TITLE_SCENES}}

+
+ + + + + + + + {{SCENES_TABLE}} +
{{HEADER_ID}}{{HEADER_SCENE}}
+ +

{{TITLE_MONSTERS}}

+
+ + + + + + + + {{MONSTERS_TABLE}} +
{{HEADER_ID}}{{HEADER_MONSTER}}
+
+
+
+ +
+ + diff --git a/src/main/resources/defaults/data/documentation/index.html b/src/main/resources/defaults/data/documentation/index.html new file mode 100644 index 000000000..f89dbe258 --- /dev/null +++ b/src/main/resources/defaults/data/documentation/index.html @@ -0,0 +1,106 @@ + + + + + + + + + Documentation + + +
+
+

{{TITLE}}

+ + +
+
+
+ +
+ + diff --git a/src/main/resources/defaults/data/gacha/details.html b/src/main/resources/defaults/data/gacha/details.html new file mode 100644 index 000000000..85443d532 --- /dev/null +++ b/src/main/resources/defaults/data/gacha/details.html @@ -0,0 +1,121 @@ + + + + + + + + + Banner Details + + + +
+
+

{{TITLE}}

+ +

{{AVAILABLE_FIVE_STARS}}

+
+
    +
+ +

{{AVAILABLE_FOUR_STARS}}

+
+
    +
+ +

{{AVAILABLE_THREE_STARS}}

+
+
    +
+
+
+
+ +
+ + + + diff --git a/data/gacha_records.html b/src/main/resources/defaults/data/gacha/records.html similarity index 67% rename from data/gacha_records.html rename to src/main/resources/defaults/data/gacha/records.html index 5ce8e660f..cad1c89d5 100644 --- a/data/gacha_records.html +++ b/src/main/resources/defaults/data/gacha/records.html @@ -53,47 +53,14 @@ } Gacha Records - @@ -144,7 +111,8 @@