Compare commits
68 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| c7c37c5a95 | |||
| 4dea005c0d | |||
| 074c5c9553 | |||
| f79cc86332 | |||
| e880fdecfa | |||
| dda45b510a | |||
| 6fec3a73ac | |||
| d22d5fdf5a | |||
| fcb90d468e | |||
| 1094716d9d | |||
| 3d57a67913 | |||
| ecc514b340 | |||
| 75baf3dfaf | |||
| 4ed8600511 | |||
| 0160aac4c6 | |||
| cbed24020a | |||
| 682029a451 | |||
| f3334ae6ec | |||
| df0e0eb82b | |||
| d09469e84b | |||
| a5e56ff2ca | |||
| e38108c99b | |||
| ab31ebc674 | |||
| 87ba8b9746 | |||
| e71b36e331 | |||
| cfb1ad88f4 | |||
| b40276f037 | |||
| e1b3a14ece | |||
| c257ebd19c | |||
| 52b6803d68 | |||
| 71f83be588 | |||
| 5733cc4e35 | |||
| a8147bb171 | |||
| 17f0269ee6 | |||
| f574f277eb | |||
| 94e7c4e822 | |||
| 01582648ee | |||
| db9bd6e469 | |||
| 0eebea7a21 | |||
| 1707673c8d | |||
| 7179463c36 | |||
| 509e782d1a | |||
| bfb5f74600 | |||
| a697b179e7 | |||
| 904abaa564 | |||
| d1aeaa83f4 | |||
| 145eae3c41 | |||
| 6e61e12285 | |||
| 56e870d999 | |||
| 7f8135b193 | |||
| 31096e1a48 | |||
| f261b9fe58 | |||
| 92412e0a72 | |||
| 23f4d70fca | |||
| c613aaa191 | |||
| 692c38f7ac | |||
| 96683209f5 | |||
| 6d9458fa01 | |||
| 333a7fc17c | |||
| 914d8cc1dd | |||
| 6f9e4abcbf | |||
| 986cce555c | |||
| 34cc8765c6 | |||
| a5393833e8 | |||
| 878ee153ef | |||
| 71455cca64 | |||
| 16f2258eed | |||
| 24d4ebdc7c |
@@ -9,6 +9,8 @@ body:
|
||||
- type: markdown
|
||||
attributes:
|
||||
value: |
|
||||
在提问之前,请您先阅读 [提问的智慧](https://lug.ustc.edu.cn/wiki/doc/smart-questions/) ,我们不会为任何无意义的问题作回复。
|
||||
Please read [smart questions](http://www.catb.org/~esr/faqs/smart-questions.html) before asking questions, we won't respond to any meaningless questions.
|
||||
请详细描述您所遇到的Bug。确保您**真的**遇到了**服务端**Bug,而非原版特性或客户端Bug。
|
||||
Please describe the bug you encountered in detail. Make sure you're **actually** hitting a **server-side** bug, not a vanilla feature or client-side bug.<br>
|
||||
如果您确定的话,就按照下面的模板填充信息,并提交反馈。
|
||||
|
||||
@@ -11,6 +11,8 @@ body:
|
||||
value: |
|
||||
感谢您为 Lophine 提交新的需求!请尽可能详细地描述,以便我们可以更轻松地考虑和审查请求。
|
||||
Thank you for filling out a feature request for Lophine! Please be as detailed as possible so that we may consider and review the request easier.<br>
|
||||
在提问之前,请您先阅读 [提问的智慧](https://lug.ustc.edu.cn/wiki/doc/smart-questions/) ,我们不会为任何无意义的问题作回复。
|
||||
Please read [smart questions](http://www.catb.org/~esr/faqs/smart-questions.html) before asking questions, we won't respond to any meaningless questions.
|
||||
请搜索所有问题以避免重复的功能请求。如果您期望的功能已存在,请回复如果您有什么要补充的。
|
||||
We ask that you search all the issues to avoid a duplicate feature request. If one exists, please reply if you have anything to add.<br>
|
||||
在请求新功能之前,请确保您使用的是最新版本并且您请求的功能不在 Lophine 中。
|
||||
|
||||
@@ -1,10 +1,12 @@
|
||||
name: Lophine CI - ver/1.21.5
|
||||
name: Lophine CI
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [ "ver/1.21.5" ]
|
||||
branches:
|
||||
- "**"
|
||||
pull_request:
|
||||
branches: [ "ver/1.21.5" ]
|
||||
branches:
|
||||
- "**"
|
||||
|
||||
permissions: write-all
|
||||
|
||||
@@ -14,13 +16,13 @@ jobs:
|
||||
environment: default
|
||||
steps:
|
||||
- name: Checkout Git Repository
|
||||
uses: actions/checkout@v4
|
||||
uses: actions/checkout@v5
|
||||
|
||||
- name: Set up JDK
|
||||
uses: actions/setup-java@v4
|
||||
uses: actions/setup-java@v5
|
||||
with:
|
||||
distribution: zulu
|
||||
java-version: 22
|
||||
java-version: 21
|
||||
|
||||
- name: Setup Gradle
|
||||
uses: gradle/actions/setup-gradle@v4
|
||||
@@ -32,18 +34,10 @@ jobs:
|
||||
run: git config --global user.email "ci@luminolmc.com" && git config --global user.name "LuminolMC CI"
|
||||
|
||||
- name: Apply All Patches
|
||||
run: ./gradlew applyAllPatches
|
||||
run: ./gradlew --refresh-dependencies applyAllPatches
|
||||
|
||||
- name: CreateMojmapPaperclipJar
|
||||
run: ./gradlew createMojmapPaperclipJar
|
||||
|
||||
- name: Publish to repo
|
||||
if: github.event_name != 'pull_request' && false # do not push to any repo
|
||||
continue-on-error: true
|
||||
run: ./gradlew generateDevelopmentBundle publish -PpublishDevBundle=true
|
||||
env:
|
||||
PRIVATE_MAVEN_REPO_PASSWORD: ${{ secrets.PRIVATE_MAVEN_REPO_PASSWORD }}
|
||||
PRIVATE_MAVEN_REPO_USERNAME: ${{ secrets.PRIVATE_MAVEN_REPO_USERNAME }}
|
||||
run: ./gradlew --refresh-dependencies createMojmapPaperclipJar
|
||||
|
||||
- name: Upload Artifact
|
||||
uses: actions/upload-artifact@v4
|
||||
@@ -55,8 +49,16 @@ jobs:
|
||||
if: github.event_name != 'pull_request'
|
||||
run: sh scripts/SetENV.sh
|
||||
|
||||
- name: Publish to repo
|
||||
if: github.event_name != 'pull_request' && env.flag_release == 'true' && false # do not push to any repo
|
||||
continue-on-error: true
|
||||
run: ./gradlew --refresh-dependencies generateDevelopmentBundle publish -PpublishDevBundle=true
|
||||
env:
|
||||
PRIVATE_MAVEN_REPO_PASSWORD: ${{ secrets.PRIVATE_MAVEN_REPO_PASSWORD }}
|
||||
PRIVATE_MAVEN_REPO_USERNAME: ${{ secrets.PRIVATE_MAVEN_REPO_USERNAME }}
|
||||
|
||||
- name: Create Release
|
||||
if: github.event_name != 'pull_request'
|
||||
if: github.event_name != 'pull_request' && env.flag_release == 'true'
|
||||
uses: ncipollo/release-action@v1
|
||||
with:
|
||||
tag: ${{ env.tag }}
|
||||
+1
-1
@@ -2,7 +2,7 @@ Lophine inherits its licensing from the included upstream projects.
|
||||
|
||||
Lophine从上游继承了许可证。
|
||||
|
||||
As such, Lophine is licensed under the
|
||||
As such, Lophine is licensed under the
|
||||
[GNU General Public License version 3](licenses/GPL.md); as it inherits it from Luminol,
|
||||
who in turn inherits it from the original Folia, Paper, Spigot, Bukkit and CraftBukkit projects.
|
||||
|
||||
|
||||
@@ -2,8 +2,9 @@
|
||||
|
||||
# Lophine
|
||||
|
||||
<h4>Lophine 是一个基于Luminol的分支,具有许多有用的优化和可配置的原版特性,目标是在Folia上实现更多生电的实现</h4>
|
||||
<h4>Lophine 是一个基于Luminol的分支,具有许多有用的优化和可配置的原版特性,目标是在Folia上实现更多生电的内容(请注意,完整生电请使用Fabric)</h4>
|
||||
|
||||

|
||||
[](LICENSE.md)
|
||||
[](https://github.com/LuminolMC/Lophine/issues)
|
||||

|
||||
@@ -13,27 +14,35 @@
|
||||
[English](./README_EN.md) | **中文**
|
||||
|
||||
## 特性
|
||||
- 支持部分Bukkit插件
|
||||
- 可配置的原版特性
|
||||
- Tpsbar 支持
|
||||
- 对单线程区域性能的优化
|
||||
- 更多有用的功能
|
||||
|
||||
- 支持部分Bukkit插件
|
||||
- 可配置的原版特性
|
||||
- Tpsbar 支持
|
||||
- 针对folia bug的一些修复
|
||||
- linear和b_linear(linear的重新实现)存档格式支持
|
||||
- 对单线程区域性能的优化
|
||||
- 更多有用的功能
|
||||
|
||||
## 下载
|
||||
|
||||
任何版本都可以在 [Release](https://github.com/LuminolMC/Lophine/releases), 中找到,你也可以通过[以下步骤](./README.md#构建)自己构建。
|
||||
|
||||
## 构建
|
||||
|
||||
要构建一个paperclip jar,你需要运行以下命令。你可以在lophine-server/build/libs中找到jar(注意:需要`JDK21`)
|
||||
|
||||
```shell
|
||||
./gradlew applyAllPatches && ./gradlew createMojmapPaperclipJar
|
||||
```
|
||||
|
||||
## 使用API
|
||||
|
||||
使用 Gradle:
|
||||
|
||||
```kotlin
|
||||
repositories {
|
||||
maven {
|
||||
url = "https://repo.menthamc.com/repository/maven-public/"
|
||||
url = "https://repo.menthamc.org/repository/maven-public/"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -48,7 +57,7 @@ dependencies {
|
||||
<repositories>
|
||||
<repository>
|
||||
<id>menthamc</id>
|
||||
<url>https://repo.menthamc.com/repository/maven-public/</url>
|
||||
<url>https://repo.menthamc.org/repository/maven-public/</url>
|
||||
</repository>
|
||||
</repositories>
|
||||
|
||||
@@ -62,27 +71,32 @@ dependencies {
|
||||
```
|
||||
|
||||
## 联系方式
|
||||
|
||||
> 如果您对这个项目感兴趣或有任何问题,请随时向我们提问。
|
||||
|
||||
**QQ群: [1015048616](http://qm.qq.com/cgi-bin/qm/qr?_wv=1027&k=QML5kIVsniPi1PlZvnjHQT_02EHsZ5Jc&authKey=%2FTCJsZC7JFQ9sxAroPCKuYnlV57Z5fyqp36ewXZk3Sn4iJ9p4MB1JKdc%2FFcX3HOM&noverify=0&group_code=1015048616)** | QQ频道: [点击加入](https://pd.qq.com/s/eq9krf9j) | Telegram: [点击加入](https://t.me/LuminolMinecraft) | Discord: [点击加入](https://discord.gg/Qd7m3V6eDx)
|
||||
|
||||
|
||||
## 关于 Issue
|
||||
|
||||
当您遇到任何问题时,请向我们提问,我们将尽力解决,但请记得清楚地描述您的问题并提供足够的日志等信息。
|
||||
|
||||
## 贡献代码
|
||||
|
||||
可查看 [Contributing](./docs/CONTRIBUTING.md)
|
||||
|
||||
## BStats
|
||||
Temporarily use Luminol's.
|
||||

|
||||
|
||||

|
||||
|
||||
## 特别感谢
|
||||
|
||||
<b>感谢[LegacyLands](https://github.com/LegacyLands)对本项目的赞助,如果你想开发一些跨folia/非folia平台的插件,[legacy-lands-library](https://github.com/LegacyLands/legacy-lands-library/)将会是个不错的lib</b>
|
||||
|
||||

|
||||
|
||||
## 请给我们一个 ⭐Star!
|
||||
|
||||
> 你的每一个免费的 ⭐Star 就是我们每一个前进的动力。
|
||||
|
||||
<a href="https://star-history.com/#LuminolMC/Luminol&LuminolMC/LightingLuminol&LuminolMC/Lophine&Date">
|
||||
|
||||
+25
-10
@@ -2,8 +2,10 @@
|
||||
|
||||
# Lophine
|
||||
|
||||
<h4>LightingLuminol is a Luminol fork with many useful optimizations and configurable vanilla features, aims to provide more function for survival-usable circuit on folia</h4>
|
||||
<h4>Lophine is a Luminol fork with many useful optimizations and configurable vanilla features, aims to provide
|
||||
more function for survival-usable circuit on folia (Please note that Fabric should be used for complete survival-usable)</h4>
|
||||
|
||||

|
||||
[](LICENSE.md)
|
||||
[](https://github.com/LuminolMC/Lophine/issues)
|
||||

|
||||
@@ -13,27 +15,35 @@
|
||||
**English** | [中文](./README.md)
|
||||
|
||||
## Features
|
||||
- Supported some Bukkit plugin
|
||||
- Configurable vanilla features
|
||||
- Tpsbar support
|
||||
- Useful optimizations to improve the performance of single threaded region
|
||||
- More functions
|
||||
|
||||
- Supported some Bukkit plugin
|
||||
- Configurable vanilla features
|
||||
- Tpsbar support
|
||||
- Useful fixes of bugs of folia
|
||||
- Linear and BufferedLinear(AKA. b_linear)(a reimplementation of linear) region format support
|
||||
- Useful optimizations to improve the performance of single threaded region
|
||||
- More functions
|
||||
|
||||
## Download
|
||||
|
||||
Any versions are available in the [release](https://github.com/LuminolMC/Lophine/releases), also you can build it by yourself through [the following steps](./README_EN.md#Build).
|
||||
|
||||
## Build
|
||||
|
||||
To build a paperclip jar, you need to run the following command. You can find the jar in lophine-server/build/libs(Note: `JDK21` is needed)
|
||||
|
||||
```shell
|
||||
./gradlew applyAllPatches && ./gradlew createMojmapPaperclipJar
|
||||
```
|
||||
|
||||
## Using API
|
||||
|
||||
For gradle:
|
||||
|
||||
```kotlin
|
||||
repositories {
|
||||
maven {
|
||||
url = "https://repo.menthamc.com/repository/maven-public/"
|
||||
url = "https://repo.menthamc.org/repository/maven-public/"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -48,7 +58,7 @@ For maven
|
||||
<repositories>
|
||||
<repository>
|
||||
<id>menthamc</id>
|
||||
<url>https://repo.menthamc.com/repository/maven-public/</url>
|
||||
<url>https://repo.menthamc.org/repository/maven-public/</url>
|
||||
</repository>
|
||||
</repositories>
|
||||
|
||||
@@ -62,26 +72,31 @@ For maven
|
||||
```
|
||||
|
||||
## Contact
|
||||
|
||||
> If you are interested in this project or have any issue, feel free to ask us.
|
||||
|
||||
**QQ Group: [1015048616](http://qm.qq.com/cgi-bin/qm/qr?_wv=1027&k=hTPlI5j6XB8pgk4sdx6RkjhBPGG1r4IR&authKey=pnu6uCKQP7Sja2CJWC15Qi3BeI%2FAsh8tU4m5muufMBjbB3zz%2BwHBZCTRRdSNKhld&noverify=0&group_code=1015048616)** | QQ Channel: [Click To Join](https://pd.qq.com/s/eq9krf9j) | Telegram: [Click To Join](https://t.me/LuminolMinecraft) | Discord: [Click To Join](https://discord.gg/Qd7m3V6eDx)
|
||||
|
||||
## About Issue
|
||||
|
||||
When you meet any problems, just ask us, we will do our best to solve it, but remember to state your problem clear and provide enough logs etc.</br>
|
||||
|
||||
## Pull Requests
|
||||
|
||||
See [Contributing](./docs/CONTRIBUTING_EN.md)
|
||||
|
||||
## BStats
|
||||
Temporarily use Luminol's.
|
||||

|
||||
|
||||

|
||||
|
||||
## Special Thanks
|
||||
|
||||
Thanks [LegacyLands](https://github.com/LegacyLands) for sponsoring this project.If you want to develop some crossing folia/non-folia platform plugins, [legacy-lands-library](https://github.com/LegacyLands/legacy-lands-library/) will be a nice choice for you
|
||||
|
||||

|
||||
|
||||
## Please ⭐star us!
|
||||
|
||||
<a href="https://star-history.com/#LuminolMC/Luminol&LuminolMC/LightingLuminol&LuminolMC/Lophine&Date">
|
||||
<picture>
|
||||
<source media="(prefers-color-scheme: dark)" srcset="https://api.star-history.com/svg?repos=LuminolMC/Luminol%2CLuminolMC/LightingLuminol%2CLuminolMC/Lophine&type=Date&theme=dark" />
|
||||
|
||||
+11
-10
@@ -3,7 +3,7 @@ import org.gradle.api.tasks.testing.logging.TestLogEvent
|
||||
|
||||
plugins {
|
||||
java // TODO java launcher tasks
|
||||
id("io.papermc.paperweight.patcher") version "2.0.0-beta.17"
|
||||
id("moe.luminolmc.hyacinthusweight.patcher") version "+" // Automatically pulls the latest
|
||||
}
|
||||
|
||||
paperweight {
|
||||
@@ -41,6 +41,7 @@ paperweight {
|
||||
}
|
||||
|
||||
val paperMavenPublicUrl = "https://repo.papermc.io/repository/maven-public/"
|
||||
val menthaMavenPublicUrl = "https://repo.menthamc.org/repository/maven-public/";
|
||||
|
||||
subprojects {
|
||||
apply(plugin = "java-library")
|
||||
@@ -55,6 +56,7 @@ subprojects {
|
||||
repositories {
|
||||
mavenCentral()
|
||||
maven(paperMavenPublicUrl)
|
||||
maven(menthaMavenPublicUrl)
|
||||
}
|
||||
|
||||
dependencies {
|
||||
@@ -65,16 +67,15 @@ subprojects {
|
||||
isPreserveFileTimestamps = false
|
||||
isReproducibleFileOrder = true
|
||||
}
|
||||
tasks.withType<JavaCompile> {
|
||||
tasks.withType<JavaCompile>().configureEach {
|
||||
options.encoding = Charsets.UTF_8.name()
|
||||
options.release = 21
|
||||
options.isFork = true
|
||||
options.forkOptions.memoryMaximumSize = "6g" // Prevent OOM during building
|
||||
}
|
||||
tasks.withType<Javadoc> {
|
||||
tasks.withType<Javadoc>().configureEach {
|
||||
options.encoding = Charsets.UTF_8.name()
|
||||
}
|
||||
tasks.withType<ProcessResources> {
|
||||
tasks.withType<ProcessResources>().configureEach {
|
||||
filteringCharset = Charsets.UTF_8.name()
|
||||
}
|
||||
tasks.withType<Test> {
|
||||
@@ -87,17 +88,17 @@ subprojects {
|
||||
|
||||
extensions.configure<PublishingExtension> {
|
||||
repositories {
|
||||
maven("https://repo.menthamc.com/repository/maven-snapshots/") {
|
||||
maven("https://repo.menthamc.org/repository/maven-snapshots/") {
|
||||
name = "MenthaMC"
|
||||
credentials(PasswordCredentials::class) {
|
||||
username = System.getenv("MAVEN_REPO_USER")
|
||||
password = System.getenv("MAVEN_REPO_PASSWORD")
|
||||
username = System.getenv("PRIVATE_MAVEN_REPO_USERNAME")
|
||||
password = System.getenv("PRIVATE_MAVEN_REPO_PASSWORD")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
tasks.withType<Javadoc> {
|
||||
tasks.withType<Javadoc>().configureEach {
|
||||
options {
|
||||
(this as StandardJavadocDocletOptions).apply {
|
||||
addStringOption("-add-modules", "jdk.incubator.vector")
|
||||
@@ -105,4 +106,4 @@ subprojects {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+14
-9
@@ -1,5 +1,5 @@
|
||||
为Lophine贡献代码
|
||||
===========
|
||||
===============
|
||||
|
||||
[English](./CONTRIBUTING_EN.md) | **中文**
|
||||
|
||||
@@ -41,19 +41,24 @@ Lophine 使用和 Folia 一样的补丁系统,并为了针对不同部分的
|
||||
|
||||
1. 将你的仓库 clone 到本地;
|
||||
2. 在你的 IDE 或 终端 内执行 Gradle 的 `applyAllPatches` 任务,如果是在终端内,你可以执行 `./gradlew applyAllPatches`;
|
||||
3. 在执行操作后,仓库根目录下应该存在以下目录对: `lophine-api` 和 `lophine-server` , `folia-api` 和 `folia-server` , 以及 `paper-api` 和 `paper-server`(下文称作 `*-api` 和 `*-server` );
|
||||
3. 在执行操作后,仓库根目录下应该存在以下目录对: `lophine-api` 和 `lophine-server` , `luminol-api` 和 `luminol-server` , `folia-api` 和 `folia-server` , 以及 `paper-api` 和 `paper-server`(下文称作 `*-api` 和 `*-server` );
|
||||
4. 进入 仓库根目录下的 `*-api` 和 `*-server` 文件夹进行修改。
|
||||
|
||||
以下为对上述各个文件夹的简单描述,详细描述可以参考[这里](https://github.com/Toffikk/paperweight-examples/blob/18241979c88068d5b061d95ad69c98ecb201c246/README.md):
|
||||
|
||||
1. API部分
|
||||
- `lophine-api` :暂无有效文件
|
||||
- `folia-api` :对folia-API的修改应当在此文件夹下进行
|
||||
- `paper-api` :对paper-API/spigot-API/bukkit-API的修改应该在此文件夹下进行
|
||||
|
||||
- `lophine-api` :对新增API的修改
|
||||
- `luminol-api` :对luminol-API的修改应当在此文件夹下进行
|
||||
- `folia-api` :对folia-API的修改应当在此文件夹下进行
|
||||
- `paper-api` :对paper-API/spigot-API/bukkit-API的修改应该在此文件夹下进行
|
||||
|
||||
2. Server部分
|
||||
- `lophine-server` :对Minecraft原版服务器的修改应当在此文件夹下进行
|
||||
- `folia-server` :暂无有效文件
|
||||
- `paper-server` :对于非原版的对服务器逻辑的修改应当在此文件夹下进行
|
||||
|
||||
- `lophine-server` :对Minecraft原版服务器的修改和新增文件应当在此文件夹下进行
|
||||
- `luminol-server` :对luminol-server的修改应当在此文件夹下进行
|
||||
- `folia-server` :对folia-Server的修改应当在此文件夹下进行
|
||||
- `paper-server` :对于paper对服务器逻辑的修改应当在此文件夹下进行
|
||||
|
||||
顺便一提,仓库根目录下的 `*-api` 和 `*-server` 并不是正常的 git 仓库:
|
||||
|
||||
@@ -80,7 +85,7 @@ Lophine 使用和 Folia 一样的补丁系统,并为了针对不同部分的
|
||||
|
||||
1. 在 HEAD 上直接进行修改;
|
||||
2. 使用 `git commit -a --fixup <hash>` 来进行一个更正提交;(不要提交对在lophine新建文件的修改)
|
||||
- 如果你想要更改提交信息,你也可以用 `--squash` 来代替 `--fixup`。
|
||||
- 如果你想要更改提交信息,你也可以用 `--squash` 来代替 `--fixup`。
|
||||
3. 使用 `git rebase -i --autosquash base` 来进行自动变基,你只需要输入 `:q` 来关闭确认页面即可;
|
||||
4. 运行 Gradle 任务 `fixupPaperApiFilePatches` 来修改已被修改的在lophine新建文件的补丁(注意不要提交);
|
||||
5. 运行 Gradle 任务 `rebuildAllServerPatches` 来修改已被修改的补丁;
|
||||
|
||||
+12
-8
@@ -1,5 +1,5 @@
|
||||
Contributing to Lophine
|
||||
===========
|
||||
=======================
|
||||
|
||||
**English** | [中文](./CONTRIBUTING.md)
|
||||
|
||||
@@ -44,20 +44,24 @@ If you have forked the main repository, then you should follow the steps below:
|
||||
|
||||
1. Clone your repository to local
|
||||
2. Run Gradle's `applyAllPatches` task in your IDE or terminal (You can run `./gradlew applyAllPatches` directly in terminal.)
|
||||
3. After performing the operation, the following directory pairs should exist in the root directory of the warehouse: `lophine-api` and `lophine-server` , `folia-api` and `folia-server` , `paper-api` and `paper-server` (Referred to `*-api` and `*-server` as below)
|
||||
3. After performing the operation, the following directory pairs should exist in the root directory of the warehouse: `lophine-api` and `lophine-server` , `luminol-api` and `luminol-server` , `folia-api` and `folia-server` , `paper-api` and `paper-server` (Referred to `*-api` and `*-server` as below)
|
||||
4. Enter `*-api` and `*-server` directory to carry out modifications.
|
||||
|
||||
The following is the simple description of the aforementioned folders, detailed description can be referred to [here](https://github.com/Toffikk/paperweight-examples/blob/18241979c88068d5b061d95ad69c98ecb201c246/README.md):
|
||||
|
||||
1. API part
|
||||
- `lophine-api` : There is no valid file
|
||||
- `folia-api` : Modifications to Folia API should be carried out in this folder
|
||||
- `paper-api` : Modifications to Paper API/Spigot API/Bukkit API should be carried out in this folder
|
||||
|
||||
- `lophine-api` : Modifications to the new API
|
||||
- `luminol-api` : Modifications to Luminol API should be carried out in this folder
|
||||
- `folia-api` : Modifications to Folia API should be carried out in this folder
|
||||
- `paper-api` : Modifications to Paper API/Spigot API/Bukkit API should be carried out in this folder
|
||||
|
||||
2. Server part
|
||||
- `lophine-server` : Modifications to Minecraft Vanilla Server's source logic should be carried out in this folder
|
||||
- `folia-server` : There is no valid file
|
||||
- `paper-server` : Modifications to non-vanilla server logic should be carried out in this folder
|
||||
|
||||
- `lophine-server` : Changes and new files to the Minecraft vanilla server should be made in this folder
|
||||
- `luminol-server` : Changes to luminol-server should be made in this folder
|
||||
- `folia-server` : Changes to folia-server should be made in this folder
|
||||
- `paper-server` : Modifications to the server logic for paper should be made in this folder
|
||||
|
||||
BTW, `*-api` and `*-server` and are not normal git repositories.
|
||||
|
||||
|
||||
+6
-4
@@ -1,8 +1,10 @@
|
||||
group=me.earthme.lophine
|
||||
version=1.21.5-R0.1-SNAPSHOT
|
||||
mcVersion=1.21.5
|
||||
group=fun.bm.lophine
|
||||
version=1.21.8-R0.1-SNAPSHOT
|
||||
mcVersion=1.21.8
|
||||
release=2
|
||||
# 0 for skip release, 1 for pre-release, 2 for release
|
||||
|
||||
luminolRef=818e1132dfb8a9849b082f6adf767b66c7da8b31
|
||||
luminolRef=ce22eda2a30c6724e57616de5f4f246d056e7a54
|
||||
|
||||
org.gradle.configuration-cache=true
|
||||
org.gradle.caching=true
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
distributionBase=GRADLE_USER_HOME
|
||||
distributionPath=wrapper/dists
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-8.14-bin.zip
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-8.14.1-bin.zip
|
||||
networkTimeout=10000
|
||||
validateDistributionUrl=true
|
||||
zipStoreBase=GRADLE_USER_HOME
|
||||
|
||||
+71
-70
@@ -1,4 +1,5 @@
|
||||
### GNU GENERAL PUBLIC LICENSE
|
||||
==============================
|
||||
|
||||
Version 3, 29 June 2007
|
||||
|
||||
@@ -217,23 +218,23 @@ produce it from the Program, in the form of source code under the
|
||||
terms of section 4, provided that you also meet all of these
|
||||
conditions:
|
||||
|
||||
- a) The work must carry prominent notices stating that you modified
|
||||
it, and giving a relevant date.
|
||||
- b) The work must carry prominent notices stating that it is
|
||||
released under this License and any conditions added under
|
||||
section 7. This requirement modifies the requirement in section 4
|
||||
to "keep intact all notices".
|
||||
- c) You must license the entire work, as a whole, under this
|
||||
License to anyone who comes into possession of a copy. This
|
||||
License will therefore apply, along with any applicable section 7
|
||||
additional terms, to the whole of the work, and all its parts,
|
||||
regardless of how they are packaged. This License gives no
|
||||
permission to license the work in any other way, but it does not
|
||||
invalidate such permission if you have separately received it.
|
||||
- d) If the work has interactive user interfaces, each must display
|
||||
Appropriate Legal Notices; however, if the Program has interactive
|
||||
interfaces that do not display Appropriate Legal Notices, your
|
||||
work need not make them do so.
|
||||
- a) The work must carry prominent notices stating that you modified
|
||||
it, and giving a relevant date.
|
||||
- b) The work must carry prominent notices stating that it is
|
||||
released under this License and any conditions added under
|
||||
section 7. This requirement modifies the requirement in section 4
|
||||
to "keep intact all notices".
|
||||
- c) You must license the entire work, as a whole, under this
|
||||
License to anyone who comes into possession of a copy. This
|
||||
License will therefore apply, along with any applicable section 7
|
||||
additional terms, to the whole of the work, and all its parts,
|
||||
regardless of how they are packaged. This License gives no
|
||||
permission to license the work in any other way, but it does not
|
||||
invalidate such permission if you have separately received it.
|
||||
- d) If the work has interactive user interfaces, each must display
|
||||
Appropriate Legal Notices; however, if the Program has interactive
|
||||
interfaces that do not display Appropriate Legal Notices, your
|
||||
work need not make them do so.
|
||||
|
||||
A compilation of a covered work with other separate and independent
|
||||
works, which are not by their nature extensions of the covered work,
|
||||
@@ -252,42 +253,42 @@ sections 4 and 5, provided that you also convey the machine-readable
|
||||
Corresponding Source under the terms of this License, in one of these
|
||||
ways:
|
||||
|
||||
- a) Convey the object code in, or embodied in, a physical product
|
||||
(including a physical distribution medium), accompanied by the
|
||||
Corresponding Source fixed on a durable physical medium
|
||||
customarily used for software interchange.
|
||||
- b) Convey the object code in, or embodied in, a physical product
|
||||
(including a physical distribution medium), accompanied by a
|
||||
written offer, valid for at least three years and valid for as
|
||||
long as you offer spare parts or customer support for that product
|
||||
model, to give anyone who possesses the object code either (1) a
|
||||
copy of the Corresponding Source for all the software in the
|
||||
product that is covered by this License, on a durable physical
|
||||
medium customarily used for software interchange, for a price no
|
||||
more than your reasonable cost of physically performing this
|
||||
conveying of source, or (2) access to copy the Corresponding
|
||||
Source from a network server at no charge.
|
||||
- c) Convey individual copies of the object code with a copy of the
|
||||
written offer to provide the Corresponding Source. This
|
||||
alternative is allowed only occasionally and noncommercially, and
|
||||
only if you received the object code with such an offer, in accord
|
||||
with subsection 6b.
|
||||
- d) Convey the object code by offering access from a designated
|
||||
place (gratis or for a charge), and offer equivalent access to the
|
||||
Corresponding Source in the same way through the same place at no
|
||||
further charge. You need not require recipients to copy the
|
||||
Corresponding Source along with the object code. If the place to
|
||||
copy the object code is a network server, the Corresponding Source
|
||||
may be on a different server (operated by you or a third party)
|
||||
that supports equivalent copying facilities, provided you maintain
|
||||
clear directions next to the object code saying where to find the
|
||||
Corresponding Source. Regardless of what server hosts the
|
||||
Corresponding Source, you remain obligated to ensure that it is
|
||||
available for as long as needed to satisfy these requirements.
|
||||
- e) Convey the object code using peer-to-peer transmission,
|
||||
provided you inform other peers where the object code and
|
||||
Corresponding Source of the work are being offered to the general
|
||||
public at no charge under subsection 6d.
|
||||
- a) Convey the object code in, or embodied in, a physical product
|
||||
(including a physical distribution medium), accompanied by the
|
||||
Corresponding Source fixed on a durable physical medium
|
||||
customarily used for software interchange.
|
||||
- b) Convey the object code in, or embodied in, a physical product
|
||||
(including a physical distribution medium), accompanied by a
|
||||
written offer, valid for at least three years and valid for as
|
||||
long as you offer spare parts or customer support for that product
|
||||
model, to give anyone who possesses the object code either (1) a
|
||||
copy of the Corresponding Source for all the software in the
|
||||
product that is covered by this License, on a durable physical
|
||||
medium customarily used for software interchange, for a price no
|
||||
more than your reasonable cost of physically performing this
|
||||
conveying of source, or (2) access to copy the Corresponding
|
||||
Source from a network server at no charge.
|
||||
- c) Convey individual copies of the object code with a copy of the
|
||||
written offer to provide the Corresponding Source. This
|
||||
alternative is allowed only occasionally and noncommercially, and
|
||||
only if you received the object code with such an offer, in accord
|
||||
with subsection 6b.
|
||||
- d) Convey the object code by offering access from a designated
|
||||
place (gratis or for a charge), and offer equivalent access to the
|
||||
Corresponding Source in the same way through the same place at no
|
||||
further charge. You need not require recipients to copy the
|
||||
Corresponding Source along with the object code. If the place to
|
||||
copy the object code is a network server, the Corresponding Source
|
||||
may be on a different server (operated by you or a third party)
|
||||
that supports equivalent copying facilities, provided you maintain
|
||||
clear directions next to the object code saying where to find the
|
||||
Corresponding Source. Regardless of what server hosts the
|
||||
Corresponding Source, you remain obligated to ensure that it is
|
||||
available for as long as needed to satisfy these requirements.
|
||||
- e) Convey the object code using peer-to-peer transmission,
|
||||
provided you inform other peers where the object code and
|
||||
Corresponding Source of the work are being offered to the
|
||||
public at no charge under subsection 6d.
|
||||
|
||||
A separable portion of the object code, whose source code is excluded
|
||||
from the Corresponding Source as a System Library, need not be
|
||||
@@ -363,23 +364,23 @@ Notwithstanding any other provision of this License, for material you
|
||||
add to a covered work, you may (if authorized by the copyright holders
|
||||
of that material) supplement the terms of this License with terms:
|
||||
|
||||
- a) Disclaiming warranty or limiting liability differently from the
|
||||
terms of sections 15 and 16 of this License; or
|
||||
- b) Requiring preservation of specified reasonable legal notices or
|
||||
author attributions in that material or in the Appropriate Legal
|
||||
Notices displayed by works containing it; or
|
||||
- c) Prohibiting misrepresentation of the origin of that material,
|
||||
or requiring that modified versions of such material be marked in
|
||||
reasonable ways as different from the original version; or
|
||||
- d) Limiting the use for publicity purposes of names of licensors
|
||||
or authors of the material; or
|
||||
- e) Declining to grant rights under trademark law for use of some
|
||||
trade names, trademarks, or service marks; or
|
||||
- f) Requiring indemnification of licensors and authors of that
|
||||
material by anyone who conveys the material (or modified versions
|
||||
of it) with contractual assumptions of liability to the recipient,
|
||||
for any liability that these contractual assumptions directly
|
||||
impose on those licensors and authors.
|
||||
- a) Disclaiming warranty or limiting liability differently from the
|
||||
terms of sections 15 and 16 of this License; or
|
||||
- b) Requiring preservation of specified reasonable legal notices or
|
||||
author attributions in that material or in the Appropriate Legal
|
||||
Notices displayed by works containing it; or
|
||||
- c) Prohibiting misrepresentation of the origin of that material,
|
||||
or requiring that modified versions of such material be marked in
|
||||
reasonable ways as different from the original version; or
|
||||
- d) Limiting the use for publicity purposes of names of licensors
|
||||
or authors of the material; or
|
||||
- e) Declining to grant rights under trademark law for use of some
|
||||
trade names, trademarks, or service marks; or
|
||||
- f) Requiring indemnification of licensors and authors of that
|
||||
material by anyone who conveys the material (or modified versions
|
||||
of it) with contractual assumptions of liability to the recipient,
|
||||
for any liability that these contractual assumptions directly
|
||||
impose on those licensors and authors.
|
||||
|
||||
All other non-permissive additional terms are considered "further
|
||||
restrictions" within the meaning of section 10. If the Program as you
|
||||
|
||||
+2
-2
@@ -1,5 +1,5 @@
|
||||
The MIT License (MIT)
|
||||
=====================
|
||||
### The MIT License (MIT)
|
||||
=========================
|
||||
|
||||
Permission is hereby granted, free of charge, to any person
|
||||
obtaining a copy of this software and associated documentation
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
--- a/luminol-server/build.gradle.kts
|
||||
+++ b/luminol-server/build.gradle.kts
|
||||
@@ -54,7 +_,23 @@
|
||||
@@ -52,7 +_,23 @@
|
||||
}
|
||||
}
|
||||
|
||||
@@ -23,8 +23,8 @@
|
||||
+
|
||||
+ activeFork = lophine
|
||||
|
||||
|
||||
spigot {
|
||||
buildDataRef = "702e1a0a5072b2c4082371d5228cb30525687efc"
|
||||
@@ -142,12 +_,16 @@
|
||||
resources { srcDir("../paper-server/src/main/resources") }
|
||||
java { srcDir("../folia-server/src/main/java") }
|
||||
@@ -47,11 +47,11 @@
|
||||
|
||||
dependencies {
|
||||
- implementation(project(":luminol-api")) // Luminol
|
||||
+ implementation(project(":lophine-api")) // Luminol // Lophine
|
||||
implementation("com.electronwill.night-config:toml:3.8.2") // Luminol - Night config
|
||||
// Abomination start
|
||||
+ implementation(project(":lophine-api")) // Lophine
|
||||
// Luminol start - Dependenices insert
|
||||
implementation("com.electronwill.night-config:toml:3.8.2") // Night config
|
||||
implementation("com.github.luben:zstd-jni:1.5.4-1")
|
||||
@@ -264,14 +_,14 @@
|
||||
@@ -276,14 +_,14 @@
|
||||
val gitBranch = git.exec(providers, "rev-parse", "--abbrev-ref", "HEAD").get().trim()
|
||||
attributes(
|
||||
"Main-Class" to "org.bukkit.craftbukkit.Main",
|
||||
@@ -62,7 +62,7 @@
|
||||
- "Specification-Title" to "Luminol",
|
||||
+ "Specification-Title" to "Lophine",
|
||||
"Specification-Version" to project.version,
|
||||
"Specification-Vendor" to "Luminol Team",
|
||||
"Specification-Vendor" to "LuminolMC org",
|
||||
- "Brand-Id" to "luminolmc:luminol",
|
||||
- "Brand-Name" to "Luminol",
|
||||
+ "Brand-Id" to "luminolmc:lophine",
|
||||
@@ -70,3 +70,12 @@
|
||||
"Build-Number" to (build ?: ""),
|
||||
"Build-Time" to buildTime.toString(),
|
||||
"Git-Branch" to gitBranch,
|
||||
@@ -447,7 +_,7 @@
|
||||
}
|
||||
|
||||
fill {
|
||||
- project("luminol")
|
||||
+ project("lophine")
|
||||
versionFamily(paperweight.minecraftVersion.map { it.split(".", "-").takeWhile { part -> part.toIntOrNull() != null }.take(2).joinToString(".") })
|
||||
version(paperweight.minecraftVersion)
|
||||
|
||||
|
||||
@@ -0,0 +1,31 @@
|
||||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: Helvetica Volubi <suisuroru@blue-millennium.fun>
|
||||
Date: Sun, 8 Jun 2025 00:58:13 +0800
|
||||
Subject: [PATCH] Rebrand to Lophine
|
||||
|
||||
|
||||
diff --git a/src/main/java/me/earthme/luminol/config/ConfigManager.java b/src/main/java/me/earthme/luminol/config/ConfigManager.java
|
||||
index 136b9e9c3bcb43698c653060665dc82444e09768..cc2b7b8f897570e5d079f80375c93cf2550df6af 100644
|
||||
--- a/src/main/java/me/earthme/luminol/config/ConfigManager.java
|
||||
+++ b/src/main/java/me/earthme/luminol/config/ConfigManager.java
|
||||
@@ -21,6 +21,7 @@ public class ConfigManager {
|
||||
|
||||
public static void initConfigs() {
|
||||
configfiles.put("luminol", ConfigsInstance.of(new File("luminol_config"), "luminol", "me.earthme.luminol.config.modules"));
|
||||
+ configfiles.put("lophine", ConfigsInstance.of(new File("lophine_config"), "lophine", "fun.bm.lophine.config.modules")); // add lophine config to global config
|
||||
preLoad();
|
||||
}
|
||||
|
||||
diff --git a/src/main/java/me/earthme/luminol/config/modules/misc/ServerModNameConfig.java b/src/main/java/me/earthme/luminol/config/modules/misc/ServerModNameConfig.java
|
||||
index 997bc00dffbf5c15fde610c95527e8013ae57660..d94e11eb9f10c9622052c7b327ccfdf24de6ae73 100644
|
||||
--- a/src/main/java/me/earthme/luminol/config/modules/misc/ServerModNameConfig.java
|
||||
+++ b/src/main/java/me/earthme/luminol/config/modules/misc/ServerModNameConfig.java
|
||||
@@ -8,7 +8,7 @@ import me.earthme.luminol.config.flags.ConfigInfo;
|
||||
@ConfigClassInfo(configAttribution = EnumConfigCategory.MISC, mainName = "server_mod_name")
|
||||
public class ServerModNameConfig implements IConfigModule {
|
||||
@ConfigInfo(baseName = "name")
|
||||
- public static String serverModName = "Luminol";
|
||||
+ public static String serverModName = "Lophine";
|
||||
|
||||
@ConfigInfo(baseName = "vanilla_spoof")
|
||||
public static boolean fakeVanilla = false;
|
||||
@@ -0,0 +1,30 @@
|
||||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: Helvetica Volubi <suisuroru@blue-millennium.fun>
|
||||
Date: Mon, 28 Jul 2025 23:02:47 +0800
|
||||
Subject: [PATCH] Transformed Configs
|
||||
|
||||
|
||||
diff --git a/src/main/java/me/earthme/luminol/config/modules/experiment/CommandBlockExecutionConfig.java b/src/main/java/me/earthme/luminol/config/modules/experiment/CommandBlockExecutionConfig.java
|
||||
index 83d803d7d7f3cecdbcb593da70234aa9dd81a1ff..00ad1597bdb20118d756d9371c0d50890d0a5e8c 100644
|
||||
--- a/src/main/java/me/earthme/luminol/config/modules/experiment/CommandBlockExecutionConfig.java
|
||||
+++ b/src/main/java/me/earthme/luminol/config/modules/experiment/CommandBlockExecutionConfig.java
|
||||
@@ -7,6 +7,7 @@ import me.earthme.luminol.config.flags.ConfigInfo;
|
||||
|
||||
@ConfigClassInfo(configAttribution = EnumConfigCategory.EXPERIMENT, mainName = "force_enable_command_block_command_execution")
|
||||
public class CommandBlockExecutionConfig implements IConfigModule {
|
||||
+ @me.earthme.luminol.config.flags.TransformedConfig(name = "command_block_enabled", category = {"experiment", "command"}, originInstance = "lophine")
|
||||
@ConfigInfo(baseName = "enabled")
|
||||
public static boolean enabled = false;
|
||||
}
|
||||
diff --git a/src/main/java/me/earthme/luminol/config/modules/misc/PaperPacketLimiterConfig.java b/src/main/java/me/earthme/luminol/config/modules/misc/PaperPacketLimiterConfig.java
|
||||
index 385612dd2e3bd2b4651ccfda41986ff7047067e4..4450ba56a491c15aea6ea9e1fed0e379660fbec5 100644
|
||||
--- a/src/main/java/me/earthme/luminol/config/modules/misc/PaperPacketLimiterConfig.java
|
||||
+++ b/src/main/java/me/earthme/luminol/config/modules/misc/PaperPacketLimiterConfig.java
|
||||
@@ -7,6 +7,7 @@ import me.earthme.luminol.config.flags.ConfigInfo;
|
||||
|
||||
@ConfigClassInfo(configAttribution = EnumConfigCategory.MISC, mainName = "force_disable_packet_limiter_of_paper")
|
||||
public class PaperPacketLimiterConfig implements IConfigModule {
|
||||
+ @me.earthme.luminol.config.flags.TransformedConfig(name = "unlimit-packet", category = {"misc", "network"}, originInstance = "lophine")
|
||||
@ConfigInfo(baseName = "force_disable")
|
||||
public static boolean forceDisable = false;
|
||||
}
|
||||
@@ -0,0 +1,188 @@
|
||||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: Helvetica Volubi <suisuroru@blue-millennium.fun>
|
||||
Date: Wed, 13 Aug 2025 22:23:46 +0800
|
||||
Subject: [PATCH] Leaves: Item overstack util
|
||||
|
||||
Co-authored by: violetc <58360096+s-yh-china@users.noreply.github.com>
|
||||
As a part of : Leaves (https://github.com/LeavesMC/Leaves/blob/d93e9766d3797d130f66179e70a1b374c546fef7/leaves-server/src/main/java/org/leavesmc/leaves/util/ItemOverstackUtils.java)
|
||||
Licensed under: GPL-3.0 (https://www.gnu.org/licenses/gpl-3.0.html)
|
||||
|
||||
diff --git a/src/main/java/org/leavesmc/leaves/util/ItemOverstackUtils.java b/src/main/java/org/leavesmc/leaves/util/ItemOverstackUtils.java
|
||||
index 102de78be0eb82d6898b4104fad1bf5e8366f993..11891a66fe3232287238ea87289444586fbc9717 100644
|
||||
--- a/src/main/java/org/leavesmc/leaves/util/ItemOverstackUtils.java
|
||||
+++ b/src/main/java/org/leavesmc/leaves/util/ItemOverstackUtils.java
|
||||
@@ -17,12 +17,173 @@
|
||||
|
||||
package org.leavesmc.leaves.util;
|
||||
|
||||
+import fun.bm.lophine.config.modules.misc.ContainerExpansionConfig;
|
||||
+import net.minecraft.core.component.DataComponents;
|
||||
+import net.minecraft.nbt.CompoundTag;
|
||||
+import net.minecraft.world.entity.item.ItemEntity;
|
||||
+import net.minecraft.world.item.BlockItem;
|
||||
import net.minecraft.world.item.ItemStack;
|
||||
+import net.minecraft.world.item.component.CustomData;
|
||||
+import net.minecraft.world.item.component.ItemContainerContents;
|
||||
+import net.minecraft.world.level.block.ShulkerBoxBlock;
|
||||
+import org.jetbrains.annotations.NotNull;
|
||||
+
|
||||
+import java.util.List;
|
||||
+import java.util.Objects;
|
||||
+import java.util.Optional;
|
||||
|
||||
// Only the used part is retained
|
||||
public class ItemOverstackUtils {
|
||||
+ private static final List<ItemUtil> overstackUtils = List.of(
|
||||
+ new ShulkerBox()
|
||||
+ );
|
||||
+
|
||||
+ public static int getItemStackMaxCount(ItemStack stack) {
|
||||
+ int size;
|
||||
+ for (ItemUtil util : overstackUtils) {
|
||||
+ if ((size = util.getMaxServerStackCount(stack)) != -1) {
|
||||
+ return size;
|
||||
+ }
|
||||
+ }
|
||||
+ return stack.getMaxStackSize();
|
||||
+ }
|
||||
+
|
||||
+ public static int getNetworkMaxCount(ItemStack stack) {
|
||||
+ int size;
|
||||
+ for (ItemUtil util : overstackUtils) {
|
||||
+ if ((size = util.getMaxClientStackCount(stack)) != -1) {
|
||||
+ return size;
|
||||
+ }
|
||||
+ }
|
||||
+ return stack.getMaxStackSize();
|
||||
+ }
|
||||
+
|
||||
+ public static boolean tryStackItems(ItemEntity self, ItemEntity other) {
|
||||
+ for (ItemUtil util : overstackUtils) {
|
||||
+ if (util.tryStackItems(self, other)) {
|
||||
+ return true;
|
||||
+ }
|
||||
+ }
|
||||
+ return false;
|
||||
+ }
|
||||
+
|
||||
+ public static boolean hasOverstackingItem() {
|
||||
+ return overstackUtils.stream().anyMatch(ItemUtil::isEnabled);
|
||||
+ }
|
||||
+
|
||||
+ public static int getItemStackMaxCountReal(ItemStack stack) {
|
||||
+ CompoundTag nbt = Optional.ofNullable(stack.get(DataComponents.CUSTOM_DATA)).orElse(CustomData.EMPTY).copyTag();
|
||||
+ return nbt.getInt("Leaves.RealStackSize").orElse(stack.getMaxStackSize());
|
||||
+ }
|
||||
+
|
||||
+ public static ItemStack encodeMaxStackSize(ItemStack itemStack) {
|
||||
+ int realMaxStackSize = getItemStackMaxCountReal(itemStack);
|
||||
+ int modifiedMaxStackSize = getNetworkMaxCount(itemStack);
|
||||
+ if (itemStack.getMaxStackSize() != modifiedMaxStackSize) {
|
||||
+ itemStack.set(DataComponents.MAX_STACK_SIZE, modifiedMaxStackSize);
|
||||
+ CompoundTag nbt = itemStack.getOrDefault(DataComponents.CUSTOM_DATA, CustomData.EMPTY).copyTag();
|
||||
+ nbt.putInt("Leaves.RealStackSize", realMaxStackSize);
|
||||
+ itemStack.set(DataComponents.CUSTOM_DATA, CustomData.of(nbt));
|
||||
+ }
|
||||
+ return itemStack;
|
||||
+ }
|
||||
+
|
||||
+ public static ItemStack decodeMaxStackSize(ItemStack itemStack) {
|
||||
+ int realMaxStackSize = getItemStackMaxCountReal(itemStack);
|
||||
+ if (itemStack.getMaxStackSize() != realMaxStackSize) {
|
||||
+ itemStack.set(DataComponents.MAX_STACK_SIZE, realMaxStackSize);
|
||||
+ CompoundTag nbt = itemStack.getOrDefault(DataComponents.CUSTOM_DATA, CustomData.EMPTY).copyTag();
|
||||
+ nbt.remove("Leaves.RealStackSize");
|
||||
+ if (nbt.isEmpty()) {
|
||||
+ itemStack.remove(DataComponents.CUSTOM_DATA);
|
||||
+ } else {
|
||||
+ itemStack.set(DataComponents.CUSTOM_DATA, CustomData.of(nbt));
|
||||
+ }
|
||||
+ }
|
||||
+ return itemStack;
|
||||
+ }
|
||||
+
|
||||
public static float getItemStackSignalStrength(int maxStackSize, ItemStack itemStack) {
|
||||
float result = (float) itemStack.getCount() / Math.min(maxStackSize, itemStack.getMaxStackSize());
|
||||
return Math.clamp(result, 0f, 1f);
|
||||
}
|
||||
+
|
||||
+ public static boolean isStackable(ItemStack itemStack) {
|
||||
+ return getItemStackMaxCount(itemStack) > 1 && (!itemStack.isDamageableItem() || !itemStack.isDamaged());
|
||||
+ }
|
||||
+
|
||||
+
|
||||
+ private interface ItemUtil {
|
||||
+ boolean isEnabled();
|
||||
+
|
||||
+ boolean tryStackItems(ItemEntity self, ItemEntity other);
|
||||
+
|
||||
+ // number -> modified count, -1 -> I don't care
|
||||
+ int getMaxServerStackCount(ItemStack stack);
|
||||
+
|
||||
+ // number -> modified count, -1 -> I don't care
|
||||
+ default int getMaxClientStackCount(ItemStack stack) {
|
||||
+ return getMaxServerStackCount(stack);
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ private static class ShulkerBox implements ItemUtil {
|
||||
+ public static boolean shulkerBoxCheck(@NotNull ItemStack stack1, @NotNull ItemStack stack2) {
|
||||
+ if (ContainerExpansionConfig.nbtShulkerStackable) {
|
||||
+ return Objects.equals(stack1.getComponents(), stack2.getComponents());
|
||||
+ }
|
||||
+ return shulkerBoxNoItem(stack1) && shulkerBoxNoItem(stack2) && Objects.equals(stack1.getComponents(), stack2.getComponents());
|
||||
+ }
|
||||
+
|
||||
+ public static boolean shulkerBoxNoItem(@NotNull ItemStack stack) {
|
||||
+ return stack.getComponents().getOrDefault(DataComponents.CONTAINER, ItemContainerContents.EMPTY).stream().findAny().isEmpty();
|
||||
+ }
|
||||
+
|
||||
+ @Override
|
||||
+ public boolean isEnabled() {
|
||||
+ return ContainerExpansionConfig.shulkerCount > 1;
|
||||
+ }
|
||||
+
|
||||
+ @Override
|
||||
+ public boolean tryStackItems(ItemEntity self, ItemEntity other) {
|
||||
+ ItemStack selfStack = self.getItem();
|
||||
+ if (!isEnabled() ||
|
||||
+ !(selfStack.getItem() instanceof BlockItem blockItem) ||
|
||||
+ !(blockItem.getBlock() instanceof ShulkerBoxBlock)
|
||||
+ ) {
|
||||
+ return false;
|
||||
+ }
|
||||
+
|
||||
+ ItemStack otherStack = other.getItem();
|
||||
+ if (selfStack.getItem() == otherStack.getItem()
|
||||
+ && shulkerBoxCheck(selfStack, otherStack)
|
||||
+ && selfStack.getCount() != ContainerExpansionConfig.shulkerCount) {
|
||||
+ int amount = Math.min(otherStack.getCount(), ContainerExpansionConfig.shulkerCount - selfStack.getCount());
|
||||
+
|
||||
+ selfStack.grow(amount);
|
||||
+ self.setItem(selfStack);
|
||||
+
|
||||
+ self.pickupDelay = Math.max(other.pickupDelay, self.pickupDelay);
|
||||
+ self.age = Math.min(other.getAge(), self.age);
|
||||
+
|
||||
+ otherStack.shrink(amount);
|
||||
+ if (otherStack.isEmpty()) {
|
||||
+ other.discard();
|
||||
+ } else {
|
||||
+ other.setItem(otherStack);
|
||||
+ }
|
||||
+ return true;
|
||||
+ }
|
||||
+ return false;
|
||||
+ }
|
||||
+
|
||||
+ @Override
|
||||
+ public int getMaxServerStackCount(ItemStack stack) {
|
||||
+ if (isEnabled() && stack.getItem() instanceof BlockItem bi &&
|
||||
+ bi.getBlock() instanceof ShulkerBoxBlock && (ContainerExpansionConfig.nbtShulkerStackable || shulkerBoxNoItem(stack))) {
|
||||
+ return ContainerExpansionConfig.shulkerCount;
|
||||
+ }
|
||||
+ return -1;
|
||||
+ }
|
||||
+ }
|
||||
}
|
||||
\ No newline at end of file
|
||||
+60
@@ -0,0 +1,60 @@
|
||||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: Helvetica Volubi <suisuroru@blue-millennium.fun>
|
||||
Date: Mon, 7 Jul 2025 16:30:54 +0800
|
||||
Subject: [PATCH] Add config to allow infinite trade
|
||||
|
||||
|
||||
diff --git a/net/minecraft/world/entity/npc/Villager.java b/net/minecraft/world/entity/npc/Villager.java
|
||||
index 79242037e868a140513f032e74e8594c0c6ff41c..8570c5089b605125680f8558ae201550df2d0a47 100644
|
||||
--- a/net/minecraft/world/entity/npc/Villager.java
|
||||
+++ b/net/minecraft/world/entity/npc/Villager.java
|
||||
@@ -490,8 +490,15 @@ public class Villager extends AbstractVillager implements ReputationEventHandler
|
||||
}
|
||||
|
||||
private void resendOffersToTradingPlayer() {
|
||||
- MerchantOffers offers = this.getOffers();
|
||||
+ // Lophine start - infinite trade
|
||||
+ MerchantOffers offers = this.getOffers().copy();
|
||||
Player tradingPlayer = this.getTradingPlayer();
|
||||
+ if (fun.bm.lophine.config.modules.misc.VillagerConfig.villagerInfiniteTrade) {
|
||||
+ for (MerchantOffer merchantOffer : offers) {
|
||||
+ merchantOffer.maxUses = 524288;
|
||||
+ }
|
||||
+ }
|
||||
+ // Lophine end - infinite trade
|
||||
if (tradingPlayer != null && !offers.isEmpty()) {
|
||||
tradingPlayer.sendMerchantOffers(
|
||||
tradingPlayer.containerMenu.containerId,
|
||||
diff --git a/net/minecraft/world/item/trading/Merchant.java b/net/minecraft/world/item/trading/Merchant.java
|
||||
index 6e2fa442ddcd4ef3c4fe789b9ebac9dd409c9de7..28d743cc7beeb92a33683b8eea84096f94d90fb2 100644
|
||||
--- a/net/minecraft/world/item/trading/Merchant.java
|
||||
+++ b/net/minecraft/world/item/trading/Merchant.java
|
||||
@@ -42,7 +42,14 @@ public interface Merchant {
|
||||
new SimpleMenuProvider((containerId, inventory, player1) -> new MerchantMenu(containerId, inventory, this), displayName)
|
||||
);
|
||||
if (optionalInt.isPresent()) {
|
||||
- MerchantOffers offers = this.getOffers();
|
||||
+ // Lophine start - infinite trade
|
||||
+ MerchantOffers offers = this.getOffers().copy();
|
||||
+ if (fun.bm.lophine.config.modules.misc.VillagerConfig.villagerInfiniteTrade) {
|
||||
+ for (MerchantOffer merchantOffer : offers) {
|
||||
+ merchantOffer.maxUses = 5242884;
|
||||
+ }
|
||||
+ }
|
||||
+ // Lophine end - infinite trade
|
||||
if (!offers.isEmpty()) {
|
||||
player.sendMerchantOffers(optionalInt.getAsInt(), offers, level, this.getVillagerXp(), this.showProgressBar(), this.canRestock());
|
||||
}
|
||||
diff --git a/net/minecraft/world/item/trading/MerchantOffer.java b/net/minecraft/world/item/trading/MerchantOffer.java
|
||||
index 64c99df8ff305fa28c75dc03fc5ef8c61634ad84..21ec77d5167cfdf05043ea474165148fb2b0c2d9 100644
|
||||
--- a/net/minecraft/world/item/trading/MerchantOffer.java
|
||||
+++ b/net/minecraft/world/item/trading/MerchantOffer.java
|
||||
@@ -196,7 +196,7 @@ public class MerchantOffer {
|
||||
}
|
||||
|
||||
public boolean isOutOfStock() {
|
||||
- return this.uses >= this.maxUses;
|
||||
+ return (this.uses >= this.maxUses && !fun.bm.lophine.config.modules.misc.VillagerConfig.villagerInfiniteTrade); // Lophine - infinite trade
|
||||
}
|
||||
|
||||
public void setToOutOfStock() {
|
||||
@@ -1,33 +0,0 @@
|
||||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: Helvetica Volubi <suisuroru@blue-millennium.fun>
|
||||
Date: Sun, 8 Jun 2025 00:58:12 +0800
|
||||
Subject: [PATCH] Rebrand to Lophine
|
||||
|
||||
|
||||
diff --git a/net/minecraft/server/Main.java b/net/minecraft/server/Main.java
|
||||
index fcf599846a38beeb8f094d5376c01d5a690f7a2f..a70c04dc72a15e4e8de8de677951f1eb605057b0 100644
|
||||
--- a/net/minecraft/server/Main.java
|
||||
+++ b/net/minecraft/server/Main.java
|
||||
@@ -108,7 +108,7 @@ public class Main {
|
||||
JvmProfiler.INSTANCE.start(Environment.SERVER);
|
||||
}
|
||||
|
||||
- me.earthme.luminol.config.LuminolConfig.preLoadConfig(); // Luminol - Luminol config
|
||||
+ me.earthme.lophine.config.LophineConfig.initConfigs(); // Lophine - Lophine config
|
||||
io.papermc.paper.plugin.PluginInitializerManager.load(optionSet); // Paper
|
||||
Bootstrap.bootStrap();
|
||||
Bootstrap.validate();
|
||||
diff --git a/net/minecraft/server/dedicated/DedicatedServer.java b/net/minecraft/server/dedicated/DedicatedServer.java
|
||||
index 5290b9cb524160717fb40704fb10fc0d0a37941b..ba0cdba050cd3bf9231e15dd9543d34f37d9dae5 100644
|
||||
--- a/net/minecraft/server/dedicated/DedicatedServer.java
|
||||
+++ b/net/minecraft/server/dedicated/DedicatedServer.java
|
||||
@@ -171,8 +171,7 @@ public class DedicatedServer extends MinecraftServer implements ServerInterface
|
||||
this.paperConfigurations.initializeGlobalConfiguration(this.registryAccess());
|
||||
this.paperConfigurations.initializeWorldDefaultsConfiguration(this.registryAccess());
|
||||
// Paper end - initialize global and world-defaults configuration
|
||||
- me.earthme.luminol.config.LuminolConfig.finalizeLoadConfig(); //Luminol - load config file
|
||||
- me.earthme.luminol.config.LuminolConfig.setupLatch(); //Luminol - load config file
|
||||
+ me.earthme.lophine.config.LophineConfig.loadConfigFiles(); //Luminol - load config file // Lophine - load config file
|
||||
if (false) this.server.spark.enableEarlyIfRequested(); // Paper - spark // Luminol - Force disable builtin spark
|
||||
// Paper start - fix converting txt to json file; convert old users earlier after PlayerList creation but before file load/save
|
||||
if (this.convertOldUsers()) {
|
||||
-19
@@ -1,19 +0,0 @@
|
||||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: MrHua269 <wangxyper@163.com>
|
||||
Date: Sun, 12 Jan 2025 10:36:31 +0800
|
||||
Subject: [PATCH] Add config for command block command execution
|
||||
|
||||
|
||||
diff --git a/net/minecraft/world/level/BaseCommandBlock.java b/net/minecraft/world/level/BaseCommandBlock.java
|
||||
index 91e30f9c83259abc0589f4ee69c429cd4305d6ea..8155da15908fd944d452fd6aaff69cfdfcc7b36b 100644
|
||||
--- a/net/minecraft/world/level/BaseCommandBlock.java
|
||||
+++ b/net/minecraft/world/level/BaseCommandBlock.java
|
||||
@@ -102,7 +102,7 @@ public abstract class BaseCommandBlock implements CommandSource {
|
||||
}
|
||||
|
||||
public boolean performCommand(Level level) {
|
||||
- if (true) return false; // Folia - region threading
|
||||
+ if (!me.earthme.lophine.config.modules.experiment.CommandConfig.block) return false; // Folia - region threading // Luminol
|
||||
if (level.isClientSide || level.getGameTime() == this.lastExecution) {
|
||||
return false;
|
||||
} else if ("Searge".equalsIgnoreCase(this.command)) {
|
||||
+105
@@ -0,0 +1,105 @@
|
||||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: Helvetica Volubi <suisuroru@blue-millennium.fun>
|
||||
Date: Tue, 29 Apr 2025 23:03:56 +0800
|
||||
Subject: [PATCH] Add config to enable cross region damage trace
|
||||
|
||||
|
||||
diff --git a/net/minecraft/server/level/ServerPlayer.java b/net/minecraft/server/level/ServerPlayer.java
|
||||
index 11e6197dec541b28733715f0d7eaa4c7b834d632..6b7c92fdf562dfa46eee8e8acbc82b02fc3226c5 100644
|
||||
--- a/net/minecraft/server/level/ServerPlayer.java
|
||||
+++ b/net/minecraft/server/level/ServerPlayer.java
|
||||
@@ -1345,6 +1345,13 @@ public class ServerPlayer extends Player implements ca.spottedleaf.moonrise.patc
|
||||
this.awardStat(Stats.ENTITY_KILLED_BY.get(killCredit.getType()));
|
||||
killCredit.awardKillScore(this, cause);
|
||||
this.createWitherRose(killCredit);
|
||||
+ // Luminol Start - Cross Region Damage trace
|
||||
+ } else if (fun.bm.lophine.config.modules.experiment.EntityDamageSourceTraceConfig.enabled) {
|
||||
+ final LivingEntity entitylivingnew = this.getKillCreditOrigin();
|
||||
+ if (entitylivingnew != null) {
|
||||
+ this.damageTransferToAsync(entitylivingnew, cause);
|
||||
+ }
|
||||
+ // Luminol End - Cross Region Damage trace
|
||||
}
|
||||
|
||||
this.level().broadcastEntityEvent(this, (byte)3);
|
||||
@@ -1368,6 +1375,24 @@ public class ServerPlayer extends Player implements ca.spottedleaf.moonrise.patc
|
||||
}
|
||||
// Leaves end - exp fix
|
||||
|
||||
+ // Luminol Start - Cross Region Damage trace
|
||||
+ private void damageTransferToAsync(LivingEntity entity, DamageSource cause) {
|
||||
+ // Operations running on current entity
|
||||
+ this.awardStat(Stats.ENTITY_KILLED_BY.get(entity.getType()));
|
||||
+ this.createWitherRose(entity);
|
||||
+
|
||||
+ // the entity might be in another tickregion sometimes, so we need to schedule the task onto the entity
|
||||
+ // to ensure thread safe
|
||||
+ entity.getBukkitEntity().taskScheduler.schedule((LivingEntity nmsEntity) -> {
|
||||
+ try {
|
||||
+ nmsEntity.awardKillScore(this, cause);
|
||||
+ } catch (Throwable ex) {
|
||||
+ LOGGER.error(ex.getMessage(), ex);
|
||||
+ }
|
||||
+ }, null, 1L );
|
||||
+ }
|
||||
+ // Luminol End - Cross Region Damage trace
|
||||
+
|
||||
private void tellNeutralMobsThatIDied() {
|
||||
AABB aabb = new AABB(this.blockPosition()).inflate(32.0, 10.0, 32.0);
|
||||
this.level()
|
||||
diff --git a/net/minecraft/world/entity/LivingEntity.java b/net/minecraft/world/entity/LivingEntity.java
|
||||
index 567d63ae589705e13403814b36e35d00c241a69e..3f4b1c54f9207ba1854a8c8da965cbaca5ea517d 100644
|
||||
--- a/net/minecraft/world/entity/LivingEntity.java
|
||||
+++ b/net/minecraft/world/entity/LivingEntity.java
|
||||
@@ -1948,6 +1948,13 @@ public abstract class LivingEntity extends Entity implements Attackable, Waypoin
|
||||
final LivingEntity killer = this.getKillCredit();
|
||||
if (killer != null) {
|
||||
killer.awardKillScore(this, damageSource);
|
||||
+ // Luminol Start - Cross Region Damage trace
|
||||
+ } else if (fun.bm.lophine.config.modules.experiment.EntityDamageSourceTraceConfig.enabled) {
|
||||
+ final LivingEntity killernew = this.getKillCreditOrigin();
|
||||
+ if (killernew != null) {
|
||||
+ this.damageTransferToAsync(killernew, damageSource);
|
||||
+ }
|
||||
+ // Luminol End - Cross Region Damage trace
|
||||
}
|
||||
}); // Paper end
|
||||
this.postDeathDropItems(deathEvent); // Paper
|
||||
@@ -1958,6 +1965,18 @@ public abstract class LivingEntity extends Entity implements Attackable, Waypoin
|
||||
return deathEvent; // Paper
|
||||
}
|
||||
|
||||
+ // Luminol Start - Cross Region Damage trace
|
||||
+ private void damageTransferToAsync(LivingEntity entity, DamageSource damageSource) {
|
||||
+ entity.getBukkitEntity().taskScheduler.schedule((LivingEntity nmsEntity) -> {
|
||||
+ try {
|
||||
+ nmsEntity.awardKillScore(this, damageSource);
|
||||
+ } catch (Throwable ex) {
|
||||
+ LOGGER.error(ex.getMessage(), ex);
|
||||
+ }
|
||||
+ }, null, 1L );
|
||||
+ }
|
||||
+ // Luminol End - Cross Region Damage trace
|
||||
+
|
||||
protected void dropEquipment(ServerLevel level) {
|
||||
}
|
||||
protected void postDeathDropItems(org.bukkit.event.entity.EntityDeathEvent event) {} // Paper - method for post death logic that cannot be ran before the event is potentially cancelled
|
||||
@@ -2556,6 +2575,18 @@ public abstract class LivingEntity extends Entity implements Attackable, Waypoin
|
||||
}
|
||||
}
|
||||
|
||||
+ // Luminol Start - Cross Region Damage trace
|
||||
+ @Nullable
|
||||
+ public LivingEntity getKillCreditOrigin() {
|
||||
+ if (this.lastHurtByPlayer != null) {
|
||||
+ return this.lastHurtByPlayer.getEntity(this.level(), Player.class);
|
||||
+ } else if (this.lastHurtByMob != null) {
|
||||
+ return this.lastHurtByMob.getEntity(this.level(), LivingEntity.class);
|
||||
+ }
|
||||
+ return null;
|
||||
+ }
|
||||
+ // Luminol End - Cross Region Damage trace
|
||||
+
|
||||
public final float getMaxHealth() {
|
||||
return (float)this.getAttributeValue(Attributes.MAX_HEALTH);
|
||||
}
|
||||
-27
@@ -1,27 +0,0 @@
|
||||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: Helvetica Volubi <suisuroru@blue-millennium.fun>
|
||||
Date: Wed, 21 May 2025 13:04:00 +0800
|
||||
Subject: [PATCH] Add config to disable end crystal check
|
||||
|
||||
|
||||
diff --git a/net/minecraft/world/level/dimension/end/EndDragonFight.java b/net/minecraft/world/level/dimension/end/EndDragonFight.java
|
||||
index 4fa8371c212dcc02b8cf5fd267b736e1cf3f50c1..d62a9e93dae7b4a7a079e73e80e68e6a81b4c437 100644
|
||||
--- a/net/minecraft/world/level/dimension/end/EndDragonFight.java
|
||||
+++ b/net/minecraft/world/level/dimension/end/EndDragonFight.java
|
||||
@@ -547,6 +547,8 @@ public class EndDragonFight {
|
||||
|
||||
blockPos = this.portalLocation;
|
||||
}
|
||||
+ // Luminol start - Disable end crystal check
|
||||
+ if (!me.earthme.lophine.config.modules.misc.DisableEndCrystalCheckConfig.disableCheck) {
|
||||
// Paper start - Perf: Do crystal-portal proximity check before entity lookup
|
||||
if (placedEndCrystalPos != null) {
|
||||
// The end crystal must be 0 or 1 higher than the portal origin
|
||||
@@ -562,6 +564,7 @@ public class EndDragonFight {
|
||||
}
|
||||
}
|
||||
// Paper end - Perf: Do crystal-portal proximity check before entity lookup
|
||||
+ } // Luminol end - Disable end crystal check
|
||||
|
||||
|
||||
List<EndCrystal> list = Lists.newArrayList();
|
||||
+59
@@ -0,0 +1,59 @@
|
||||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: Helvetica Volubi <suisuroru@blue-millennium.fun>
|
||||
Date: Wed, 2 Jul 2025 23:36:25 +0800
|
||||
Subject: [PATCH] Add config to enable datapack command
|
||||
|
||||
|
||||
diff --git a/net/minecraft/commands/Commands.java b/net/minecraft/commands/Commands.java
|
||||
index 8a90aa5c145f53afbb3f2371f97cb09974f30bbb..b6e10157c572dadb1bff18f8431166966eed46d9 100644
|
||||
--- a/net/minecraft/commands/Commands.java
|
||||
+++ b/net/minecraft/commands/Commands.java
|
||||
@@ -192,7 +192,9 @@ public class Commands {
|
||||
if(me.earthme.luminol.config.modules.experiment.CommandDataConfig.enabled) {
|
||||
DataCommands.register(this.dispatcher); // Folia - region threading - TODO
|
||||
}
|
||||
- //DataPackCommand.register(this.dispatcher, context); // Folia - region threading - TODO
|
||||
+ if (fun.bm.lophine.config.modules.experiment.CommandConfig.datapack) {
|
||||
+ DataPackCommand.register(this.dispatcher, context); // Folia - region threading - TODO
|
||||
+ }
|
||||
//DebugCommand.register(this.dispatcher); // Folia - region threading - TODO
|
||||
DefaultGameModeCommands.register(this.dispatcher);
|
||||
//DialogCommand.register(this.dispatcher, context); // Folia - region threading - TODO
|
||||
diff --git a/net/minecraft/server/MinecraftServer.java b/net/minecraft/server/MinecraftServer.java
|
||||
index 100e8fdc52c201d386e3c779d19f6b5abde39820..d08a5b553f4e5932d960fbcdeb6a43419730e32a 100644
|
||||
--- a/net/minecraft/server/MinecraftServer.java
|
||||
+++ b/net/minecraft/server/MinecraftServer.java
|
||||
@@ -2352,6 +2352,16 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
|
||||
return this.reloadResources(selectedIds, io.papermc.paper.event.server.ServerResourcesReloadedEvent.Cause.PLUGIN);
|
||||
}
|
||||
|
||||
+ // Lophine start - region thread skip reload // TODO - add reload support
|
||||
+ public void saveDatapacks(Collection<String> selectedIds) {
|
||||
+ this.packRepository.setSelected(selectedIds, false); // Paper - add pendingReload flag to determine required pack loading - false as this is *after* a reload (see above)
|
||||
+ WorldDataConfiguration worldDataConfiguration = new WorldDataConfiguration(
|
||||
+ getSelectedPacks(this.packRepository, true), this.worldData.enabledFeatures()
|
||||
+ );
|
||||
+ this.worldData.setDataConfiguration(worldDataConfiguration);
|
||||
+ }
|
||||
+ // Lophine end - region thread skip reload // TODO - add reload support
|
||||
+
|
||||
public CompletableFuture<Void> reloadResources(Collection<String> selectedIds, io.papermc.paper.event.server.ServerResourcesReloadedEvent.Cause cause) {
|
||||
// Paper end - Add ServerResourcesReloadedEvent
|
||||
CompletableFuture<Void> completableFuture = CompletableFuture.<ImmutableList>supplyAsync(
|
||||
diff --git a/net/minecraft/server/commands/ReloadCommand.java b/net/minecraft/server/commands/ReloadCommand.java
|
||||
index b540a3c3a02f7242578a63477e34b9063e86f1d2..9f0adf157872e1c68cd7752a24eacdc636e8d17a 100644
|
||||
--- a/net/minecraft/server/commands/ReloadCommand.java
|
||||
+++ b/net/minecraft/server/commands/ReloadCommand.java
|
||||
@@ -16,6 +16,12 @@ public class ReloadCommand {
|
||||
private static final Logger LOGGER = LogUtils.getLogger();
|
||||
|
||||
public static void reloadPacks(Collection<String> selectedIds, CommandSourceStack source) {
|
||||
+ // Lophine start - region thread skip reload // TODO - add reload support
|
||||
+ if (true) {
|
||||
+ source.getServer().saveDatapacks(selectedIds);
|
||||
+ return;
|
||||
+ }
|
||||
+ // Lophine end - region thread skip reload // TODO - add reload support
|
||||
source.getServer().reloadResources(selectedIds, io.papermc.paper.event.server.ServerResourcesReloadedEvent.Cause.COMMAND).exceptionally(throwable -> { // Paper - Add ServerResourcesReloadedEvent
|
||||
LOGGER.warn("Failed to execute reload", throwable);
|
||||
source.sendFailure(Component.translatable("commands.reload.failure"));
|
||||
+24
-24
@@ -1,11 +1,11 @@
|
||||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: MrHua269 <wangxyper@163.com>
|
||||
Date: Wed, 5 Feb 2025 15:22:19 +0800
|
||||
Subject: [PATCH] Add config to enable Raytracing tracker
|
||||
Subject: [PATCH] Add config to enable raytracing tracker
|
||||
|
||||
|
||||
diff --git a/net/minecraft/server/level/ChunkMap.java b/net/minecraft/server/level/ChunkMap.java
|
||||
index ee53f78396f4377d3e5c2998826f231208066ef6..607091ecbd4a7261f2c0d839ee4dd1d27fa2190a 100644
|
||||
index 6868b915bf3deb85783a638d4441a15fea6da2dc..70740381c6501c1a518c52b24381edd16792507f 100644
|
||||
--- a/net/minecraft/server/level/ChunkMap.java
|
||||
+++ b/net/minecraft/server/level/ChunkMap.java
|
||||
@@ -1278,7 +1278,7 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
|
||||
@@ -18,19 +18,19 @@ index ee53f78396f4377d3e5c2998826f231208066ef6..607091ecbd4a7261f2c0d839ee4dd1d2
|
||||
double rangeY = level.paperConfig().entities.trackingRangeY.get(this.entity, -1);
|
||||
if (rangeY != -1) {
|
||||
diff --git a/net/minecraft/world/entity/Entity.java b/net/minecraft/world/entity/Entity.java
|
||||
index a023cc399fc8bfee7771e5ca6716578f89b7072f..f487b44174678196e2bade0066511daf58e93876 100644
|
||||
index 125412d16d1a66dadfe4bbf64629c4052e0c7647..ccb05b6e328a29eea4761721b496ab36ba362361 100644
|
||||
--- a/net/minecraft/world/entity/Entity.java
|
||||
+++ b/net/minecraft/world/entity/Entity.java
|
||||
@@ -140,7 +140,7 @@ import net.minecraft.world.scores.ScoreHolder;
|
||||
import net.minecraft.world.scores.Team;
|
||||
@@ -147,7 +147,7 @@ import net.minecraft.world.waypoints.WaypointTransmitter;
|
||||
import org.jetbrains.annotations.Contract;
|
||||
import org.slf4j.Logger;
|
||||
|
||||
-public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess, ScoreHolder, DataComponentGetter, ca.spottedleaf.moonrise.patches.chunk_system.entity.ChunkSystemEntity, ca.spottedleaf.moonrise.patches.entity_tracker.EntityTrackerEntity { // Paper - rewrite chunk system // Paper - optimise entity tracker
|
||||
+public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess, ScoreHolder, DataComponentGetter, ca.spottedleaf.moonrise.patches.chunk_system.entity.ChunkSystemEntity, ca.spottedleaf.moonrise.patches.entity_tracker.EntityTrackerEntity, dev.tr7zw.entityculling.versionless.access.Cullable { // Paper - rewrite chunk system // Paper - optimise entity tracker // Luminol - Ray tracing entity tracker
|
||||
// CraftBukkit start
|
||||
private static final org.slf4j.Logger LOGGER = com.mojang.logging.LogUtils.getLogger();
|
||||
private static final int CURRENT_LEVEL = 2;
|
||||
@@ -6106,4 +6106,46 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess
|
||||
public boolean preserveMotion = true; // Paper - Fix Entity Teleportation and cancel velocity if teleported; keep initial motion on first snapTo
|
||||
@@ -6472,4 +6472,46 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess
|
||||
// Paper end - Expose entity id counter
|
||||
|
||||
public boolean shouldTickHot() { return this.tickCount > 20 * 10 && this.isAlive(); } // KioCG
|
||||
@@ -59,7 +59,7 @@ index a023cc399fc8bfee7771e5ca6716578f89b7072f..f487b44174678196e2bade0066511daf
|
||||
+
|
||||
+ @Override
|
||||
+ public boolean isCulled() {
|
||||
+ if (!me.earthme.lophine.config.modules.experiment.RayTrackingEntityTrackerConfig.enabled)
|
||||
+ if (!fun.bm.lophine.config.modules.experiment.RayTrackingEntityTrackerConfig.enabled)
|
||||
+ return false;
|
||||
+ return this.culled;
|
||||
+ }
|
||||
@@ -71,17 +71,17 @@ index a023cc399fc8bfee7771e5ca6716578f89b7072f..f487b44174678196e2bade0066511daf
|
||||
+
|
||||
+ @Override
|
||||
+ public boolean isOutOfCamera() {
|
||||
+ if (!me.earthme.lophine.config.modules.experiment.RayTrackingEntityTrackerConfig.enabled)
|
||||
+ if (!fun.bm.lophine.config.modules.experiment.RayTrackingEntityTrackerConfig.enabled)
|
||||
+ return false;
|
||||
+ return this.outOfCamera;
|
||||
+ }
|
||||
+
|
||||
}
|
||||
diff --git a/net/minecraft/world/entity/EntityType.java b/net/minecraft/world/entity/EntityType.java
|
||||
index 6b72ab233508e6df1eca34360ce76d102ee25a41..f39ee4605cc15102d6560afd1dad5f56dd53cf4e 100644
|
||||
index 6f00ffa05d9597917574357e0069c9b056aa5ce2..5cc7a91e32f17d8d9585ae4844a4ed3b706e3273 100644
|
||||
--- a/net/minecraft/world/entity/EntityType.java
|
||||
+++ b/net/minecraft/world/entity/EntityType.java
|
||||
@@ -1109,6 +1109,9 @@ public class EntityType<T extends Entity> implements FeatureElement, EntityTypeT
|
||||
@@ -1119,6 +1119,9 @@ public class EntityType<T extends Entity> implements FeatureElement, EntityTypeT
|
||||
public final int passengerTickTimerId;
|
||||
public final int passengerInactiveTickTimerId;
|
||||
// Folia end - profiler
|
||||
@@ -92,53 +92,53 @@ index 6b72ab233508e6df1eca34360ce76d102ee25a41..f39ee4605cc15102d6560afd1dad5f56
|
||||
public EntityType(
|
||||
EntityType.EntityFactory<T> factory,
|
||||
diff --git a/net/minecraft/world/entity/player/Player.java b/net/minecraft/world/entity/player/Player.java
|
||||
index 543750bee43bfaaaf33f2cec53eb0de05e7a1dd2..34c4c195519de03ec11897160b98c80353eb3d6c 100644
|
||||
index 2842314c712449625e9275aff7707c916c3ea767..09b549fa9568aa1ae13b1b975db14a6268fdaa46 100644
|
||||
--- a/net/minecraft/world/entity/player/Player.java
|
||||
+++ b/net/minecraft/world/entity/player/Player.java
|
||||
@@ -220,6 +220,25 @@ public abstract class Player extends LivingEntity {
|
||||
@@ -229,6 +229,25 @@ public abstract class Player extends LivingEntity {
|
||||
return (org.bukkit.craftbukkit.entity.CraftHumanEntity) super.getBukkitEntity();
|
||||
}
|
||||
// CraftBukkit end
|
||||
+ // Luminol start - Raytracing entity tracker
|
||||
+ public dev.tr7zw.entityculling.CullTask cullTask;
|
||||
+ {
|
||||
+ if (!me.earthme.lophine.config.modules.experiment.RayTrackingEntityTrackerConfig.enabled) {
|
||||
+ if (!fun.bm.lophine.config.modules.experiment.RayTrackingEntityTrackerConfig.enabled) {
|
||||
+ this.cullTask = null;
|
||||
+ }else {
|
||||
+ final com.logisticscraft.occlusionculling.OcclusionCullingInstance culling = new com.logisticscraft.occlusionculling.OcclusionCullingInstance(
|
||||
+ me.earthme.lophine.config.modules.experiment.RayTrackingEntityTrackerConfig.tracingDistance,
|
||||
+ fun.bm.lophine.config.modules.experiment.RayTrackingEntityTrackerConfig.tracingDistance,
|
||||
+ new dev.tr7zw.entityculling.DefaultChunkDataProvider(this.level())
|
||||
+ );
|
||||
+
|
||||
+ this.cullTask = new dev.tr7zw.entityculling.CullTask(
|
||||
+ culling, this,
|
||||
+ me.earthme.lophine.config.modules.experiment.RayTrackingEntityTrackerConfig.hitboxLimit,
|
||||
+ me.earthme.lophine.config.modules.experiment.RayTrackingEntityTrackerConfig.checkIntervalMs
|
||||
+ fun.bm.lophine.config.modules.experiment.RayTrackingEntityTrackerConfig.hitboxLimit,
|
||||
+ fun.bm.lophine.config.modules.experiment.RayTrackingEntityTrackerConfig.checkIntervalMs
|
||||
+ );
|
||||
+ }
|
||||
+ }
|
||||
+ // Luminol end
|
||||
|
||||
public Player(Level level, BlockPos pos, float yRot, GameProfile gameProfile) {
|
||||
public Player(Level level, GameProfile gameProfile) {
|
||||
super(EntityType.PLAYER, level);
|
||||
@@ -277,6 +296,26 @@ public abstract class Player extends LivingEntity {
|
||||
@@ -287,6 +306,26 @@ public abstract class Player extends LivingEntity {
|
||||
|
||||
@Override
|
||||
public void tick() {
|
||||
+ // Luminol start - Ray tracing entity tracker
|
||||
+ if (!me.earthme.lophine.config.modules.experiment.RayTrackingEntityTrackerConfig.enabled) {
|
||||
+ if (!fun.bm.lophine.config.modules.experiment.RayTrackingEntityTrackerConfig.enabled) {
|
||||
+ if (this.cullTask != null) this.cullTask.signalStop();
|
||||
+ this.cullTask = null;
|
||||
+ }else {
|
||||
+ final com.logisticscraft.occlusionculling.OcclusionCullingInstance culling = new com.logisticscraft.occlusionculling.OcclusionCullingInstance(
|
||||
+ me.earthme.lophine.config.modules.experiment.RayTrackingEntityTrackerConfig.tracingDistance,
|
||||
+ fun.bm.lophine.config.modules.experiment.RayTrackingEntityTrackerConfig.tracingDistance,
|
||||
+ new dev.tr7zw.entityculling.DefaultChunkDataProvider(this.level())
|
||||
+ );
|
||||
+
|
||||
+ this.cullTask = new dev.tr7zw.entityculling.CullTask(
|
||||
+ culling, this,
|
||||
+ me.earthme.lophine.config.modules.experiment.RayTrackingEntityTrackerConfig.hitboxLimit,
|
||||
+ me.earthme.lophine.config.modules.experiment.RayTrackingEntityTrackerConfig.checkIntervalMs
|
||||
+ fun.bm.lophine.config.modules.experiment.RayTrackingEntityTrackerConfig.hitboxLimit,
|
||||
+ fun.bm.lophine.config.modules.experiment.RayTrackingEntityTrackerConfig.checkIntervalMs
|
||||
+ );
|
||||
+ }
|
||||
+ if (this.cullTask != null) this.cullTask.setup();
|
||||
@@ -148,7 +148,7 @@ index 543750bee43bfaaaf33f2cec53eb0de05e7a1dd2..34c4c195519de03ec11897160b98c803
|
||||
this.noPhysics = this.isSpectator();
|
||||
if (this.isSpectator() || this.isPassenger()) {
|
||||
this.setOnGround(false);
|
||||
@@ -1415,6 +1454,7 @@ public abstract class Player extends LivingEntity {
|
||||
@@ -1426,6 +1465,7 @@ public abstract class Player extends LivingEntity {
|
||||
if (this.containerMenu != null && this.hasContainerOpen()) {
|
||||
this.doCloseContainer();
|
||||
}
|
||||
+19
-19
@@ -5,7 +5,7 @@ Subject: [PATCH] Add config to enable tick command
|
||||
|
||||
|
||||
diff --git a/io/papermc/paper/threadedregions/RegionizedServer.java b/io/papermc/paper/threadedregions/RegionizedServer.java
|
||||
index 8e91ec81128bdbd5f78e1f04fe17bcbd6e5dc280..6b4a33189b4dcd4979fb421248f72530c547ac19 100644
|
||||
index e1842ab8f20271a8b20ab8462b09a4745a612a9a..8405157ec35a443e238fa6866772c839621d73d7 100644
|
||||
--- a/io/papermc/paper/threadedregions/RegionizedServer.java
|
||||
+++ b/io/papermc/paper/threadedregions/RegionizedServer.java
|
||||
@@ -299,6 +299,11 @@ public final class RegionizedServer {
|
||||
@@ -13,19 +13,19 @@ index 8e91ec81128bdbd5f78e1f04fe17bcbd6e5dc280..6b4a33189b4dcd4979fb421248f72530
|
||||
*/
|
||||
++this.tickCount;
|
||||
+ // Luminol start - Add a config to enable tick command
|
||||
+ if (me.earthme.lophine.config.modules.experiment.CommandConfig.tick) {
|
||||
+ if (fun.bm.lophine.config.modules.experiment.CommandConfig.tick) {
|
||||
+ MinecraftServer.tickRateManager.tick();
|
||||
+ }
|
||||
+ // Luminol end - Add a config to enable tick command
|
||||
// expire invalid click command callbacks
|
||||
io.papermc.paper.adventure.providers.ClickCallbackProviderImpl.CALLBACK_MANAGER.handleQueue((int)this.tickCount);
|
||||
|
||||
@@ -321,6 +326,13 @@ public final class RegionizedServer {
|
||||
io.papermc.paper.adventure.providers.ClickCallbackProviderImpl.ADVENTURE_CLICK_MANAGER.handleQueue((int)this.tickCount); // Paper // Folia - region threading - moved to global tick
|
||||
io.papermc.paper.adventure.providers.ClickCallbackProviderImpl.DIALOG_CLICK_MANAGER.handleQueue((int)this.tickCount); // Paper // Folia - region threading - moved to global tick
|
||||
@@ -322,6 +327,13 @@ public final class RegionizedServer {
|
||||
this.globalTick(world, tickCount);
|
||||
}
|
||||
|
||||
+ // Luminol start - Add a config to enable tick command
|
||||
+ if (me.earthme.lophine.config.modules.experiment.CommandConfig.tick) {
|
||||
+ if (fun.bm.lophine.config.modules.experiment.CommandConfig.tick) {
|
||||
+ MinecraftServer.tickRateManager.reduceSprintTicks();
|
||||
+ MinecraftServer.tickRateManager.endTickWork();
|
||||
+ }
|
||||
@@ -34,17 +34,17 @@ index 8e91ec81128bdbd5f78e1f04fe17bcbd6e5dc280..6b4a33189b4dcd4979fb421248f72530
|
||||
// tick connections
|
||||
this.tickConnections();
|
||||
|
||||
@@ -454,7 +466,7 @@ public final class RegionizedServer {
|
||||
@@ -455,7 +467,7 @@ public final class RegionizedServer {
|
||||
}
|
||||
|
||||
private void tickTime(final ServerLevel world, final int tickCount) {
|
||||
- if (world.tickTime) {
|
||||
+ if ((!me.earthme.lophine.config.modules.experiment.CommandConfig.tick || world.tickRateManager().runsNormally()) && world.tickTime) { // Luminol - Add a config to enable tick command
|
||||
+ if ((!fun.bm.lophine.config.modules.experiment.CommandConfig.tick || world.tickRateManager().runsNormally()) && world.tickTime) { // Luminol - Add a config to enable tick command
|
||||
if (world.getGameRules().getBoolean(GameRules.RULE_DAYLIGHT)) {
|
||||
world.setDayTime(world.levelData.getDayTime() + (long)tickCount);
|
||||
}
|
||||
diff --git a/io/papermc/paper/threadedregions/TickRegionScheduler.java b/io/papermc/paper/threadedregions/TickRegionScheduler.java
|
||||
index fa6b8d756195c1b430cc11214a901bd42eebc98d..1d79465c9b04c5cdfd299278786916cb10e49522 100644
|
||||
index 0357792de0ed8ec9058d1847c8b45c33ff365af6..4945c11f573a53df2f5be4beb7af6a3f4d32f7d4 100644
|
||||
--- a/io/papermc/paper/threadedregions/TickRegionScheduler.java
|
||||
+++ b/io/papermc/paper/threadedregions/TickRegionScheduler.java
|
||||
@@ -31,8 +31,8 @@ public final class TickRegionScheduler {
|
||||
@@ -58,7 +58,7 @@ index fa6b8d756195c1b430cc11214a901bd42eebc98d..1d79465c9b04c5cdfd299278786916cb
|
||||
|
||||
// Folia start - watchdog
|
||||
public static final FoliaWatchdogThread WATCHDOG_THREAD = new FoliaWatchdogThread();
|
||||
@@ -375,8 +375,23 @@ public final class TickRegionScheduler {
|
||||
@@ -394,8 +394,23 @@ public final class TickRegionScheduler {
|
||||
final long cpuStart = MEASURE_CPU_TIME ? THREAD_MX_BEAN.getCurrentThreadCpuTime() : 0L;
|
||||
final long tickStart = System.nanoTime();
|
||||
|
||||
@@ -66,7 +66,7 @@ index fa6b8d756195c1b430cc11214a901bd42eebc98d..1d79465c9b04c5cdfd299278786916cb
|
||||
- final int tickCount = Math.max(1, this.tickSchedule.getPeriodsAhead(TIME_BETWEEN_TICKS, tickStart));
|
||||
+ // Luminol start - Add a config to enable tick command
|
||||
+ final int tickCount;
|
||||
+ if (me.earthme.lophine.config.modules.experiment.CommandConfig.tick) {
|
||||
+ if (fun.bm.lophine.config.modules.experiment.CommandConfig.tick) {
|
||||
+ if (MinecraftServer.tickRateManager.isSprinting() && MinecraftServer.tickRateManager.checkShouldSprintThisTick()) {
|
||||
+ TICK_RATE = net.minecraft.server.commands.TickCommand.MAX_TICKRATE;
|
||||
+ TIME_BETWEEN_TICKS = (long) (1_000_000_000L / TICK_RATE);
|
||||
@@ -84,12 +84,12 @@ index fa6b8d756195c1b430cc11214a901bd42eebc98d..1d79465c9b04c5cdfd299278786916cb
|
||||
|
||||
if (!this.tryMarkTicking()) {
|
||||
if (!this.cancelled.get()) {
|
||||
@@ -416,6 +431,11 @@ public final class TickRegionScheduler {
|
||||
@@ -435,6 +450,11 @@ public final class TickRegionScheduler {
|
||||
try {
|
||||
// next start isn't updated until the end of this tick
|
||||
this.tickRegion(tickCount, tickStart, scheduledEnd);
|
||||
+ // Luminol start - Add a config to enable tick command
|
||||
+ if (me.earthme.lophine.config.modules.experiment.CommandConfig.tick) {
|
||||
+ if (fun.bm.lophine.config.modules.experiment.CommandConfig.tick) {
|
||||
+ MinecraftServer.tickRateManager.endTickWork();
|
||||
+ }
|
||||
+ // Luminol end - Add a config to enable tick command
|
||||
@@ -97,16 +97,16 @@ index fa6b8d756195c1b430cc11214a901bd42eebc98d..1d79465c9b04c5cdfd299278786916cb
|
||||
this.scheduler.regionFailed(this, false, thr);
|
||||
// regionFailed will schedule a shutdown, so we should avoid letting this region tick further
|
||||
diff --git a/net/minecraft/commands/Commands.java b/net/minecraft/commands/Commands.java
|
||||
index ea50e3c0c4044f03dc8257680814f808fbf3a177..bd5d8bbb667585c1bf976e2eaa38f2b72eabe9e2 100644
|
||||
index b6e10157c572dadb1bff18f8431166966eed46d9..e2fc15d84fafde6847ecead23c55169449275e1b 100644
|
||||
--- a/net/minecraft/commands/Commands.java
|
||||
+++ b/net/minecraft/commands/Commands.java
|
||||
@@ -214,7 +214,11 @@ public class Commands {
|
||||
@@ -245,7 +245,11 @@ public class Commands {
|
||||
TeleportCommand.register(this.dispatcher);
|
||||
TellRawCommand.register(this.dispatcher, context);
|
||||
//TestCommand.register(this.dispatcher, context); // Folia - region threading
|
||||
- //TickCommand.register(this.dispatcher); // Folia - region threading - TODO later
|
||||
+ // Luminol start - Add a config to enable tick command
|
||||
+ if (me.earthme.lophine.config.modules.experiment.CommandConfig.tick) {
|
||||
+ if (fun.bm.lophine.config.modules.experiment.CommandConfig.tick) {
|
||||
+ TickCommand.register(this.dispatcher); // Folia - region threading - TODO later
|
||||
+ }
|
||||
+ // Luminol end - Add a config to enable tick command
|
||||
@@ -114,10 +114,10 @@ index ea50e3c0c4044f03dc8257680814f808fbf3a177..bd5d8bbb667585c1bf976e2eaa38f2b7
|
||||
TitleCommand.register(this.dispatcher, context);
|
||||
//TriggerCommand.register(this.dispatcher); // Folia - region threading - TODO later
|
||||
diff --git a/net/minecraft/server/MinecraftServer.java b/net/minecraft/server/MinecraftServer.java
|
||||
index 33d596c0f07d076886c0dd8eb93e901cdb56e438..87625b365e04ce05fa2b6cdf2003c839255c4393 100644
|
||||
index d08a5b553f4e5932d960fbcdeb6a43419730e32a..a0f1db50adf5163a671d3d9e9df7adb7de4250c6 100644
|
||||
--- a/net/minecraft/server/MinecraftServer.java
|
||||
+++ b/net/minecraft/server/MinecraftServer.java
|
||||
@@ -266,7 +266,7 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
|
||||
@@ -267,7 +267,7 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
|
||||
private String serverId;
|
||||
public MinecraftServer.ReloadableResources resources;
|
||||
private final StructureTemplateManager structureTemplateManager;
|
||||
@@ -153,7 +153,7 @@ index 40338efd1c0e56d869d03f1d0687e7ff0fcbf11a..d0d90a25a10bbecfffceee1992af88c6
|
||||
this.sprintTimeSpend = this.sprintTimeSpend + (System.nanoTime() - this.sprintTickStartTime);
|
||||
}
|
||||
diff --git a/net/minecraft/server/commands/TickCommand.java b/net/minecraft/server/commands/TickCommand.java
|
||||
index 6b6c8ce49eda6806c8288d70848dd143ba2c4703..5d09d2c8bb45cc10b2a13100793249adc7b5a7e9 100644
|
||||
index 126685fb266692082b432ed297a499149e921dfb..9d8f6025c420e651611da17e77a362c738228b72 100644
|
||||
--- a/net/minecraft/server/commands/TickCommand.java
|
||||
+++ b/net/minecraft/server/commands/TickCommand.java
|
||||
@@ -14,7 +14,7 @@ import net.minecraft.server.ServerTickRateManager;
|
||||
-204
@@ -1,204 +0,0 @@
|
||||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: Helvetica Volubi <suisuroru@blue-millennium.fun>
|
||||
Date: Fri, 11 Apr 2025 16:53:57 +0800
|
||||
Subject: [PATCH] Add config to revert raid changes
|
||||
|
||||
|
||||
diff --git a/net/minecraft/world/effect/BadOmenMobEffect.java b/net/minecraft/world/effect/BadOmenMobEffect.java
|
||||
index 80f17f33f670018240c854df589cf90cdeab6e70..faf31ef66ab7fcdb4135e4c225312dc353bdb99b 100644
|
||||
--- a/net/minecraft/world/effect/BadOmenMobEffect.java
|
||||
+++ b/net/minecraft/world/effect/BadOmenMobEffect.java
|
||||
@@ -22,6 +22,11 @@ class BadOmenMobEffect extends MobEffect {
|
||||
&& !serverPlayer.isSpectator()
|
||||
&& level.getDifficulty() != Difficulty.PEACEFUL
|
||||
&& level.isVillage(serverPlayer.blockPosition())) {
|
||||
+ // Leaves start - Revert raid changes
|
||||
+ if (me.earthme.lophine.config.modules.misc.RaidChangesConfig.trigger) {
|
||||
+ return level.getRaids().createOrExtendRaid(serverPlayer, serverPlayer.blockPosition()) == null;
|
||||
+ }
|
||||
+ // Leaves end - Revert raid changes
|
||||
Raid raidAt = level.getRaidAt(serverPlayer.blockPosition());
|
||||
if (raidAt == null || raidAt.getRaidOmenLevel() < raidAt.getMaxRaidOmenLevel()) {
|
||||
serverPlayer.addEffect(new MobEffectInstance(MobEffects.RAID_OMEN, 600, amplifier));
|
||||
diff --git a/net/minecraft/world/entity/raid/Raid.java b/net/minecraft/world/entity/raid/Raid.java
|
||||
index dbb207c638a64b733dc21704033ff55ca1f44f1d..94fdf1b5ce2622e4e654d09562f8a3e032e6add8 100644
|
||||
--- a/net/minecraft/world/entity/raid/Raid.java
|
||||
+++ b/net/minecraft/world/entity/raid/Raid.java
|
||||
@@ -340,7 +340,20 @@ public class Raid {
|
||||
}
|
||||
|
||||
if (flag1) {
|
||||
- this.waveSpawnPos = this.getValidSpawnPos(level);
|
||||
+ // Luminol Start - Raid revert
|
||||
+ if (!me.earthme.lophine.config.modules.misc.RaidChangesConfig.posRevert) {
|
||||
+ this.waveSpawnPos = this.getValidSpawnPos(level);
|
||||
+ } else {
|
||||
+ int n4 = 0;
|
||||
+ if (this.raidCooldownTicks < 100) {
|
||||
+ n4 = 1;
|
||||
+ }
|
||||
+ if (this.raidCooldownTicks < 40) {
|
||||
+ n4 = 2;
|
||||
+ }
|
||||
+ this.waveSpawnPos = this.getValidSpawnPos(level, n4);
|
||||
+ }
|
||||
+ // Luminol End - Raid revert
|
||||
}
|
||||
|
||||
if (this.raidCooldownTicks == 300 || this.raidCooldownTicks % 20 == 0) {
|
||||
@@ -375,7 +388,14 @@ public class Raid {
|
||||
int i = 0;
|
||||
|
||||
while (this.shouldSpawnGroup()) {
|
||||
- BlockPos blockPos = this.waveSpawnPos.orElseGet(() -> this.findRandomSpawnPos(level, 20));
|
||||
+ // Luminol Start - Raid revert
|
||||
+ BlockPos blockPos;
|
||||
+ if (!me.earthme.lophine.config.modules.misc.RaidChangesConfig.posRevert) {
|
||||
+ blockPos = this.waveSpawnPos.orElseGet(() -> this.findRandomSpawnPos(level, 20));
|
||||
+ } else {
|
||||
+ blockPos = this.waveSpawnPos.isPresent() ? this.waveSpawnPos.get() : this.findRandomSpawnPos(level, i, 20);
|
||||
+ }
|
||||
+ // Luminol End - Raid revert
|
||||
if (blockPos != null) {
|
||||
this.started = true;
|
||||
this.spawnGroup(level, blockPos);
|
||||
@@ -387,7 +407,7 @@ public class Raid {
|
||||
i++;
|
||||
}
|
||||
|
||||
- if (i > 5) {
|
||||
+ if (i > (me.earthme.lophine.config.modules.misc.RaidChangesConfig.posRevert ? 3 : 5)) { // Luminol - Raid revert
|
||||
org.bukkit.craftbukkit.event.CraftEventFactory.callRaidStopEvent(level, this, org.bukkit.event.raid.RaidStopEvent.Reason.UNSPAWNABLE); // CraftBukkit
|
||||
this.stop();
|
||||
break;
|
||||
@@ -458,6 +478,17 @@ public class Raid {
|
||||
return blockPos != null ? Optional.of(blockPos) : Optional.empty();
|
||||
}
|
||||
|
||||
+ // Luminol Start - Raid revert
|
||||
+ private Optional<BlockPos> getValidSpawnPos(ServerLevel level, int n) {
|
||||
+ for (int i = 0; i < 3; ++i) {
|
||||
+ BlockPos blockPos = this.findRandomSpawnPos(level, n, 1);
|
||||
+ if (blockPos == null) continue;
|
||||
+ return Optional.of(blockPos);
|
||||
+ }
|
||||
+ return Optional.empty();
|
||||
+ }
|
||||
+ // Luminol End - Raid revert
|
||||
+
|
||||
private boolean hasMoreWaves() {
|
||||
return this.hasBonusWave() ? !this.hasSpawnedBonusWave() : !this.isFinalWave();
|
||||
}
|
||||
@@ -683,7 +714,7 @@ public class Raid {
|
||||
int i2 = this.center.getX() + Mth.floor(Mth.cos(f2) * 32.0F * f) + level.random.nextInt(3) * Mth.floor(f);
|
||||
int i3 = this.center.getZ() + Mth.floor(Mth.sin(f2) * 32.0F * f) + level.random.nextInt(3) * Mth.floor(f);
|
||||
int height = level.getHeight(Heightmap.Types.WORLD_SURFACE, i2, i3);
|
||||
- if (Mth.abs(height - this.center.getY()) <= 96) {
|
||||
+ if (me.earthme.lophine.config.modules.misc.RaidChangesConfig.heightCheck || Mth.abs(height - this.center.getY()) <= 96) { // Leaves - Disable height check
|
||||
mutableBlockPos.set(i2, height, i3);
|
||||
if (!level.isVillage(mutableBlockPos) || i <= 7) {
|
||||
int i4 = 10;
|
||||
@@ -702,6 +733,26 @@ public class Raid {
|
||||
return null;
|
||||
}
|
||||
|
||||
+ // Luminol Start - Raid revert
|
||||
+ @Nullable
|
||||
+ private BlockPos findRandomSpawnPos(ServerLevel level, int n, int n2) {
|
||||
+ int n3 = 2 - n;
|
||||
+ BlockPos.MutableBlockPos mutableBlockPos = new BlockPos.MutableBlockPos();
|
||||
+ SpawnPlacementType spawnPlacementType = SpawnPlacements.getPlacementType(EntityType.RAVAGER);
|
||||
+ for (int i = 0; i < n2; ++i) {
|
||||
+ float f = level.random.nextFloat() * ((float)Math.PI * 2);
|
||||
+ int n4 = this.center.getX() + Mth.floor(Mth.cos(f) * 32.0f * (float)n3) + level.random.nextInt(5);
|
||||
+ int n5 = this.center.getZ() + Mth.floor(Mth.sin(f) * 32.0f * (float)n3) + level.random.nextInt(5);
|
||||
+ int n6 = level.getHeight(Heightmap.Types.WORLD_SURFACE, n4, n5);
|
||||
+ mutableBlockPos.set(n4, n6, n5);
|
||||
+ if (level.isVillage(mutableBlockPos) && n < 2) continue;
|
||||
+ if (!level.hasChunksAt(mutableBlockPos.getX() - 10, mutableBlockPos.getZ() - 10, mutableBlockPos.getX() + 10, mutableBlockPos.getZ() + 10) || !level.isPositionEntityTicking(mutableBlockPos) || !spawnPlacementType.isSpawnPositionOk(level, mutableBlockPos, EntityType.RAVAGER) && (!level.getBlockState(mutableBlockPos.below()).is(Blocks.SNOW) || !level.getBlockState(mutableBlockPos).isAir())) continue;
|
||||
+ return mutableBlockPos;
|
||||
+ }
|
||||
+ return null;
|
||||
+ }
|
||||
+ // Luminol End - Raid revert
|
||||
+
|
||||
private boolean addWaveMob(ServerLevel level, int wave, Raider raider) {
|
||||
// Folia start - make raids thread-safe
|
||||
if (!this.ownsRaid(level)) {
|
||||
diff --git a/net/minecraft/world/entity/raid/Raider.java b/net/minecraft/world/entity/raid/Raider.java
|
||||
index f6f36c15120da6c57c0cbea3743a0819252cb6cc..b2bb9b0ff94a7e463ebc4e59fe4378e022e52843 100644
|
||||
--- a/net/minecraft/world/entity/raid/Raider.java
|
||||
+++ b/net/minecraft/world/entity/raid/Raider.java
|
||||
@@ -127,6 +127,41 @@ public abstract class Raider extends PatrollingMonster {
|
||||
|
||||
currentRaid.removeFromRaid(serverLevel, this, false);
|
||||
}
|
||||
+
|
||||
+ // Leaves start - Revert raid changes
|
||||
+ if (me.earthme.lophine.config.modules.misc.RaidChangesConfig.effect && !this.hasRaid()) {
|
||||
+ ItemStack itemstack = this.getItemBySlot(EquipmentSlot.HEAD);
|
||||
+ net.minecraft.world.entity.player.Player entityhuman = null;
|
||||
+ if (entity instanceof net.minecraft.world.entity.player.Player player) {
|
||||
+ entityhuman = player;
|
||||
+ } else if (entity instanceof net.minecraft.world.entity.animal.wolf.Wolf wolf) {
|
||||
+ LivingEntity entityliving = wolf.getOwner();
|
||||
+ if (wolf.isTame() && entityliving instanceof net.minecraft.world.entity.player.Player player) {
|
||||
+ entityhuman = player;
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ if (entityhuman != null && !itemstack.isEmpty() && this.isCaptain()) {
|
||||
+ net.minecraft.world.effect.MobEffectInstance mobeffect = entityhuman.getEffect(net.minecraft.world.effect.MobEffects.BAD_OMEN);
|
||||
+ int i = 1;
|
||||
+
|
||||
+ if (mobeffect != null) {
|
||||
+ i += mobeffect.getAmplifier();
|
||||
+ entityhuman.removeEffectNoUpdate(net.minecraft.world.effect.MobEffects.BAD_OMEN);
|
||||
+ } else {
|
||||
+ --i;
|
||||
+ }
|
||||
+
|
||||
+ i = net.minecraft.util.Mth.clamp(i, 0, 4);
|
||||
+ net.minecraft.world.effect.MobEffectInstance mobeffect1 = new net.minecraft.world.effect.MobEffectInstance(net.minecraft.world.effect.MobEffects.BAD_OMEN, me.earthme.lophine.config.modules.misc.RaidChangesConfig.infinite ? net.minecraft.world.effect.MobEffectInstance.INFINITE_DURATION : 120000, i, false, false, true);
|
||||
+
|
||||
+ if (!serverLevel.getGameRules().getBoolean(net.minecraft.world.level.GameRules.RULE_DISABLE_RAIDS)) {
|
||||
+ entityhuman.addEffect(mobeffect1, entityhuman, org.bukkit.event.entity.EntityPotionEffectEvent.Cause.PATROL_CAPTAIN, true); // CraftBukkit
|
||||
+ }
|
||||
+ this.setPatrolLeader(false);
|
||||
+ }
|
||||
+ }
|
||||
+ // Leaves end - Revert raid changes
|
||||
}
|
||||
|
||||
super.die(cause);
|
||||
@@ -155,7 +190,7 @@ public abstract class Raider extends PatrollingMonster {
|
||||
}
|
||||
|
||||
public boolean hasRaid() {
|
||||
- return this.level() instanceof ServerLevel serverLevel && (this.getCurrentRaid() != null || serverLevel.getRaidAt(this.blockPosition()) != null);
|
||||
+ return !me.earthme.lophine.config.modules.misc.RaidChangesConfig.selfCheck && (this.level() instanceof ServerLevel serverLevel && (this.getCurrentRaid() != null || serverLevel.getRaidAt(this.blockPosition()) != null)); // Leaves - Disable raid self check
|
||||
}
|
||||
|
||||
public boolean hasActiveRaid() {
|
||||
diff --git a/net/minecraft/world/item/component/OminousBottleAmplifier.java b/net/minecraft/world/item/component/OminousBottleAmplifier.java
|
||||
index 33907bb190ffa22ccf9ea424b1e536297878711a..4b48aa0db9c1ab3e6227d1e1a42ce73db6943b04 100644
|
||||
--- a/net/minecraft/world/item/component/OminousBottleAmplifier.java
|
||||
+++ b/net/minecraft/world/item/component/OminousBottleAmplifier.java
|
||||
@@ -29,7 +29,7 @@ public record OminousBottleAmplifier(int value) implements ConsumableListener, T
|
||||
|
||||
@Override
|
||||
public void onConsume(Level level, LivingEntity entity, ItemStack stack, Consumable consumable) {
|
||||
- entity.addEffect(new MobEffectInstance(MobEffects.BAD_OMEN, 120000, this.value, false, false, true)); // Paper - properly resend entities - diff on change for below
|
||||
+ entity.addEffect(new MobEffectInstance(MobEffects.BAD_OMEN, me.earthme.lophine.config.modules.misc.RaidChangesConfig.infinite ? net.minecraft.world.effect.MobEffectInstance.INFINITE_DURATION : 120000, this.value, false, false, true)); // Paper - properly resend entities - diff on change for below // Luminol - Raid effect infinite
|
||||
}
|
||||
|
||||
// Paper start - properly resend entities - collect packets for bundle
|
||||
@@ -41,7 +41,7 @@ public record OminousBottleAmplifier(int value) implements ConsumableListener, T
|
||||
|
||||
@Override
|
||||
public void addToTooltip(Item.TooltipContext context, Consumer<Component> tooltipAdder, TooltipFlag flag, DataComponentGetter componentGetter) {
|
||||
- List<MobEffectInstance> list = List.of(new MobEffectInstance(MobEffects.BAD_OMEN, 120000, this.value, false, false, true));
|
||||
+ List<MobEffectInstance> list = List.of(new MobEffectInstance(MobEffects.BAD_OMEN, me.earthme.lophine.config.modules.misc.RaidChangesConfig.infinite ? net.minecraft.world.effect.MobEffectInstance.INFINITE_DURATION : 120000, this.value, false, false, true)); // Luminol - Raid effect infinite
|
||||
PotionContents.addPotionTooltip(list, tooltipAdder, 1.0F, context.tickRate());
|
||||
}
|
||||
}
|
||||
-154
@@ -1,154 +0,0 @@
|
||||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: Helvetica Volubi <suisuroru@blue-millennium.fun>
|
||||
Date: Tue, 29 Apr 2025 23:03:56 +0800
|
||||
Subject: [PATCH] Add config to enable Cross Region Damage trace
|
||||
|
||||
|
||||
diff --git a/net/minecraft/server/level/ServerPlayer.java b/net/minecraft/server/level/ServerPlayer.java
|
||||
index c3201cb0ba6f928f658d4c74a1ef2b7e8900b1f6..1764875b9024569671bf2be537afb8b492f79102 100644
|
||||
--- a/net/minecraft/server/level/ServerPlayer.java
|
||||
+++ b/net/minecraft/server/level/ServerPlayer.java
|
||||
@@ -1330,6 +1330,13 @@ public class ServerPlayer extends Player implements ca.spottedleaf.moonrise.patc
|
||||
this.awardStat(Stats.ENTITY_KILLED_BY.get(killCredit.getType()));
|
||||
killCredit.awardKillScore(this, cause);
|
||||
this.createWitherRose(killCredit);
|
||||
+ // Luminol Start - Cross Region Damage trace
|
||||
+ } else if (me.earthme.lophine.config.modules.experiment.EntityDamageSourceTraceConfig.enabled) {
|
||||
+ final LivingEntity entitylivingnew = this.getKillCreditOrigin();
|
||||
+ if (entitylivingnew != null) {
|
||||
+ this.damageTransferToAsync(entitylivingnew, cause);
|
||||
+ }
|
||||
+ // Luminol End - Cross Region Damage trace
|
||||
}
|
||||
|
||||
this.level().broadcastEntityEvent(this, (byte)3);
|
||||
@@ -1353,6 +1360,24 @@ public class ServerPlayer extends Player implements ca.spottedleaf.moonrise.patc
|
||||
}
|
||||
// Leaves end - exp fix
|
||||
|
||||
+ // Luminol Start - Cross Region Damage trace
|
||||
+ private void damageTransferToAsync(LivingEntity entity, DamageSource cause) {
|
||||
+ // Operations running on current entity
|
||||
+ this.awardStat(Stats.ENTITY_KILLED_BY.get(entity.getType()));
|
||||
+ this.createWitherRose(entity);
|
||||
+
|
||||
+ // the entity might be in another tickregion sometimes, so we need to schedule the task onto the entity
|
||||
+ // to ensure thread safe
|
||||
+ entity.getBukkitEntity().taskScheduler.schedule((LivingEntity nmsEntity) -> {
|
||||
+ try {
|
||||
+ nmsEntity.awardKillScore(this, cause);
|
||||
+ } catch (Throwable ex) {
|
||||
+ LOGGER.error(ex.getMessage(), ex);
|
||||
+ }
|
||||
+ }, null, 1L );
|
||||
+ }
|
||||
+ // Luminol End - Cross Region Damage trace
|
||||
+
|
||||
private void tellNeutralMobsThatIDied() {
|
||||
AABB aabb = new AABB(this.blockPosition()).inflate(32.0, 10.0, 32.0);
|
||||
this.level()
|
||||
diff --git a/net/minecraft/world/entity/LivingEntity.java b/net/minecraft/world/entity/LivingEntity.java
|
||||
index 5b5e63c4dc10076ba33a8ddf0ebae96643b1161f..063d682ae6cde044717a32402785b7a20ea8a351 100644
|
||||
--- a/net/minecraft/world/entity/LivingEntity.java
|
||||
+++ b/net/minecraft/world/entity/LivingEntity.java
|
||||
@@ -1191,6 +1191,29 @@ public abstract class LivingEntity extends Entity implements Attackable {
|
||||
}
|
||||
}
|
||||
|
||||
+ // Luminol Start - raid revert adapt Cross Region Damage trace
|
||||
+ public boolean addEffect(MobEffectInstance effectInstance, @Nullable Entity entity, EntityPotionEffectEvent.Cause cause, boolean fireEvent, boolean async) {
|
||||
+ if (ca.spottedleaf.moonrise.common.util.TickThread.isTickThreadFor(entity)) {
|
||||
+ return addEffect(effectInstance, entity, org.bukkit.event.entity.EntityPotionEffectEvent.Cause.PATROL_CAPTAIN, true);
|
||||
+ } else if (me.earthme.lophine.config.modules.experiment.EntityDamageSourceTraceConfig.enabled) {
|
||||
+ postToEntityThreadAddEffect(effectInstance, entity, org.bukkit.event.entity.EntityPotionEffectEvent.Cause.PATROL_CAPTAIN, true);
|
||||
+ return true;
|
||||
+ }
|
||||
+ return false;
|
||||
+ }
|
||||
+
|
||||
+ private void postToEntityThreadAddEffect(MobEffectInstance effectInstance, @Nullable Entity entity, EntityPotionEffectEvent.Cause cause, boolean fireEvent) {
|
||||
+ if (entity != null)
|
||||
+ entity.getBukkitEntity().taskScheduler.schedule((Entity nmsEntity) -> {
|
||||
+ try {
|
||||
+ addEffect(effectInstance, nmsEntity, cause, fireEvent);
|
||||
+ } catch (Throwable ex) {
|
||||
+ LOGGER.error(ex.getMessage(), ex);
|
||||
+ }
|
||||
+ }, null, 1L );
|
||||
+ }
|
||||
+ // Luminol End - raid revert adapt Cross Region Damage trace
|
||||
+
|
||||
public boolean canBeAffected(MobEffectInstance effectInstance) {
|
||||
if (this.getType().is(EntityTypeTags.IMMUNE_TO_INFESTED)) {
|
||||
return !effectInstance.is(MobEffects.INFESTED);
|
||||
@@ -1916,6 +1939,13 @@ public abstract class LivingEntity extends Entity implements Attackable {
|
||||
final LivingEntity killer = this.getKillCredit();
|
||||
if (killer != null) {
|
||||
killer.awardKillScore(this, damageSource);
|
||||
+ // Luminol Start - Cross Region Damage trace
|
||||
+ } else if (me.earthme.lophine.config.modules.experiment.EntityDamageSourceTraceConfig.enabled) {
|
||||
+ final LivingEntity killernew = this.getKillCreditOrigin();
|
||||
+ if (killernew != null) {
|
||||
+ this.damageTransferToAsync(killernew, damageSource);
|
||||
+ }
|
||||
+ // Luminol End - Cross Region Damage trace
|
||||
}
|
||||
}); // Paper end
|
||||
this.postDeathDropItems(deathEvent); // Paper
|
||||
@@ -1926,6 +1956,18 @@ public abstract class LivingEntity extends Entity implements Attackable {
|
||||
return deathEvent; // Paper
|
||||
}
|
||||
|
||||
+ // Luminol Start - Cross Region Damage trace
|
||||
+ private void damageTransferToAsync(LivingEntity entity, DamageSource damageSource) {
|
||||
+ entity.getBukkitEntity().taskScheduler.schedule((LivingEntity nmsEntity) -> {
|
||||
+ try {
|
||||
+ nmsEntity.awardKillScore(this, damageSource);
|
||||
+ } catch (Throwable ex) {
|
||||
+ LOGGER.error(ex.getMessage(), ex);
|
||||
+ }
|
||||
+ }, null, 1L );
|
||||
+ }
|
||||
+ // Luminol End - Cross Region Damage trace
|
||||
+
|
||||
protected void dropEquipment(ServerLevel level) {
|
||||
}
|
||||
protected void postDeathDropItems(org.bukkit.event.entity.EntityDeathEvent event) {} // Paper - method for post death logic that cannot be ran before the event is potentially cancelled
|
||||
@@ -2524,6 +2566,18 @@ public abstract class LivingEntity extends Entity implements Attackable {
|
||||
}
|
||||
}
|
||||
|
||||
+ // Luminol Start - Cross Region Damage trace
|
||||
+ @Nullable
|
||||
+ public LivingEntity getKillCreditOrigin() {
|
||||
+ if (this.lastHurtByPlayer != null) {
|
||||
+ return this.lastHurtByPlayer.getEntity(this.level(), Player.class);
|
||||
+ } else if (this.lastHurtByMob != null) {
|
||||
+ return this.lastHurtByMob.getEntity(this.level(), LivingEntity.class);
|
||||
+ }
|
||||
+ return null;
|
||||
+ }
|
||||
+ // Luminol End - Cross Region Damage trace
|
||||
+
|
||||
public final float getMaxHealth() {
|
||||
return (float)this.getAttributeValue(Attributes.MAX_HEALTH);
|
||||
}
|
||||
diff --git a/net/minecraft/world/entity/raid/Raider.java b/net/minecraft/world/entity/raid/Raider.java
|
||||
index b2bb9b0ff94a7e463ebc4e59fe4378e022e52843..4b72db3350f2990b00076fffb9df811560db8207 100644
|
||||
--- a/net/minecraft/world/entity/raid/Raider.java
|
||||
+++ b/net/minecraft/world/entity/raid/Raider.java
|
||||
@@ -156,7 +156,13 @@ public abstract class Raider extends PatrollingMonster {
|
||||
net.minecraft.world.effect.MobEffectInstance mobeffect1 = new net.minecraft.world.effect.MobEffectInstance(net.minecraft.world.effect.MobEffects.BAD_OMEN, me.earthme.lophine.config.modules.misc.RaidChangesConfig.infinite ? net.minecraft.world.effect.MobEffectInstance.INFINITE_DURATION : 120000, i, false, false, true);
|
||||
|
||||
if (!serverLevel.getGameRules().getBoolean(net.minecraft.world.level.GameRules.RULE_DISABLE_RAIDS)) {
|
||||
- entityhuman.addEffect(mobeffect1, entityhuman, org.bukkit.event.entity.EntityPotionEffectEvent.Cause.PATROL_CAPTAIN, true); // CraftBukkit
|
||||
+ if (me.earthme.lophine.config.modules.experiment.EntityDamageSourceTraceConfig.enabled) {
|
||||
+ // Luminol start - Raid changes adapt DamageSource trace
|
||||
+ entityhuman.addEffect(mobeffect1, entityhuman, org.bukkit.event.entity.EntityPotionEffectEvent.Cause.PATROL_CAPTAIN, true, true);
|
||||
+ } else {
|
||||
+ entityhuman.addEffect(mobeffect1, entityhuman, org.bukkit.event.entity.EntityPotionEffectEvent.Cause.PATROL_CAPTAIN, true); // CraftBukkit
|
||||
+ }
|
||||
+ // Luminol end - Raid changes adapt DamageSource trace
|
||||
}
|
||||
this.setPatrolLeader(false);
|
||||
}
|
||||
+92
@@ -0,0 +1,92 @@
|
||||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: Helvetica Volubi <suisuroru@blue-millennium.fun>
|
||||
Date: Mon, 7 Jul 2025 18:19:00 +0800
|
||||
Subject: [PATCH] Add config to enable unsafe waypoint bar
|
||||
|
||||
|
||||
diff --git a/net/minecraft/commands/Commands.java b/net/minecraft/commands/Commands.java
|
||||
index e2fc15d84fafde6847ecead23c55169449275e1b..b0a06cdfaa49237f372ecd131d47289160476b07 100644
|
||||
--- a/net/minecraft/commands/Commands.java
|
||||
+++ b/net/minecraft/commands/Commands.java
|
||||
@@ -253,7 +253,11 @@ public class Commands {
|
||||
TimeCommand.register(this.dispatcher);
|
||||
TitleCommand.register(this.dispatcher, context);
|
||||
//TriggerCommand.register(this.dispatcher); // Folia - region threading - TODO later
|
||||
- //WaypointCommand.register(this.dispatcher, context); // Folia - region threading - TODO later
|
||||
+ // Lophine start - unsafe waypoint bar
|
||||
+ if (fun.bm.lophine.config.modules.experiment.WaypointConfig.forceEnableWaypointUnsafe) {
|
||||
+ WaypointCommand.register(this.dispatcher, context); // Folia - region threading - TODO later
|
||||
+ }
|
||||
+ // Lophine end - unsafe waypoint bar
|
||||
WeatherCommand.register(this.dispatcher);
|
||||
WorldBorderCommand.register(this.dispatcher);
|
||||
if (JvmProfiler.INSTANCE.isAvailable()) {
|
||||
diff --git a/net/minecraft/server/waypoints/ServerWaypointManager.java b/net/minecraft/server/waypoints/ServerWaypointManager.java
|
||||
index 0f8cacbb8fe55a60e2f0c98bf36c005b29f41a4b..36e9b027ce5213f8914728293633e78e9905c1b2 100644
|
||||
--- a/net/minecraft/server/waypoints/ServerWaypointManager.java
|
||||
+++ b/net/minecraft/server/waypoints/ServerWaypointManager.java
|
||||
@@ -22,7 +22,14 @@ public class ServerWaypointManager implements WaypointManager<WaypointTransmitte
|
||||
|
||||
@Override
|
||||
public void trackWaypoint(WaypointTransmitter waypoint) {
|
||||
- // Folia - region threading
|
||||
+ // Lophine start - unsafe waypoint bar
|
||||
+ if (!fun.bm.lophine.config.modules.experiment.WaypointConfig.forceEnableWaypointUnsafe) return;
|
||||
+ this.waypoints.add(waypoint);
|
||||
+
|
||||
+ for (ServerPlayer serverPlayer : this.players) {
|
||||
+ this.createConnection(serverPlayer, waypoint);
|
||||
+ }
|
||||
+ // Lophine end - unsafe waypoint bar
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -50,14 +57,48 @@ public class ServerWaypointManager implements WaypointManager<WaypointTransmitte
|
||||
|
||||
public void addPlayer(ServerPlayer player) {
|
||||
// Folia - region threading
|
||||
+ // Lophine start - unsafe waypoint bar
|
||||
+ if (!fun.bm.lophine.config.modules.experiment.WaypointConfig.forceEnableWaypointUnsafe) return;
|
||||
+ this.players.add(player);
|
||||
+
|
||||
+ for (WaypointTransmitter waypointTransmitter : this.waypoints) {
|
||||
+ this.createConnection(player, waypointTransmitter);
|
||||
+ }
|
||||
+
|
||||
+ if (player.isTransmittingWaypoint()) {
|
||||
+ this.trackWaypoint((WaypointTransmitter)player);
|
||||
+ }
|
||||
+ // Lophine end - unsafe waypoint bar
|
||||
}
|
||||
|
||||
public void updatePlayer(ServerPlayer player) {
|
||||
// Folia - region threading
|
||||
+ // Lophine start - unsafe waypoint bar
|
||||
+ if (!fun.bm.lophine.config.modules.experiment.WaypointConfig.forceEnableWaypointUnsafe) return;
|
||||
+ Map<WaypointTransmitter, WaypointTransmitter.Connection> map = this.connections.row(player);
|
||||
+ SetView<WaypointTransmitter> set = Sets.difference(this.waypoints, map.keySet());
|
||||
+
|
||||
+ for (Entry<WaypointTransmitter, WaypointTransmitter.Connection> entry : ImmutableSet.copyOf(map.entrySet())) {
|
||||
+ this.updateConnection(player, entry.getKey(), entry.getValue());
|
||||
+ }
|
||||
+
|
||||
+ for (WaypointTransmitter waypointTransmitter : set) {
|
||||
+ this.createConnection(player, waypointTransmitter);
|
||||
+ }
|
||||
+ // Lophine end - unsafe waypoint bar
|
||||
}
|
||||
|
||||
public void removePlayer(ServerPlayer player) {
|
||||
// Folia - region threading
|
||||
+ // Lophine start - unsafe waypoint bar
|
||||
+ if (!fun.bm.lophine.config.modules.experiment.WaypointConfig.forceEnableWaypointUnsafe) return;
|
||||
+ this.connections.row(player).values().removeIf(connection -> {
|
||||
+ connection.disconnect();
|
||||
+ return true;
|
||||
+ });
|
||||
+ this.untrackWaypoint((WaypointTransmitter)player);
|
||||
+ this.players.remove(player);
|
||||
+ // Lophine end - unsafe waypoint bar
|
||||
}
|
||||
|
||||
public void breakAllConnections() {
|
||||
@@ -0,0 +1,198 @@
|
||||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: Helvetica Volubi <suisuroru@blue-millennium.fun>
|
||||
Date: Sun, 3 Aug 2025 15:24:01 +0800
|
||||
Subject: [PATCH] Leaves: Base Protocol Core
|
||||
|
||||
Co-authored by: violetc <58360096+s-yh-china@users.noreply.github.com>
|
||||
As a part of : Leaves (https://github.com/LeavesMC/Leaves/blob/ea91106ae57fc4cc14e2e0225009cf9919072f7f/leaves-server/minecraft-patches/features/0004-Leaves-Protocol-Core.patch)
|
||||
Licensed under: GPL-3.0 (https://www.gnu.org/licenses/gpl-3.0.html)
|
||||
|
||||
diff --git a/io/papermc/paper/threadedregions/RegionizedServer.java b/io/papermc/paper/threadedregions/RegionizedServer.java
|
||||
index 8405157ec35a443e238fa6866772c839621d73d7..4edb6e6e6287f232f7eef45c747554264ec05319 100644
|
||||
--- a/io/papermc/paper/threadedregions/RegionizedServer.java
|
||||
+++ b/io/papermc/paper/threadedregions/RegionizedServer.java
|
||||
@@ -334,6 +334,8 @@ public final class RegionizedServer {
|
||||
}
|
||||
// Luminol end - Add a config to enable tick command
|
||||
|
||||
+ org.leavesmc.leaves.protocol.core.LeavesProtocolManager.handleTick(tickCount); // Leaves - protocol
|
||||
+
|
||||
// tick connections
|
||||
this.tickConnections();
|
||||
|
||||
@@ -443,6 +445,8 @@ public final class RegionizedServer {
|
||||
|
||||
world.updateTickData();
|
||||
|
||||
+ MinecraftServer.getServer().handleTickCount(tickCount); // Lophine - reuse tick count
|
||||
+
|
||||
world.moonrise$getChunkTaskScheduler().chunkHolderManager.processTicketUpdates(); // required to eventually process ticket updates
|
||||
}
|
||||
|
||||
diff --git a/net/minecraft/network/protocol/common/custom/CustomPacketPayload.java b/net/minecraft/network/protocol/common/custom/CustomPacketPayload.java
|
||||
index fb263fa1f30a7dfcb7ec2656abfb38e5fe88eac9..56fd1ed7ccaf96e7eedea60fbdbf7f934939d563 100644
|
||||
--- a/net/minecraft/network/protocol/common/custom/CustomPacketPayload.java
|
||||
+++ b/net/minecraft/network/protocol/common/custom/CustomPacketPayload.java
|
||||
@@ -40,13 +40,22 @@ public interface CustomPacketPayload {
|
||||
|
||||
@Override
|
||||
public void encode(B buffer, CustomPacketPayload value) {
|
||||
+ // Leaves start - protocol core
|
||||
+ if (value instanceof org.leavesmc.leaves.protocol.core.LeavesCustomPayload payload) {
|
||||
+ org.leavesmc.leaves.protocol.core.LeavesProtocolManager.encode(buffer, payload);
|
||||
+ return;
|
||||
+ }
|
||||
+ // Leaves end - protocol core
|
||||
this.writeCap(buffer, value.type(), value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public CustomPacketPayload decode(B buffer) {
|
||||
ResourceLocation resourceLocation = buffer.readResourceLocation();
|
||||
- return (CustomPacketPayload)this.findCodec(resourceLocation).decode(buffer);
|
||||
+ // Leaves start - protocol core
|
||||
+ var payload = org.leavesmc.leaves.protocol.core.LeavesProtocolManager.decode(resourceLocation, buffer);
|
||||
+ return java.util.Objects.requireNonNullElseGet(payload, () -> this.findCodec(resourceLocation).decode(buffer));
|
||||
+ // Leaves end - protocol core
|
||||
}
|
||||
};
|
||||
}
|
||||
diff --git a/net/minecraft/server/MinecraftServer.java b/net/minecraft/server/MinecraftServer.java
|
||||
index a0f1db50adf5163a671d3d9e9df7adb7de4250c6..af13707933af14171447d5ab6faacb64ddf194a7 100644
|
||||
--- a/net/minecraft/server/MinecraftServer.java
|
||||
+++ b/net/minecraft/server/MinecraftServer.java
|
||||
@@ -305,6 +305,7 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
|
||||
|
||||
// Folia start - regionised ticking
|
||||
public final io.papermc.paper.threadedregions.RegionizedServer regionizedServer = new io.papermc.paper.threadedregions.RegionizedServer();
|
||||
+ private int tickCount;
|
||||
|
||||
@Override
|
||||
public <V> CompletableFuture<V> submit(java.util.function.Supplier<V> task) {
|
||||
@@ -2226,9 +2227,16 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
|
||||
return false;
|
||||
}
|
||||
|
||||
+ // Lophine start - reuse tick count
|
||||
public int getTickCount() {
|
||||
- throw new UnsupportedOperationException(); // Folia - region threading
|
||||
+ if (false) throw new UnsupportedOperationException(); // Folia - region threading
|
||||
+ return this.tickCount;
|
||||
+ }
|
||||
+
|
||||
+ public void handleTickCount(int tickCount1) {
|
||||
+ tickCount = tickCount1;
|
||||
}
|
||||
+ // Lophine end - reuse tick count
|
||||
|
||||
public int getSpawnProtectionRadius() {
|
||||
return 16;
|
||||
diff --git a/net/minecraft/server/network/ServerCommonPacketListenerImpl.java b/net/minecraft/server/network/ServerCommonPacketListenerImpl.java
|
||||
index 8150b16c196edcb226be9268ea6e0012e44517fa..c54bf0429c0ca3a35a730658c7b1b3ddc776bf9e 100644
|
||||
--- a/net/minecraft/server/network/ServerCommonPacketListenerImpl.java
|
||||
+++ b/net/minecraft/server/network/ServerCommonPacketListenerImpl.java
|
||||
@@ -57,6 +57,7 @@ public abstract class ServerCommonPacketListenerImpl implements ServerCommonPack
|
||||
public @Nullable String playerBrand;
|
||||
public final java.util.Set<String> pluginMessagerChannels;
|
||||
// Paper end - retain certain values
|
||||
+ public final GameProfile profile; // Leaves - protocol core
|
||||
|
||||
public ServerCommonPacketListenerImpl(MinecraftServer server, Connection connection, CommonListenerCookie cookie) {
|
||||
this.server = server;
|
||||
@@ -70,6 +71,7 @@ public abstract class ServerCommonPacketListenerImpl implements ServerCommonPack
|
||||
this.pluginMessagerChannels = cookie.channels();
|
||||
this.keepAlive = cookie.keepAlive();
|
||||
// Paper end
|
||||
+ this.profile = cookie.gameProfile(); // Leaves - protocol core
|
||||
}
|
||||
|
||||
// Paper start - configuration phase API
|
||||
@@ -160,6 +162,18 @@ public abstract class ServerCommonPacketListenerImpl implements ServerCommonPack
|
||||
|
||||
@Override
|
||||
public void handleCustomPayload(ServerboundCustomPayloadPacket packet) {
|
||||
+ // Leaves start - protocol
|
||||
+ if (packet.payload() instanceof org.leavesmc.leaves.protocol.core.LeavesCustomPayload leavesPayload) {
|
||||
+ org.leavesmc.leaves.protocol.core.LeavesProtocolManager.handlePayload(org.leavesmc.leaves.protocol.core.ProtocolUtils.createSelector(this), leavesPayload);
|
||||
+ return;
|
||||
+ }
|
||||
+ if (packet.payload() instanceof net.minecraft.network.protocol.common.custom.DiscardedPayload(net.minecraft.resources.ResourceLocation id, byte[] data)) {
|
||||
+ if (org.leavesmc.leaves.protocol.core.LeavesProtocolManager.handleBytebuf(org.leavesmc.leaves.protocol.core.ProtocolUtils.createSelector(this), id, io.netty.buffer.Unpooled.wrappedBuffer(data))) {
|
||||
+ return;
|
||||
+ }
|
||||
+ }
|
||||
+ // Leaves end - protocol
|
||||
+
|
||||
// Paper start
|
||||
if (!(packet.payload() instanceof final net.minecraft.network.protocol.common.custom.DiscardedPayload discardedPayload)) {
|
||||
return;
|
||||
@@ -219,10 +233,11 @@ public abstract class ServerCommonPacketListenerImpl implements ServerCommonPack
|
||||
final String channel = new String(data, from, length, java.nio.charset.StandardCharsets.US_ASCII);
|
||||
if (register) {
|
||||
bridge.addChannel(channel);
|
||||
+ org.leavesmc.leaves.protocol.core.LeavesProtocolManager.handleMinecraftRegister(channel, org.leavesmc.leaves.protocol.core.ProtocolUtils.createSelector(this)); // Leaves - protocol
|
||||
} else {
|
||||
bridge.removeChannel(channel);
|
||||
}
|
||||
- // Paper end
|
||||
+ // Paper end
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -385,9 +400,9 @@ public abstract class ServerCommonPacketListenerImpl implements ServerCommonPack
|
||||
net.minecraft.server.level.ServerPlayer player = serverGamePacketListener.player;
|
||||
org.bukkit.event.player.PlayerKickEvent.Cause cause = disconnectionDetails.disconnectionReason().orElseThrow().game().orElse(org.bukkit.event.player.PlayerKickEvent.Cause.UNKNOWN);
|
||||
org.bukkit.event.player.PlayerKickEvent event = new org.bukkit.event.player.PlayerKickEvent(
|
||||
- player.getBukkitEntity(),
|
||||
- io.papermc.paper.adventure.PaperAdventure.asAdventure(disconnectionDetails.reason()),
|
||||
- rawLeaveMessage, cause
|
||||
+ player.getBukkitEntity(),
|
||||
+ io.papermc.paper.adventure.PaperAdventure.asAdventure(disconnectionDetails.reason()),
|
||||
+ rawLeaveMessage, cause
|
||||
|
||||
);
|
||||
|
||||
@@ -420,10 +435,10 @@ public abstract class ServerCommonPacketListenerImpl implements ServerCommonPack
|
||||
|
||||
private void disconnect0(DisconnectionDetails disconnectionDetails) {
|
||||
this.connection
|
||||
- .send(
|
||||
- new ClientboundDisconnectPacket(disconnectionDetails.reason()),
|
||||
- PacketSendListener.thenRun(() -> this.connection.disconnect(disconnectionDetails))
|
||||
- );
|
||||
+ .send(
|
||||
+ new ClientboundDisconnectPacket(disconnectionDetails.reason()),
|
||||
+ PacketSendListener.thenRun(() -> this.connection.disconnect(disconnectionDetails))
|
||||
+ );
|
||||
this.onDisconnect(disconnectionDetails);
|
||||
this.connection.setReadOnly();
|
||||
// CraftBukkit - Don't wait
|
||||
diff --git a/net/minecraft/server/players/PlayerList.java b/net/minecraft/server/players/PlayerList.java
|
||||
index 4ef2816210f06082f2209c51d04aad6bb188232b..593dbcda4b497291eb1ecdc64b947a8d279f8be6 100644
|
||||
--- a/net/minecraft/server/players/PlayerList.java
|
||||
+++ b/net/minecraft/server/players/PlayerList.java
|
||||
@@ -423,6 +423,8 @@ public abstract class PlayerList {
|
||||
//return; // Folia - region threading - must still allow the player to connect, as we must add to chunk map before handling disconnect
|
||||
}
|
||||
|
||||
+ org.leavesmc.leaves.protocol.core.LeavesProtocolManager.handlePlayerJoin(player);
|
||||
+
|
||||
final net.kyori.adventure.text.Component jm = playerJoinEvent.joinMessage();
|
||||
|
||||
if (jm != null && !jm.equals(net.kyori.adventure.text.Component.empty())) { // Paper - Adventure
|
||||
@@ -605,6 +607,7 @@ public abstract class PlayerList {
|
||||
return this.remove(player, net.kyori.adventure.text.Component.translatable("multiplayer.player.left", net.kyori.adventure.text.format.NamedTextColor.YELLOW, io.papermc.paper.configuration.GlobalConfiguration.get().messages.useDisplayNameInQuitMessage ? player.getBukkitEntity().displayName() : io.papermc.paper.adventure.PaperAdventure.asAdventure(player.getDisplayName())));
|
||||
}
|
||||
public @Nullable net.kyori.adventure.text.Component remove(ServerPlayer player, net.kyori.adventure.text.Component leaveMessage) {
|
||||
+ org.leavesmc.leaves.protocol.core.LeavesProtocolManager.handlePlayerLeave(player); // Leaves - protocol
|
||||
// Paper end - Fix kick event leave message not being sent
|
||||
ServerLevel serverLevel = player.level();
|
||||
player.awardStat(Stats.LEAVE_GAME);
|
||||
@@ -1520,6 +1523,7 @@ public abstract class PlayerList {
|
||||
serverPlayer.connection.send(clientboundUpdateRecipesPacket);
|
||||
serverPlayer.getRecipeBook().sendInitialRecipeBook(serverPlayer);
|
||||
}
|
||||
+ org.leavesmc.leaves.protocol.core.LeavesProtocolManager.handleDataPackReload(); // Leaves - protocol core
|
||||
}
|
||||
|
||||
public boolean isAllowCommandsForAllPlayers() {
|
||||
+118
-174
@@ -1,107 +1,111 @@
|
||||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: Helvetica Volubi <suisuroru@blue-millennium.fun>
|
||||
Date: Mon, 9 Jun 2025 19:23:10 +0800
|
||||
Subject: [PATCH] Stackable ShulkerBoxes
|
||||
Date: Sat, 26 Jul 2025 01:11:54 +0800
|
||||
Subject: [PATCH] Leaves: Item overstack util
|
||||
|
||||
Co-authored by: violetc <58360096+s-yh-china@users.noreply.github.com>
|
||||
Some of changes is a part of : Leaves (https://github.com/LeavesMC/Leaves/blob/9d32c5bd3df7c76055aff886ed9efda02e45a45a/leaves-server/minecraft-patches/features/0034-Stackable-ShulkerBoxes.patch)
|
||||
As a part of : Leaves (https://github.com/LeavesMC/Leaves/blob/46cccfc39cafbca1681620d03947bc3ad558327f/leaves-server/minecraft-patches/features/0034-Item-overstack-util.patch)
|
||||
Licensed under: GPL-3.0 (https://www.gnu.org/licenses/gpl-3.0.html)
|
||||
|
||||
diff --git a/net/minecraft/commands/arguments/item/ItemInput.java b/net/minecraft/commands/arguments/item/ItemInput.java
|
||||
index 643797124fe5a4489d0b7419b7e600c04f283ef2..3c0d01d444210029b6380417c6ee0e6408f89c18 100644
|
||||
index 643797124fe5a4489d0b7419b7e600c04f283ef2..04b4100da96aad50f08e8c59200eec934e7e873d 100644
|
||||
--- a/net/minecraft/commands/arguments/item/ItemInput.java
|
||||
+++ b/net/minecraft/commands/arguments/item/ItemInput.java
|
||||
@@ -39,8 +39,9 @@ public class ItemInput {
|
||||
@@ -39,11 +39,13 @@ public class ItemInput {
|
||||
public ItemStack createItemStack(int count, boolean allowOversizedStacks) throws CommandSyntaxException {
|
||||
ItemStack itemStack = new ItemStack(this.item, count);
|
||||
itemStack.applyComponents(this.components);
|
||||
- if (allowOversizedStacks && count > itemStack.getMaxStackSize()) {
|
||||
- throw ERROR_STACK_TOO_BIG.create(this.getItemName(), itemStack.getMaxStackSize());
|
||||
+ int maxCount = me.earthme.lophine.utils.ShulkerBoxesUtil.getItemMaxCount(itemStack); // Lophine - Stackable ShulkerBoxes
|
||||
+ if (allowOversizedStacks && count > maxCount) { // Lophine - Stackable ShulkerBoxes
|
||||
+ throw ERROR_STACK_TOO_BIG.create(this.getItemName(), maxCount); // Lophine - Stackable ShulkerBoxes
|
||||
+ // Leaves start - item over-stack util
|
||||
+ if (allowOversizedStacks && count > org.leavesmc.leaves.util.ItemOverstackUtils.getItemStackMaxCount(itemStack)) {
|
||||
+ throw ERROR_STACK_TOO_BIG.create(this.getItemName(), org.leavesmc.leaves.util.ItemOverstackUtils.getItemStackMaxCount(itemStack));
|
||||
} else {
|
||||
return itemStack;
|
||||
}
|
||||
+ // Leaves end - item over-stack util
|
||||
}
|
||||
|
||||
public String serialize(HolderLookup.Provider levelRegistry) {
|
||||
diff --git a/net/minecraft/server/commands/GiveCommand.java b/net/minecraft/server/commands/GiveCommand.java
|
||||
index c1c0dea9f5ac3101c2f12714c8dd460457d45cb7..f94fc39297caebd0cb14d7b71c865f13b5c3b17c 100644
|
||||
index f7002d6752aa03603eb06e298ba1d7130cf13826..72c87f7c0392d087440db9d267a608598f6feb6d 100644
|
||||
--- a/net/minecraft/server/commands/GiveCommand.java
|
||||
+++ b/net/minecraft/server/commands/GiveCommand.java
|
||||
@@ -52,7 +52,7 @@ public class GiveCommand {
|
||||
@@ -55,7 +55,7 @@ public class GiveCommand {
|
||||
private static int giveItem(CommandSourceStack source, ItemInput item, Collection<ServerPlayer> targets, int count) throws CommandSyntaxException {
|
||||
ItemStack itemStack = item.createItemStack(1, false);
|
||||
final Component displayName = itemStack.getDisplayName(); // Paper - get display name early
|
||||
- int maxStackSize = itemStack.getMaxStackSize();
|
||||
+ int maxStackSize = me.earthme.lophine.utils.ShulkerBoxesUtil.getItemMaxCount(itemStack); // Lophine - Stackable ShulkerBoxes
|
||||
+ int maxStackSize = org.leavesmc.leaves.util.ItemOverstackUtils.getItemStackMaxCount(itemStack); // Leaves - item over-stack util
|
||||
int i = maxStackSize * 100;
|
||||
if (count > i) {
|
||||
source.sendFailure(Component.translatable("commands.give.failed.toomanyitems", i, itemStack.getDisplayName()));
|
||||
diff --git a/net/minecraft/server/network/ServerGamePacketListenerImpl.java b/net/minecraft/server/network/ServerGamePacketListenerImpl.java
|
||||
index 4fc3c9f2b04711569c12a0efa027601fdd0a40b5..e1e68cc934f8b5a8606a553f3e0a939c61a70377 100644
|
||||
index d74a05822c6c145971dc526a62d652d35b4f5d9d..d63d8f7523938088bf9efc1c1b8ad9f0583338bb 100644
|
||||
--- a/net/minecraft/server/network/ServerGamePacketListenerImpl.java
|
||||
+++ b/net/minecraft/server/network/ServerGamePacketListenerImpl.java
|
||||
@@ -3056,7 +3056,7 @@ public class ServerGamePacketListenerImpl
|
||||
@@ -3079,7 +3079,7 @@ public class ServerGamePacketListenerImpl
|
||||
} else if (slot.mayPlace(cursor)) {
|
||||
if (ItemStack.isSameItemSameComponents(clickedItem, cursor)) {
|
||||
int toPlace = packet.buttonNum() == 0 ? cursor.getCount() : 1;
|
||||
- toPlace = Math.min(toPlace, clickedItem.getMaxStackSize() - clickedItem.getCount());
|
||||
+ toPlace = Math.min(toPlace, me.earthme.lophine.utils.ShulkerBoxesUtil.getItemMaxCount(clickedItem) - clickedItem.getCount()); // Lophine - Stackable ShulkerBoxes
|
||||
+ toPlace = Math.min(toPlace, org.leavesmc.leaves.util.ItemOverstackUtils.getItemStackMaxCount(clickedItem) - clickedItem.getCount()); // Leaves - item over-stack util
|
||||
toPlace = Math.min(toPlace, slot.container.getMaxStackSize() - clickedItem.getCount());
|
||||
if (toPlace == 1) {
|
||||
action = InventoryAction.PLACE_ONE;
|
||||
@@ -3092,7 +3092,7 @@ public class ServerGamePacketListenerImpl
|
||||
@@ -3115,7 +3115,7 @@ public class ServerGamePacketListenerImpl
|
||||
}
|
||||
} else if (ItemStack.isSameItemSameComponents(cursor, clickedItem)) {
|
||||
if (clickedItem.getCount() >= 0) {
|
||||
- if (clickedItem.getCount() + cursor.getCount() <= cursor.getMaxStackSize()) {
|
||||
+ if (clickedItem.getCount() + cursor.getCount() <= me.earthme.lophine.utils.ShulkerBoxesUtil.getItemMaxCount(cursor)) { // Lophine - Stackable ShulkerBoxes
|
||||
+ if (clickedItem.getCount() + cursor.getCount() <= org.leavesmc.leaves.util.ItemOverstackUtils.getItemStackMaxCount(cursor)) { // Leaves - item over-stack util
|
||||
// As of 1.5, this is result slots only
|
||||
action = InventoryAction.PICKUP_ALL;
|
||||
}
|
||||
@@ -3351,6 +3351,7 @@ public class ServerGamePacketListenerImpl
|
||||
@@ -3326,6 +3326,7 @@ public class ServerGamePacketListenerImpl
|
||||
this.player.containerMenu.broadcastFullState();
|
||||
} else {
|
||||
this.player.containerMenu.broadcastChanges();
|
||||
+ if (me.earthme.lophine.utils.ShulkerBoxesUtil.shouldCheck()) this.player.containerMenu.broadcastCarriedItem(); // Lophine - Stackable ShulkerBoxes
|
||||
+ if (org.leavesmc.leaves.util.ItemOverstackUtils.hasOverstackingItem()) this.player.containerMenu.broadcastCarriedItem(); // Leaves - item over-stack util - force send carried item
|
||||
}
|
||||
if (packet.buttonNum() == Inventory.SLOT_OFFHAND && this.player.containerMenu != this.player.inventoryMenu) this.player.containerSynchronizer.sendOffHandSlotChange(); // Paper - update offhand data when the player is clicking in an inventory not their own as the sychronizer does not include offhand slots
|
||||
if (io.papermc.paper.configuration.GlobalConfiguration.get().unsupportedSettings.updateEquipmentOnPlayerActions) this.player.detectEquipmentUpdates(); // Paper - Force update attributes.
|
||||
}
|
||||
@@ -3463,7 +3464,7 @@ public class ServerGamePacketListenerImpl
|
||||
@@ -3437,7 +3438,7 @@ public class ServerGamePacketListenerImpl
|
||||
}
|
||||
|
||||
boolean flag1 = packet.slotNum() >= 1 && packet.slotNum() <= 45;
|
||||
- boolean flag2 = itemStack.isEmpty() || itemStack.getCount() <= itemStack.getMaxStackSize();
|
||||
+ boolean flag2 = itemStack.isEmpty() || itemStack.getCount() <= me.earthme.lophine.utils.ShulkerBoxesUtil.getItemMaxCount(itemStack); // Lophine - Stackable ShulkerBoxes
|
||||
+ boolean flag2 = itemStack.isEmpty() || itemStack.getCount() <= org.leavesmc.leaves.util.ItemOverstackUtils.getItemStackMaxCount(itemStack); // Leaves - item over-stack util
|
||||
if (flag || (flag1 && !ItemStack.matches(this.player.inventoryMenu.getSlot(packet.slotNum()).getItem(), packet.itemStack()))) { // Insist on valid slot
|
||||
// CraftBukkit start - Call click event
|
||||
org.bukkit.inventory.InventoryView inventory = this.player.inventoryMenu.getBukkitView();
|
||||
@@ -3505,6 +3506,7 @@ public class ServerGamePacketListenerImpl
|
||||
@@ -3479,6 +3480,7 @@ public class ServerGamePacketListenerImpl
|
||||
this.player.inventoryMenu.getSlot(packet.slotNum()).setByPlayer(itemStack);
|
||||
this.player.inventoryMenu.setRemoteSlot(packet.slotNum(), itemStack);
|
||||
this.player.inventoryMenu.broadcastChanges();
|
||||
+ if (me.earthme.lophine.utils.ShulkerBoxesUtil.shouldCheck()) this.player.containerMenu.broadcastCarriedItem(); // Lophine - Stackable ShulkerBoxes
|
||||
+ if (org.leavesmc.leaves.util.ItemOverstackUtils.hasOverstackingItem()) this.player.containerMenu.sendSingleSlot(packet.slotNum(), itemStack); // Leaves - item over-stack util - force send carried item
|
||||
if (io.papermc.paper.configuration.GlobalConfiguration.get().unsupportedSettings.updateEquipmentOnPlayerActions) this.player.detectEquipmentUpdates(); // Paper - Force update attributes.
|
||||
} else if (flag && flag2) {
|
||||
if (this.dropSpamThrottler.isUnderThreshold()) {
|
||||
diff --git a/net/minecraft/world/Container.java b/net/minecraft/world/Container.java
|
||||
index b382665cc125b8b5c0938e5e55984e4bf91d37ff..8ff16372df12f07e47d81fe45ad7270bb5c2b052 100644
|
||||
index 2afaf6a3f18af7889f4f3b35cee9bbb5e4ec01f2..da99b4bc7fe8460945070915073be141f9bd6778 100644
|
||||
--- a/net/minecraft/world/Container.java
|
||||
+++ b/net/minecraft/world/Container.java
|
||||
@@ -32,6 +32,12 @@ public interface Container extends Clearable, Iterable<ItemStack> {
|
||||
@@ -32,6 +32,12 @@ public interface Container extends Clearable, Iterable<ItemStack>, org.leavesmc.
|
||||
return Math.min(this.getMaxStackSize(), stack.getMaxStackSize());
|
||||
}
|
||||
|
||||
+ // Leaves start - stackable shulker boxes
|
||||
+ // Leaves start - item over-stack util
|
||||
+ default int getMaxStackLeaves(ItemStack stack) {
|
||||
+ return Math.min(this.getMaxStackSize(), me.earthme.lophine.utils.ShulkerBoxesUtil.getItemMaxCount(stack));
|
||||
+ return Math.min(this.getMaxStackSize(), org.leavesmc.leaves.util.ItemOverstackUtils.getItemStackMaxCount(stack));
|
||||
+ }
|
||||
+ // Leaves end - stackable shulker boxes
|
||||
+ // Leaves end - item over-stack util
|
||||
+
|
||||
void setChanged();
|
||||
|
||||
boolean stillValid(Player player);
|
||||
diff --git a/net/minecraft/world/SimpleContainer.java b/net/minecraft/world/SimpleContainer.java
|
||||
index 133e042371bcf84f1935903ec57d204e3b7abd84..201599988e20219b6a99bf1594ad6c0c19e09038 100644
|
||||
index d907e24d563e27acab2f2bf9711b1755ea9afd19..75170c8d3be477a6ea2a1d62018a6ab630b0e54e 100644
|
||||
--- a/net/minecraft/world/SimpleContainer.java
|
||||
+++ b/net/minecraft/world/SimpleContainer.java
|
||||
@@ -211,7 +211,7 @@ public class SimpleContainer implements Container, StackedContentsCompatible {
|
||||
@@ -109,7 +113,7 @@ index 133e042371bcf84f1935903ec57d204e3b7abd84..201599988e20219b6a99bf1594ad6c0c
|
||||
public void setItem(int index, ItemStack stack) {
|
||||
this.items.set(index, stack);
|
||||
- stack.limitSize(this.getMaxStackSize(stack));
|
||||
+ stack.limitSize(this.getMaxStackLeaves(stack)); // Leaves - stackable shulker boxes
|
||||
+ stack.limitSize(this.getMaxStackLeaves(stack)); // Leaves - item over-stack util
|
||||
this.setChanged();
|
||||
}
|
||||
|
||||
@@ -118,114 +122,88 @@ index 133e042371bcf84f1935903ec57d204e3b7abd84..201599988e20219b6a99bf1594ad6c0c
|
||||
|
||||
private void moveItemsBetweenStacks(ItemStack stack, ItemStack other) {
|
||||
- int maxStackSize = this.getMaxStackSize(other);
|
||||
+ int maxStackSize = this.getMaxStackLeaves(other); // Leaves - stackable shulker boxes
|
||||
+ int maxStackSize = this.getMaxStackLeaves(other); // Leaves - item over-stack util
|
||||
int min = Math.min(stack.getCount(), maxStackSize - other.getCount());
|
||||
if (min > 0) {
|
||||
other.grow(min);
|
||||
diff --git a/net/minecraft/world/entity/item/ItemEntity.java b/net/minecraft/world/entity/item/ItemEntity.java
|
||||
index a9cc35b4b253d9a7fd90f811af9be629b164fad0..5c548cb30565d156c6eeb316b0c0ce8be93ad0d7 100644
|
||||
index fd4f1e63f980b6959fc80084651991f8f8bc6c34..e5e8c752129a6ebb0dd1315c9b8ec459be11ef07 100644
|
||||
--- a/net/minecraft/world/entity/item/ItemEntity.java
|
||||
+++ b/net/minecraft/world/entity/item/ItemEntity.java
|
||||
@@ -281,10 +281,45 @@ public class ItemEntity extends Entity implements TraceableEntity {
|
||||
@@ -283,10 +283,15 @@ public class ItemEntity extends Entity implements TraceableEntity, ChangePublish
|
||||
|
||||
private boolean isMergable() {
|
||||
ItemStack item = this.getItem();
|
||||
- return this.isAlive() && this.pickupDelay != 32767 && this.age != -32768 && this.age < this.despawnRate && item.getCount() < item.getMaxStackSize(); // Paper - Alternative item-despawn-rate
|
||||
+ return this.isAlive() && this.pickupDelay != 32767 && this.age != -32768 && this.age < this.despawnRate && item.getCount() < me.earthme.lophine.utils.ShulkerBoxesUtil.getItemMaxCount(item); // Paper - Alternative item-despawn-rate // Lophine - Stackable ShulkerBoxes
|
||||
+ }
|
||||
+
|
||||
+ // Lophine start - Stackable ShulkerBoxes
|
||||
+ private boolean tryMergeShulkerBox(ItemEntity ote) {
|
||||
+ ItemStack sf = this.getItem();
|
||||
+ ItemStack ot = ote.getItem();
|
||||
+ if (!me.earthme.lophine.utils.ShulkerBoxesUtil.checkShulkerBox(sf)) return false;
|
||||
+ if (sf.getItem().equals(ot.getItem())
|
||||
+ && me.earthme.lophine.utils.ShulkerBoxesUtil.emptyShulkerBoxCheck(sf)
|
||||
+ && me.earthme.lophine.utils.ShulkerBoxesUtil.emptyShulkerBoxCheck(ot)
|
||||
+ && Objects.equals(sf.getComponents(), ot.getComponents()) // empty block entity tags are cleaned up when spawning
|
||||
+ && sf.getCount() != me.earthme.lophine.utils.ShulkerBoxesUtil.getShulkerBoxesMaxCountUnsafe()) {
|
||||
+ int count = Math.min(ot.getCount(), me.earthme.lophine.utils.ShulkerBoxesUtil.getShulkerBoxesMaxCountUnsafe() - sf.getCount());
|
||||
+ sf.grow(count);
|
||||
+ this.setItem(sf);
|
||||
+
|
||||
+ this.pickupDelay = Math.max(ote.pickupDelay, this.pickupDelay);
|
||||
+ this.age = Math.min(ote.getAge(), this.age);
|
||||
+ ot.shrink(count);
|
||||
+ if (ot.isEmpty()) {
|
||||
+ ote.discard();
|
||||
+ }
|
||||
+ else {
|
||||
+ ote.setItem(ot);
|
||||
+ }
|
||||
+ return true;
|
||||
+ }
|
||||
+ return false;
|
||||
+ return this.isAlive() && this.pickupDelay != 32767 && this.age != -32768 && this.age < this.despawnRate && item.getCount() < org.leavesmc.leaves.util.ItemOverstackUtils.getItemStackMaxCount(item); // Paper - Alternative item-despawn-rate // Leaves - item over-stack util
|
||||
}
|
||||
+ // Lophine end - Stackable ShulkerBoxes
|
||||
|
||||
private void tryToMerge(ItemEntity itemEntity) {
|
||||
+ // Lophine start - Stackable ShulkerBoxes
|
||||
+ if (me.earthme.lophine.utils.ShulkerBoxesUtil.shouldCheck()
|
||||
+ && this.tryMergeShulkerBox(itemEntity)) {
|
||||
+ // Leaves start - item over-stack util
|
||||
+ if (org.leavesmc.leaves.util.ItemOverstackUtils.tryStackItems(this, itemEntity)) {
|
||||
+ return;
|
||||
+ }
|
||||
+ // Lophine end - Stackable ShulkerBoxes
|
||||
+ // Leaves end - item over-stack util
|
||||
ItemStack item = this.getItem();
|
||||
ItemStack item1 = itemEntity.getItem();
|
||||
if (Objects.equals(this.target, itemEntity.target) && areMergable(item, item1)) {
|
||||
diff --git a/net/minecraft/world/entity/player/Inventory.java b/net/minecraft/world/entity/player/Inventory.java
|
||||
index d9cb4f0ed0c4f63362c837aeef3c4194911455c9..9fbd605df26d388c01d2ed9ca6a6138651e7f9a7 100644
|
||||
index a6bb436dc80daf6901dc027a6011ead4b3ed27e2..71df58ae26325a2d28b6679733c474c0c59f7654 100644
|
||||
--- a/net/minecraft/world/entity/player/Inventory.java
|
||||
+++ b/net/minecraft/world/entity/player/Inventory.java
|
||||
@@ -149,8 +149,8 @@ public class Inventory implements Container, Nameable {
|
||||
@@ -165,10 +165,12 @@ public class Inventory implements Container, Nameable {
|
||||
}
|
||||
|
||||
private boolean hasRemainingSpaceForItem(ItemStack destination, ItemStack origin) {
|
||||
+ // Leaves start - item over-stack util
|
||||
return !destination.isEmpty()
|
||||
- && destination.isStackable()
|
||||
- && destination.getCount() < this.getMaxStackSize(destination)
|
||||
+ && me.earthme.lophine.utils.ShulkerBoxesUtil.isStackable(destination) // Lophine - Stackable ShulkerBoxes
|
||||
+ && destination.getCount() < me.earthme.lophine.utils.ShulkerBoxesUtil.getItemMaxCount(destination) // Lophine - Stackable ShulkerBoxes
|
||||
+ && org.leavesmc.leaves.util.ItemOverstackUtils.isStackable(destination)
|
||||
+ && destination.getCount() < org.leavesmc.leaves.util.ItemOverstackUtils.getItemStackMaxCount(destination)
|
||||
&& ItemStack.isSameItemSameComponents(destination, origin); // Paper - check if itemstack is stackable first
|
||||
+ // Leaves end - item over-stack util
|
||||
}
|
||||
|
||||
@@ -164,7 +164,7 @@ public class Inventory implements Container, Nameable {
|
||||
// CraftBukkit start - Watch method above! :D
|
||||
@@ -181,7 +183,7 @@ public class Inventory implements Container, Nameable {
|
||||
}
|
||||
|
||||
if (this.hasRemainingSpaceForItem(itemInSlot, itemStack)) {
|
||||
- remains -= (itemInSlot.getMaxStackSize() < this.getMaxStackSize() ? itemInSlot.getMaxStackSize() : this.getMaxStackSize()) - itemInSlot.getCount();
|
||||
+ remains -= (me.earthme.lophine.utils.ShulkerBoxesUtil.getItemMaxCount(itemInSlot) < this.getMaxStackSize() ? me.earthme.lophine.utils.ShulkerBoxesUtil.getItemMaxCount(itemInSlot) : this.getMaxStackSize()) - itemInSlot.getCount(); // Lophine - Stackable ShulkerBoxes
|
||||
+ remains -= (org.leavesmc.leaves.util.ItemOverstackUtils.getItemStackMaxCount(itemInSlot) < this.getMaxStackSize() ? org.leavesmc.leaves.util.ItemOverstackUtils.getItemStackMaxCount(itemInSlot) : this.getMaxStackSize()) - itemInSlot.getCount(); // Leaves - item over-stack util
|
||||
}
|
||||
if (remains <= 0) {
|
||||
return itemStack.getCount();
|
||||
@@ -173,7 +173,7 @@ public class Inventory implements Container, Nameable {
|
||||
@@ -190,7 +192,7 @@ public class Inventory implements Container, Nameable {
|
||||
|
||||
ItemStack itemInOffhand = this.equipment.get(EquipmentSlot.OFFHAND);
|
||||
if (this.hasRemainingSpaceForItem(itemInOffhand, itemStack)) {
|
||||
- remains -= (itemInOffhand.getMaxStackSize() < this.getMaxStackSize() ? itemInOffhand.getMaxStackSize() : this.getMaxStackSize()) - itemInOffhand.getCount();
|
||||
+ remains -= (me.earthme.lophine.utils.ShulkerBoxesUtil.getItemMaxCount(itemInOffhand) < this.getMaxStackSize() ? me.earthme.lophine.utils.ShulkerBoxesUtil.getItemMaxCount(itemInOffhand) : this.getMaxStackSize()) - itemInOffhand.getCount(); // Lophine - Stackable ShulkerBoxes
|
||||
+ remains -= (org.leavesmc.leaves.util.ItemOverstackUtils.getItemStackMaxCount(itemInOffhand) < this.getMaxStackSize() ? org.leavesmc.leaves.util.ItemOverstackUtils.getItemStackMaxCount(itemInOffhand) : this.getMaxStackSize()) - itemInOffhand.getCount(); // Leaves - item over-stack util
|
||||
}
|
||||
if (remains <= 0) {
|
||||
return itemStack.getCount();
|
||||
@@ -297,7 +297,7 @@ public class Inventory implements Container, Nameable {
|
||||
@@ -314,7 +316,7 @@ public class Inventory implements Container, Nameable {
|
||||
this.setItem(slot, item);
|
||||
}
|
||||
|
||||
- int i = this.getMaxStackSize(item) - item.getCount();
|
||||
+ int i = this.getMaxStackLeaves(item) - item.getCount();
|
||||
+ int i = this.getMaxStackLeaves(item) - item.getCount(); // Leaves - item over-stack util
|
||||
int min = Math.min(count, i);
|
||||
if (min == 0) {
|
||||
return count;
|
||||
@@ -403,7 +403,7 @@ public class Inventory implements Container, Nameable {
|
||||
@@ -420,7 +422,7 @@ public class Inventory implements Container, Nameable {
|
||||
break;
|
||||
}
|
||||
|
||||
- int i = stack.getMaxStackSize() - this.getItem(slotWithRemainingSpace).getCount();
|
||||
+ int i = me.earthme.lophine.utils.ShulkerBoxesUtil.getItemMaxCount(stack) - this.getItem(slotWithRemainingSpace).getCount(); // Lophine - Stackable ShulkerBoxes
|
||||
+ int i = org.leavesmc.leaves.util.ItemOverstackUtils.getItemStackMaxCount(stack) - this.getItem(slotWithRemainingSpace).getCount(); // Leaves - item over-stack util
|
||||
if (this.add(slotWithRemainingSpace, stack.split(i)) && sendPacket && this.player instanceof ServerPlayer serverPlayer) {
|
||||
serverPlayer.connection.send(this.createInventoryUpdatePacket(slotWithRemainingSpace));
|
||||
}
|
||||
diff --git a/net/minecraft/world/entity/player/StackedItemContents.java b/net/minecraft/world/entity/player/StackedItemContents.java
|
||||
index 83ccde54c625d40dc595e000c533f60aa929bd5a..16e9ac7449761c1427a528be60e964ef737ce897 100644
|
||||
index 83ccde54c625d40dc595e000c533f60aa929bd5a..6779503888e6d311758cffde582b0efbd66a33de 100644
|
||||
--- a/net/minecraft/world/entity/player/StackedItemContents.java
|
||||
+++ b/net/minecraft/world/entity/player/StackedItemContents.java
|
||||
@@ -23,7 +23,7 @@ public class StackedItemContents {
|
||||
@@ -233,12 +211,12 @@ index 83ccde54c625d40dc595e000c533f60aa929bd5a..16e9ac7449761c1427a528be60e964ef
|
||||
|
||||
public void accountStack(ItemStack stack) {
|
||||
- this.accountStack(stack, stack.getMaxStackSize());
|
||||
+ this.accountStack(stack, me.earthme.lophine.utils.ShulkerBoxesUtil.getItemMaxCount(stack)); // Lophine - Stackable Shulker Boxes
|
||||
+ this.accountStack(stack, org.leavesmc.leaves.util.ItemOverstackUtils.getItemStackMaxCount(stack)); // Leaves - item over-stack util
|
||||
}
|
||||
|
||||
public void accountStack(ItemStack stack, int maxStackSize) {
|
||||
diff --git a/net/minecraft/world/entity/vehicle/ContainerEntity.java b/net/minecraft/world/entity/vehicle/ContainerEntity.java
|
||||
index feebd1610ebd3c26a337259c14f5c774dc72b937..7df6ff842e41763aec2d88d1f8a5f7503932d905 100644
|
||||
index 02d2efef2dc0f0e12eac0c71fa290af706f7694d..99f109e2653eff10c011f380694bd77a76381cee 100644
|
||||
--- a/net/minecraft/world/entity/vehicle/ContainerEntity.java
|
||||
+++ b/net/minecraft/world/entity/vehicle/ContainerEntity.java
|
||||
@@ -163,7 +163,7 @@ public interface ContainerEntity extends Container, MenuProvider {
|
||||
@@ -246,62 +224,61 @@ index feebd1610ebd3c26a337259c14f5c774dc72b937..7df6ff842e41763aec2d88d1f8a5f750
|
||||
this.unpackChestVehicleLootTable(null);
|
||||
this.getItemStacks().set(slot, stack);
|
||||
- stack.limitSize(this.getMaxStackSize(stack));
|
||||
+ stack.limitSize(this.getMaxStackLeaves(stack)); // Leaves - stackable shulker boxes
|
||||
+ stack.limitSize(this.getMaxStackLeaves(stack)); // Leaves - item over-stack util
|
||||
}
|
||||
|
||||
default SlotAccess getChestVehicleSlot(final int index) {
|
||||
diff --git a/net/minecraft/world/inventory/AbstractContainerMenu.java b/net/minecraft/world/inventory/AbstractContainerMenu.java
|
||||
index a9cf0ef95b706f05060bbcd2a4f5a03c60f783d2..6c5a01b065c7daa37219a8ab2a471d93e882e756 100644
|
||||
index fcb2e0a722ed94acf08da06f06b20f42652896bc..9489a50df2de3d641235153c5a0217d96c1f4fa9 100644
|
||||
--- a/net/minecraft/world/inventory/AbstractContainerMenu.java
|
||||
+++ b/net/minecraft/world/inventory/AbstractContainerMenu.java
|
||||
@@ -234,6 +234,14 @@ public abstract class AbstractContainerMenu {
|
||||
return list;
|
||||
@@ -295,6 +295,13 @@ public abstract class AbstractContainerMenu {
|
||||
this.sendAllDataToRemote();
|
||||
}
|
||||
|
||||
+ // Lophine start - Stackable ShulkerBoxes
|
||||
+ public void boardcastChangesSigleSlot(int slot, ItemStack item) {
|
||||
+ // Leaves start - item over-stack util
|
||||
+ public void sendSingleSlot(int slotIndex, ItemStack item) {
|
||||
+ if (this.synchronizer != null) {
|
||||
+ this.synchronizer.sendSlotChange(this, slot, item);
|
||||
+ this.synchronizer.sendSlotChange(this, slotIndex, item);
|
||||
+ }
|
||||
+ }
|
||||
+ // Lophine end - Stackable ShulkerBoxes
|
||||
+
|
||||
public void broadcastChanges() {
|
||||
for (int i = 0; i < this.slots.size(); i++) {
|
||||
ItemStack item = this.slots.get(i).getItem();
|
||||
@@ -427,7 +435,7 @@ public abstract class AbstractContainerMenu {
|
||||
+ // Leaves end - item over-stack util
|
||||
private void updateDataSlotListeners(int slotIndex, int value) {
|
||||
for (ContainerListener containerListener : this.containerListeners) {
|
||||
containerListener.dataChanged(this, slotIndex, value);
|
||||
@@ -451,7 +458,7 @@ public abstract class AbstractContainerMenu {
|
||||
&& (this.quickcraftType == 2 || carried1.getCount() >= this.quickcraftSlots.size())
|
||||
&& this.canDragTo(slot1)) {
|
||||
int i2 = slot1.hasItem() ? slot1.getItem().getCount() : 0;
|
||||
- int min = Math.min(itemStack.getMaxStackSize(), slot1.getMaxStackSize(itemStack));
|
||||
+ int min = Math.min(me.earthme.lophine.utils.ShulkerBoxesUtil.getItemMaxCount(itemStack), slot1.getMaxStackSize(itemStack)); // Lophine - Stackable ShulkerBoxes
|
||||
+ int min = Math.min(org.leavesmc.leaves.util.ItemOverstackUtils.getItemStackMaxCount(itemStack), slot1.getMaxStackSize(itemStack)); // Leaves - item over-stack util
|
||||
int min1 = Math.min(getQuickCraftPlaceCount(this.quickcraftSlots, this.quickcraftType, itemStack) + i2, min);
|
||||
count -= min1 - i2;
|
||||
// slot1.setByPlayer(itemStack.copyWithCount(min1));
|
||||
@@ -541,7 +549,7 @@ public abstract class AbstractContainerMenu {
|
||||
@@ -563,7 +570,7 @@ public abstract class AbstractContainerMenu {
|
||||
slot.setByPlayer(carried2);
|
||||
}
|
||||
} else if (ItemStack.isSameItemSameComponents(carried, carried2)) {
|
||||
- Optional<ItemStack> optional1 = slot.tryRemove(carried.getCount(), carried2.getMaxStackSize() - carried2.getCount(), player);
|
||||
+ Optional<ItemStack> optional1 = slot.tryRemove(carried.getCount(), me.earthme.lophine.utils.ShulkerBoxesUtil.getItemMaxCount(carried2) - carried2.getCount(), player); // Lophine - Stackable ShulkerBoxes
|
||||
+ Optional<ItemStack> optional1 = slot.tryRemove(carried.getCount(), org.leavesmc.leaves.util.ItemOverstackUtils.getItemStackMaxCount(carried2) - carried2.getCount(), player); // Leaves - item over-stack util
|
||||
optional1.ifPresent(itemStack2 -> {
|
||||
carried2.grow(itemStack2.getCount());
|
||||
slot.onTake(player, itemStack2);
|
||||
@@ -603,7 +611,7 @@ public abstract class AbstractContainerMenu {
|
||||
@@ -625,7 +632,7 @@ public abstract class AbstractContainerMenu {
|
||||
Slot slot2 = this.slots.get(slotId);
|
||||
if (slot2.hasItem()) {
|
||||
ItemStack itemStack = slot2.getItem();
|
||||
- this.setCarried(itemStack.copyWithCount(itemStack.getMaxStackSize()));
|
||||
+ this.setCarried(itemStack.copyWithCount(me.earthme.lophine.utils.ShulkerBoxesUtil.getItemMaxCount(itemStack))); // Lophine - Stackable ShulkerBoxes
|
||||
+ this.setCarried(itemStack.copyWithCount(org.leavesmc.leaves.util.ItemOverstackUtils.getItemStackMaxCount(itemStack))); // Leaves - item over-stack util
|
||||
}
|
||||
} else if (clickType == ClickType.THROW && this.getCarried().isEmpty() && slotId >= 0) {
|
||||
Slot slot2 = this.slots.get(slotId);
|
||||
@@ -634,15 +642,15 @@ public abstract class AbstractContainerMenu {
|
||||
@@ -656,15 +663,15 @@ public abstract class AbstractContainerMenu {
|
||||
int maxStackSize = button == 0 ? 1 : -1;
|
||||
|
||||
for (int i3 = 0; i3 < 2; i3++) {
|
||||
- for (int i4 = count; i4 >= 0 && i4 < this.slots.size() && itemStack.getCount() < itemStack.getMaxStackSize(); i4 += maxStackSize) {
|
||||
+ for (int i4 = count; i4 >= 0 && i4 < this.slots.size() && itemStack.getCount() < me.earthme.lophine.utils.ShulkerBoxesUtil.getItemMaxCount(itemStack); i4 += maxStackSize) { // Lophine - Stackable ShulkerBoxes
|
||||
+ for (int i4 = count; i4 >= 0 && i4 < this.slots.size() && itemStack.getCount() < org.leavesmc.leaves.util.ItemOverstackUtils.getItemStackMaxCount(itemStack); i4 += maxStackSize) { // Leaves - item over-stack util
|
||||
Slot slot3 = this.slots.get(i4);
|
||||
if (slot3.hasItem()
|
||||
&& canItemQuickReplace(slot3, itemStack, true)
|
||||
@@ -310,49 +287,49 @@ index a9cf0ef95b706f05060bbcd2a4f5a03c60f783d2..6c5a01b065c7daa37219a8ab2a471d93
|
||||
ItemStack item1 = slot3.getItem();
|
||||
- if (i3 != 0 || item1.getCount() != item1.getMaxStackSize()) {
|
||||
- ItemStack itemStack1 = slot3.safeTake(item1.getCount(), itemStack.getMaxStackSize() - itemStack.getCount(), player);
|
||||
+ if (i3 != 0 || item1.getCount() != me.earthme.lophine.utils.ShulkerBoxesUtil.getItemMaxCount(item1)) { // Lophine - stackable shulker boxes
|
||||
+ ItemStack itemStack1 = slot3.safeTake(item1.getCount(), me.earthme.lophine.utils.ShulkerBoxesUtil.getItemMaxCount(itemStack) - itemStack.getCount(), player); // Lophine - stackable shulker boxes
|
||||
+ if (i3 != 0 || item1.getCount() != org.leavesmc.leaves.util.ItemOverstackUtils.getItemStackMaxCount(item1)) { // Leaves - item over-stack util
|
||||
+ ItemStack itemStack1 = slot3.safeTake(item1.getCount(), org.leavesmc.leaves.util.ItemOverstackUtils.getItemStackMaxCount(itemStack) - itemStack.getCount(), player); // Leaves - item over-stack util
|
||||
itemStack.grow(itemStack1.getCount());
|
||||
}
|
||||
}
|
||||
@@ -744,7 +752,7 @@ public abstract class AbstractContainerMenu {
|
||||
@@ -766,7 +773,7 @@ public abstract class AbstractContainerMenu {
|
||||
i = endIndex - 1;
|
||||
}
|
||||
|
||||
- if (stack.isStackable()) {
|
||||
+ if (me.earthme.lophine.utils.ShulkerBoxesUtil.isStackable(stack)) { // Lophine - Stackable ShulkerBoxes
|
||||
+ if (org.leavesmc.leaves.util.ItemOverstackUtils.isStackable(stack)) { // Leaves - item over-stack util
|
||||
while (!stack.isEmpty() && (reverseDirection ? i >= startIndex : i < endIndex)) {
|
||||
Slot slot = this.slots.get(i);
|
||||
ItemStack item = slot.getItem();
|
||||
@@ -845,7 +853,7 @@ public abstract class AbstractContainerMenu {
|
||||
@@ -867,7 +874,7 @@ public abstract class AbstractContainerMenu {
|
||||
public static boolean canItemQuickReplace(@Nullable Slot slot, ItemStack stack, boolean stackSizeMatters) {
|
||||
boolean flag = slot == null || !slot.hasItem();
|
||||
return !flag && ItemStack.isSameItemSameComponents(stack, slot.getItem())
|
||||
- ? slot.getItem().getCount() + (stackSizeMatters ? 0 : stack.getCount()) <= stack.getMaxStackSize()
|
||||
+ ? slot.getItem().getCount() + (stackSizeMatters ? 0 : stack.getCount()) <= me.earthme.lophine.utils.ShulkerBoxesUtil.getItemMaxCount(stack) // Lophine - Stackable ShulkerBoxes
|
||||
+ ? slot.getItem().getCount() + (stackSizeMatters ? 0 : stack.getCount()) <= org.leavesmc.leaves.util.ItemOverstackUtils.getItemStackMaxCount(stack) // Leaves - item over-stack util
|
||||
: flag;
|
||||
}
|
||||
|
||||
@@ -853,7 +861,7 @@ public abstract class AbstractContainerMenu {
|
||||
@@ -875,7 +882,7 @@ public abstract class AbstractContainerMenu {
|
||||
return switch (type) {
|
||||
case 0 -> Mth.floor((float)stack.getCount() / slots.size());
|
||||
case 1 -> 1;
|
||||
- case 2 -> stack.getMaxStackSize();
|
||||
+ case 2 -> me.earthme.lophine.utils.ShulkerBoxesUtil.getItemMaxCount(stack); // Lophine - Stackable ShulkerBoxes
|
||||
+ case 2 -> org.leavesmc.leaves.util.ItemOverstackUtils.getItemStackMaxCount(stack); // Leaves - item over-stack util
|
||||
default -> stack.getCount();
|
||||
};
|
||||
}
|
||||
@@ -875,7 +883,7 @@ public abstract class AbstractContainerMenu {
|
||||
@@ -898,7 +905,7 @@ public abstract class AbstractContainerMenu {
|
||||
for (int i = 0; i < container.getContainerSize(); i++) {
|
||||
ItemStack item = container.getItem(i);
|
||||
if (!item.isEmpty()) {
|
||||
- f += (float)item.getCount() / container.getMaxStackSize(item);
|
||||
+ f += (float)item.getCount() / container.getMaxStackLeaves(item); // Leaves - stackable shulker boxes
|
||||
+ f += Math.clamp((float) item.getCount() / container.getMaxStackSize(item), 0f, 1f); // Leaves - item over-stack util
|
||||
}
|
||||
}
|
||||
|
||||
diff --git a/net/minecraft/world/inventory/MerchantContainer.java b/net/minecraft/world/inventory/MerchantContainer.java
|
||||
index 1e5dfb1f9e371fa23cdfa9280797aa0e183d4cd2..cf87267130c0aebd38206556261929d6f6383bc9 100644
|
||||
index 1e5dfb1f9e371fa23cdfa9280797aa0e183d4cd2..1a593d5bfc4574fd071604105422cd1d395648b8 100644
|
||||
--- a/net/minecraft/world/inventory/MerchantContainer.java
|
||||
+++ b/net/minecraft/world/inventory/MerchantContainer.java
|
||||
@@ -109,7 +109,7 @@ public class MerchantContainer implements Container {
|
||||
@@ -360,12 +337,12 @@ index 1e5dfb1f9e371fa23cdfa9280797aa0e183d4cd2..cf87267130c0aebd38206556261929d6
|
||||
public void setItem(int index, ItemStack stack) {
|
||||
this.itemStacks.set(index, stack);
|
||||
- stack.limitSize(this.getMaxStackSize(stack));
|
||||
+ stack.limitSize(this.getMaxStackLeaves(stack)); // Leaves - stackable shulker boxes
|
||||
+ stack.limitSize(this.getMaxStackLeaves(stack)); // Leaves - item over-stack util
|
||||
if (this.isPaymentSlot(index)) {
|
||||
this.updateSellItem();
|
||||
}
|
||||
diff --git a/net/minecraft/world/inventory/Slot.java b/net/minecraft/world/inventory/Slot.java
|
||||
index 5ceb8964476b40db4511bec91ff13c4f522a1357..170353b8d1d330e54d9836e78ba2d41d023e0f51 100644
|
||||
index 5ceb8964476b40db4511bec91ff13c4f522a1357..f416d7976e41a06b5a8be93485eaa233ffceda5c 100644
|
||||
--- a/net/minecraft/world/inventory/Slot.java
|
||||
+++ b/net/minecraft/world/inventory/Slot.java
|
||||
@@ -75,7 +75,7 @@ public class Slot {
|
||||
@@ -373,41 +350,41 @@ index 5ceb8964476b40db4511bec91ff13c4f522a1357..170353b8d1d330e54d9836e78ba2d41d
|
||||
|
||||
public int getMaxStackSize(ItemStack stack) {
|
||||
- return Math.min(this.getMaxStackSize(), stack.getMaxStackSize());
|
||||
+ return Math.min(this.getMaxStackSize(), me.earthme.lophine.utils.ShulkerBoxesUtil.getItemMaxCount(stack)); // Lophine - Stackable ShulkerBoxes
|
||||
+ return Math.min(this.getMaxStackSize(), org.leavesmc.leaves.util.ItemOverstackUtils.getItemStackMaxCount(stack)); // Leaves - item over-stack util
|
||||
}
|
||||
|
||||
@Nullable
|
||||
diff --git a/net/minecraft/world/item/ItemStack.java b/net/minecraft/world/item/ItemStack.java
|
||||
index 09455150810e4fb6e4637fdb9e2e8a30fe0bc247..5d2571d0199a25087954b4e67bc7551f282c6bd2 100644
|
||||
index 33c3a5e39e8e1aa023baab76ef94f8aed481480e..a27e1d53abc2d8b8912226add33510067e10ed52 100644
|
||||
--- a/net/minecraft/world/item/ItemStack.java
|
||||
+++ b/net/minecraft/world/item/ItemStack.java
|
||||
@@ -165,7 +165,7 @@ public final class ItemStack implements DataComponentHolder {
|
||||
@@ -166,7 +166,7 @@ public final class ItemStack implements DataComponentHolder, ChangePublisher<net
|
||||
@Deprecated
|
||||
@Nullable
|
||||
private Item item;
|
||||
- PatchedDataComponentMap components;
|
||||
+ public PatchedDataComponentMap components; // Leaves - stackable shulker boxes
|
||||
+ public PatchedDataComponentMap components; // Leaves - item over-stack util
|
||||
@Nullable
|
||||
private Entity entityRepresentation;
|
||||
|
||||
@@ -192,7 +192,8 @@ public final class ItemStack implements DataComponentHolder {
|
||||
@@ -193,7 +193,8 @@ public final class ItemStack implements DataComponentHolder, ChangePublisher<net
|
||||
} else {
|
||||
Holder<Item> holder = Item.STREAM_CODEC.decode(buffer);
|
||||
DataComponentPatch dataComponentPatch = codec.decode(buffer);
|
||||
- return new ItemStack(holder, varInt, dataComponentPatch);
|
||||
+ ItemStack itemStack = new ItemStack(holder, varInt, dataComponentPatch);
|
||||
+ return me.earthme.lophine.utils.ShulkerBoxesUtil.decodeMaxStackSize(itemStack);
|
||||
+ return org.leavesmc.leaves.util.ItemOverstackUtils.decodeMaxStackSize(itemStack);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -201,13 +202,15 @@ public final class ItemStack implements DataComponentHolder {
|
||||
@@ -202,13 +203,15 @@ public final class ItemStack implements DataComponentHolder, ChangePublisher<net
|
||||
if (value.isEmpty() || value.getItem() == null) { // CraftBukkit - NPE fix itemstack.getItem()
|
||||
buffer.writeVarInt(0);
|
||||
} else {
|
||||
- buffer.writeVarInt(io.papermc.paper.util.sanitizer.ItemComponentSanitizer.sanitizeCount(io.papermc.paper.util.sanitizer.ItemObfuscationSession.currentSession(), value, value.getCount())); // Paper - potentially sanitize count
|
||||
- Item.STREAM_CODEC.encode(buffer, value.getItemHolder());
|
||||
+ // Leaves start - stackable shulker boxes
|
||||
+ final ItemStack itemStack = me.earthme.lophine.utils.ShulkerBoxesUtil.encodeMaxStackSize(value.copy());
|
||||
+ // Leaves start - item over-stack util
|
||||
+ final ItemStack itemStack = org.leavesmc.leaves.util.ItemOverstackUtils.encodeMaxStackSize(value.copy());
|
||||
+ buffer.writeVarInt(io.papermc.paper.util.sanitizer.ItemComponentSanitizer.sanitizeCount(io.papermc.paper.util.sanitizer.ItemObfuscationSession.currentSession(), itemStack, itemStack.getCount())); // Paper - potentially sanitize count
|
||||
+ Item.STREAM_CODEC.encode(buffer, itemStack.getItemHolder());
|
||||
// Paper start - adventure; conditionally render translatable components
|
||||
@@ -420,50 +397,17 @@ index 09455150810e4fb6e4637fdb9e2e8a30fe0bc247..5d2571d0199a25087954b4e67bc7551f
|
||||
} finally {
|
||||
net.minecraft.network.chat.ComponentSerialization.DONT_RENDER_TRANSLATABLES.set(prev);
|
||||
}
|
||||
@@ -302,7 +305,7 @@ public final class ItemStack implements DataComponentHolder {
|
||||
@@ -303,7 +306,7 @@ public final class ItemStack implements DataComponentHolder, ChangePublisher<net
|
||||
|
||||
for (ItemStack itemStack : itemContainerContents.nonEmptyItems()) {
|
||||
int count = itemStack.getCount();
|
||||
- int maxStackSize = itemStack.getMaxStackSize();
|
||||
+ int maxStackSize = me.earthme.lophine.utils.ShulkerBoxesUtil.getItemMaxCount(itemStack); // Leaves - stackable shulker boxes
|
||||
+ int maxStackSize = org.leavesmc.leaves.util.ItemOverstackUtils.getItemStackMaxCount(itemStack); // Leaves - item over-stack util
|
||||
if (count > maxStackSize) {
|
||||
return DataResult.error(() -> "Item stack with count of " + count + " was larger than maximum: " + maxStackSize);
|
||||
}
|
||||
diff --git a/net/minecraft/world/level/block/AbstractCauldronBlock.java b/net/minecraft/world/level/block/AbstractCauldronBlock.java
|
||||
index ad3f32888afd8b5f0038445a1b0fcc8cacec9fe2..ee0315ff52914666e3e3cfc38f726512e3301986 100644
|
||||
--- a/net/minecraft/world/level/block/AbstractCauldronBlock.java
|
||||
+++ b/net/minecraft/world/level/block/AbstractCauldronBlock.java
|
||||
@@ -62,9 +62,27 @@ public abstract class AbstractCauldronBlock extends Block {
|
||||
ItemStack stack, BlockState state, Level level, BlockPos pos, Player player, InteractionHand hand, BlockHitResult hitResult
|
||||
) {
|
||||
CauldronInteraction cauldronInteraction = this.interactions.map().get(stack.getItem());
|
||||
- return cauldronInteraction.interact(state, level, pos, player, hand, stack, hitResult.getDirection()); // Paper - pass hit direction
|
||||
+ return wrapInteractor(cauldronInteraction, state, level, pos, player, hand, stack, hitResult.getDirection()); // Paper - pass hit direction // Leaves - stackable shulker boxes
|
||||
}
|
||||
|
||||
+ // Leaves start - stackable shulker boxes
|
||||
+ private InteractionResult wrapInteractor(CauldronInteraction cauldronBehavior, BlockState blockState, Level world, BlockPos blockPos, Player playerEntity, InteractionHand hand, ItemStack itemStack, net.minecraft.core.Direction hitDirection) {
|
||||
+ int count = -1;
|
||||
+ if (me.earthme.lophine.utils.ShulkerBoxesUtil.shouldCheck() && itemStack.getItem() instanceof net.minecraft.world.item.BlockItem bi &&
|
||||
+ bi.getBlock() instanceof ShulkerBoxBlock) {
|
||||
+ count = itemStack.getCount();
|
||||
+ }
|
||||
+ InteractionResult result = cauldronBehavior.interact(blockState, world, blockPos, playerEntity, hand, itemStack, hitDirection);
|
||||
+ if (count > 0 && result.consumesAction()) {
|
||||
+ ItemStack current = playerEntity.getItemInHand(hand);
|
||||
+ if (current.getItem() instanceof net.minecraft.world.item.BlockItem bi && bi.getBlock() instanceof ShulkerBoxBlock) {
|
||||
+ current.setCount(count);
|
||||
+ }
|
||||
+ }
|
||||
+ return result;
|
||||
+ }
|
||||
+ // Leaves end - stackable shulker boxes
|
||||
+
|
||||
@Override
|
||||
protected VoxelShape getShape(BlockState state, BlockGetter level, BlockPos pos, CollisionContext context) {
|
||||
return SHAPE;
|
||||
diff --git a/net/minecraft/world/level/block/CrafterBlock.java b/net/minecraft/world/level/block/CrafterBlock.java
|
||||
index 38b03c7b02bdfc579e5e126c12de3d878e26d188..33e24f2c3b63b2d3b55dfae2f2e55869abeed055 100644
|
||||
index 38b03c7b02bdfc579e5e126c12de3d878e26d188..caf31218a1aff84367da126ee1dc46b8c8a1e5d1 100644
|
||||
--- a/net/minecraft/world/level/block/CrafterBlock.java
|
||||
+++ b/net/minecraft/world/level/block/CrafterBlock.java
|
||||
@@ -192,7 +192,7 @@ public class CrafterBlock extends BaseEntityBlock {
|
||||
@@ -471,58 +415,58 @@ index 38b03c7b02bdfc579e5e126c12de3d878e26d188..33e24f2c3b63b2d3b55dfae2f2e55869
|
||||
Container containerAt = HopperBlockEntity.getContainerAt(level, pos.relative(direction));
|
||||
ItemStack itemStack = stack.copy();
|
||||
- if (containerAt != null && (containerAt instanceof CrafterBlockEntity || stack.getCount() > containerAt.getMaxStackSize(stack))) {
|
||||
+ if (containerAt != null && (containerAt instanceof CrafterBlockEntity || stack.getCount() > containerAt.getMaxStackLeaves(stack))) { // Leaves - stackable shulker boxes
|
||||
+ if (containerAt != null && (containerAt instanceof CrafterBlockEntity || stack.getCount() > containerAt.getMaxStackLeaves(stack))) { // Leaves - item over-stack util
|
||||
// CraftBukkit start - InventoryMoveItemEvent
|
||||
org.bukkit.craftbukkit.inventory.CraftItemStack oitemstack = org.bukkit.craftbukkit.inventory.CraftItemStack.asCraftMirror(itemStack);
|
||||
|
||||
diff --git a/net/minecraft/world/level/block/entity/AbstractFurnaceBlockEntity.java b/net/minecraft/world/level/block/entity/AbstractFurnaceBlockEntity.java
|
||||
index c5b3b5e5f621f8db152aa190374ae0fe567d6828..0ca27b83151c3e7be3d681aa087fa72fb5bef0c3 100644
|
||||
index d7a211220d5eba7e34b9083f06f39e9527962599..cef2d6d9a42ae36d6e2a0b31685a7738d2a400cf 100644
|
||||
--- a/net/minecraft/world/level/block/entity/AbstractFurnaceBlockEntity.java
|
||||
+++ b/net/minecraft/world/level/block/entity/AbstractFurnaceBlockEntity.java
|
||||
@@ -409,7 +409,7 @@ public abstract class AbstractFurnaceBlockEntity extends BaseContainerBlockEntit
|
||||
@@ -411,7 +411,7 @@ public abstract class AbstractFurnaceBlockEntity extends BaseContainerBlockEntit
|
||||
ItemStack itemStack = this.items.get(index);
|
||||
boolean flag = !stack.isEmpty() && ItemStack.isSameItemSameComponents(itemStack, stack);
|
||||
this.items.set(index, stack);
|
||||
- stack.limitSize(this.getMaxStackSize(stack));
|
||||
+ stack.limitSize(this.getMaxStackLeaves(stack)); // Leaves - stackable shulker boxes
|
||||
+ stack.limitSize(this.getMaxStackLeaves(stack)); // Leaves - item over-stack util
|
||||
if (index == 0 && !flag && this.level instanceof ServerLevel serverLevel) {
|
||||
this.cookingTotalTime = getTotalCookTime(serverLevel, this, this.recipeType, this.cookSpeedMultiplier); // Paper - cook speed multiplier API
|
||||
this.cookingTimer = 0;
|
||||
diff --git a/net/minecraft/world/level/block/entity/BaseContainerBlockEntity.java b/net/minecraft/world/level/block/entity/BaseContainerBlockEntity.java
|
||||
index c63370fd458fb4f7190b79b1a8174fcc92d88f9c..4cca3fbbb93bb76d5d501dfad6997d909211809e 100644
|
||||
index 10b2cc20bbf3b70e4e09dbbe14b90506e6366158..1535bb8cec8692e8d26eaff090288f1b8f75c253 100644
|
||||
--- a/net/minecraft/world/level/block/entity/BaseContainerBlockEntity.java
|
||||
+++ b/net/minecraft/world/level/block/entity/BaseContainerBlockEntity.java
|
||||
@@ -148,7 +148,7 @@ public abstract class BaseContainerBlockEntity extends BlockEntity implements Co
|
||||
@@ -155,7 +155,7 @@ public abstract class BaseContainerBlockEntity extends BlockEntity implements Co
|
||||
@Override
|
||||
public void setItem(int slot, ItemStack stack) {
|
||||
this.getItems().set(slot, stack);
|
||||
- stack.limitSize(this.getMaxStackSize(stack));
|
||||
+ stack.limitSize(this.getMaxStackLeaves(stack)); // Leaves - stackable shulker boxes
|
||||
+ stack.limitSize(this.getMaxStackLeaves(stack)); // Leaves - item over-stack util
|
||||
this.setChanged();
|
||||
}
|
||||
|
||||
diff --git a/net/minecraft/world/level/block/entity/HopperBlockEntity.java b/net/minecraft/world/level/block/entity/HopperBlockEntity.java
|
||||
index 6f5a47a3e9452f25b318e4a7a628e917da99c96f..52032c6bbc7e1c37a50d9b7334f1830896cb462d 100644
|
||||
index b78d23ab22a74db2e16707424ed14f5760edf5b4..486181bf59f0863a7b60749a0b75b5ac79e2e470 100644
|
||||
--- a/net/minecraft/world/level/block/entity/HopperBlockEntity.java
|
||||
+++ b/net/minecraft/world/level/block/entity/HopperBlockEntity.java
|
||||
@@ -123,7 +123,7 @@ public class HopperBlockEntity extends RandomizableContainerBlockEntity implemen
|
||||
@@ -145,7 +145,7 @@ public class HopperBlockEntity extends RandomizableContainerBlockEntity implemen
|
||||
public void setItem(int index, ItemStack stack) {
|
||||
this.unpackLootTable(null);
|
||||
this.getItems().set(index, stack);
|
||||
- stack.limitSize(this.getMaxStackSize(stack));
|
||||
+ stack.limitSize(this.getMaxStackLeaves(stack)); // Leaves - stackable shulker boxes
|
||||
+ stack.limitSize(this.getMaxStackLeaves(stack)); // Leaves - item over-stack util
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -690,9 +690,9 @@ public class HopperBlockEntity extends RandomizableContainerBlockEntity implemen
|
||||
@@ -742,9 +742,9 @@ public class HopperBlockEntity extends RandomizableContainerBlockEntity implemen
|
||||
if (item.isEmpty()) {
|
||||
// Spigot start - SPIGOT-6693, SimpleContainer#setItem
|
||||
ItemStack leftover = ItemStack.EMPTY; // Paper - Make hoppers respect inventory max stack size
|
||||
- if (!stack.isEmpty() && stack.getCount() > destination.getMaxStackSize()) {
|
||||
+ if (!stack.isEmpty() && (stack.getCount() > destination.getMaxStackSize() || stack.getCount() > stack.getMaxStackSize())) {
|
||||
+ if (!stack.isEmpty() && (stack.getCount() > destination.getMaxStackSize() || stack.getCount() > stack.getMaxStackSize())) { // Leaves - item over-stack util
|
||||
leftover = stack; // Paper - Make hoppers respect inventory max stack size
|
||||
- stack = stack.split(destination.getMaxStackSize());
|
||||
+ stack = stack.split(Math.min(destination.getMaxStackSize(), stack.getMaxStackSize()));
|
||||
+ stack = stack.split(Math.min(destination.getMaxStackSize(), stack.getMaxStackSize())); // Leaves - item over-stack util
|
||||
}
|
||||
// Spigot end
|
||||
IGNORE_TILE_UPDATES.set(Boolean.TRUE); // Paper - Perf: Optimize Hoppers // Folia - region threading
|
||||
//IGNORE_TILE_UPDATES.set(Boolean.TRUE); // Paper - Perf: Optimize Hoppers // Folia - region threading // Luminol - Use vanilla hopper logic
|
||||
+21
@@ -0,0 +1,21 @@
|
||||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: Helvetica Volubi <suisuroru@blue-millennium.fun>
|
||||
Date: Mon, 7 Jul 2025 15:33:57 +0800
|
||||
Subject: [PATCH] Leaves: Old Explosion Damage Calculator
|
||||
|
||||
Co-authored by: MC_XiaoHei <xor7xiaohei@gmail.com>
|
||||
As a part of : Leaves (https://github.com/LeavesMC/Leaves/blob/3e96b237749a960f297f211d439ffc9ea7fd2381/leaves-server/minecraft-patches/features/0134-Old-wet-tnt-explode-behavior.patch)
|
||||
Licensed under: GPL-3.0 (https://www.gnu.org/licenses/gpl-3.0.html)
|
||||
|
||||
diff --git a/net/minecraft/world/level/ServerExplosion.java b/net/minecraft/world/level/ServerExplosion.java
|
||||
index b3e897a4afee742b5055d652ad7047a660d8ea04..cf8869fac003c73024634078b2f7b36cddda640e 100644
|
||||
--- a/net/minecraft/world/level/ServerExplosion.java
|
||||
+++ b/net/minecraft/world/level/ServerExplosion.java
|
||||
@@ -729,6 +729,7 @@ public class ServerExplosion implements Explosion {
|
||||
public boolean shouldAffectBlocklikeEntities() {
|
||||
boolean _boolean = this.level.getGameRules().getBoolean(GameRules.RULE_MOBGRIEFING);
|
||||
boolean flag = this.source == null || this.source.getType() != EntityType.BREEZE_WIND_CHARGE && this.source.getType() != EntityType.WIND_CHARGE;
|
||||
+ if (fun.bm.lophine.config.modules.misc.OldFeatureConfig.oldExplosionDamageCalculator) flag = flag && (this.source == null || !this.source.isInWater()); // Leaves - Old MC TNT wet explosion no item damage
|
||||
return _boolean ? flag : this.blockInteraction.shouldAffectBlocklikeEntities() && flag;
|
||||
}
|
||||
|
||||
@@ -1,174 +0,0 @@
|
||||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: Helvetica Volubi <suisuroru@blue-millennium.fun>
|
||||
Date: Sat, 14 Jun 2025 05:32:51 +0800
|
||||
Subject: [PATCH] Better ShulkerBox
|
||||
|
||||
You can open shulker box with shift & right click
|
||||
|
||||
diff --git a/net/minecraft/server/level/ServerPlayer.java b/net/minecraft/server/level/ServerPlayer.java
|
||||
index 1764875b9024569671bf2be537afb8b492f79102..0ae350d7e4b63abad1666c84df6925c71b73a013 100644
|
||||
--- a/net/minecraft/server/level/ServerPlayer.java
|
||||
+++ b/net/minecraft/server/level/ServerPlayer.java
|
||||
@@ -2351,6 +2351,7 @@ public class ServerPlayer extends Player implements ca.spottedleaf.moonrise.patc
|
||||
this.containerMenu.removed(this);
|
||||
this.inventoryMenu.transferState(this.containerMenu);
|
||||
this.containerMenu = this.inventoryMenu;
|
||||
+ this.shulkerOpen = false;
|
||||
}
|
||||
|
||||
@Override
|
||||
diff --git a/net/minecraft/world/entity/player/Inventory.java b/net/minecraft/world/entity/player/Inventory.java
|
||||
index 9fbd605df26d388c01d2ed9ca6a6138651e7f9a7..e8141cb25d6b9a433890e2cded188c2eb5bcf755 100644
|
||||
--- a/net/minecraft/world/entity/player/Inventory.java
|
||||
+++ b/net/minecraft/world/entity/player/Inventory.java
|
||||
@@ -470,6 +470,16 @@ public class Inventory implements Container, Nameable {
|
||||
if (equipmentSlot != null) {
|
||||
this.equipment.set(equipmentSlot, stack);
|
||||
}
|
||||
+
|
||||
+ // Lophine start - better shulker box
|
||||
+ if (me.earthme.lophine.config.modules.misc.ContainerExpansionConfig.betterShulker && !this.player.pendingClosingShulker) {
|
||||
+ boolean isMainHand = index == selected;
|
||||
+ boolean isOffHand = index == 40;
|
||||
+ if (isMainHand || isOffHand) {
|
||||
+ me.earthme.lophine.utils.ShulkerBoxesUtil.inventoryCallBack(isMainHand, this.player);
|
||||
+ }
|
||||
+ }
|
||||
+ // Lophine end - better shulker box
|
||||
}
|
||||
|
||||
public ListTag save(ListTag listTag) {
|
||||
diff --git a/net/minecraft/world/entity/player/Player.java b/net/minecraft/world/entity/player/Player.java
|
||||
index 27e335e01d8f09f05a4fea5f15407dbe3a4555cb..fbb9fee8223cc27125e4c0c94a7eb8acab5e9a13 100644
|
||||
--- a/net/minecraft/world/entity/player/Player.java
|
||||
+++ b/net/minecraft/world/entity/player/Player.java
|
||||
@@ -211,6 +211,11 @@ public abstract class Player extends LivingEntity {
|
||||
public boolean affectsSpawning = true; // Paper - Affects Spawning API
|
||||
public net.kyori.adventure.util.TriState flyingFallDamage = net.kyori.adventure.util.TriState.NOT_SET; // Paper - flying fall damage
|
||||
public int enderChestSlotCount = -1; // Purpur - Barrels and enderchests 6 rows
|
||||
+ // Lophine start - better shulker box
|
||||
+ public net.minecraft.world.InteractionHand shulkerHand;
|
||||
+ public boolean pendingClosingShulker = false;
|
||||
+ public boolean shulkerOpen = false;
|
||||
+ // Lophine end - better shulker box
|
||||
|
||||
// CraftBukkit start
|
||||
public boolean fauxSleeping;
|
||||
@@ -595,11 +600,13 @@ public abstract class Player extends LivingEntity {
|
||||
// Paper start - special close for unloaded inventory
|
||||
public void closeUnloadedInventory(org.bukkit.event.inventory.InventoryCloseEvent.Reason reason) {
|
||||
this.containerMenu = this.inventoryMenu;
|
||||
+ this.shulkerOpen = false;
|
||||
}
|
||||
// Paper end - special close for unloaded inventory
|
||||
|
||||
public void closeContainer() {
|
||||
this.containerMenu = this.inventoryMenu;
|
||||
+ this.shulkerOpen = false;
|
||||
}
|
||||
|
||||
protected void doCloseContainer() {
|
||||
diff --git a/net/minecraft/world/inventory/AbstractContainerMenu.java b/net/minecraft/world/inventory/AbstractContainerMenu.java
|
||||
index 6c5a01b065c7daa37219a8ab2a471d93e882e756..7b49b35e7a0b9872daceb620cf83b06ae61c3df9 100644
|
||||
--- a/net/minecraft/world/inventory/AbstractContainerMenu.java
|
||||
+++ b/net/minecraft/world/inventory/AbstractContainerMenu.java
|
||||
@@ -697,7 +697,7 @@ public abstract class AbstractContainerMenu {
|
||||
}
|
||||
}
|
||||
|
||||
- private static void dropOrPlaceInInventory(Player player, ItemStack stack) {
|
||||
+ public static void dropOrPlaceInInventory(Player player, ItemStack stack) { // Lophine - better shulker box
|
||||
boolean flag = !player.isAlive(); //player.isRemoved() && player.getRemovalReason() != Entity.RemovalReason.CHANGED_DIMENSION; // Luminol - Fix uncorrected death check of folia
|
||||
boolean flag1 = player instanceof ServerPlayer serverPlayer && serverPlayer.hasDisconnected();
|
||||
if (flag || flag1) {
|
||||
diff --git a/net/minecraft/world/inventory/ShulkerBoxMenu.java b/net/minecraft/world/inventory/ShulkerBoxMenu.java
|
||||
index 903025c659e6a9423224fe65973696405c69ec6a..545fc46416932db922dc1476b983ac8b9b9f3c7b 100644
|
||||
--- a/net/minecraft/world/inventory/ShulkerBoxMenu.java
|
||||
+++ b/net/minecraft/world/inventory/ShulkerBoxMenu.java
|
||||
@@ -53,7 +53,7 @@ public class ShulkerBoxMenu extends AbstractContainerMenu {
|
||||
|
||||
@Override
|
||||
public boolean stillValid(Player player) {
|
||||
- if (!this.checkReachable) return true; // CraftBukkit
|
||||
+ if (!this.checkReachable || player.shulkerOpen) return true; // CraftBukkit // Lophine - better shulker box
|
||||
return this.container.stillValid(player);
|
||||
}
|
||||
|
||||
diff --git a/net/minecraft/world/item/Item.java b/net/minecraft/world/item/Item.java
|
||||
index c52fb17d1e496a91223b7387cacb128c1865caee..86d43c4e81dfe9775400e09ae706d4e9cc640bd8 100644
|
||||
--- a/net/minecraft/world/item/Item.java
|
||||
+++ b/net/minecraft/world/item/Item.java
|
||||
@@ -187,6 +187,13 @@ public class Item implements FeatureElement, ItemLike {
|
||||
player.startUsingItem(hand);
|
||||
return InteractionResult.CONSUME;
|
||||
} else {
|
||||
+ // Lophine start - better shulker box
|
||||
+ if (me.earthme.lophine.config.modules.misc.ContainerExpansionConfig.betterShulker
|
||||
+ && player.isShiftKeyDown()
|
||||
+ && me.earthme.lophine.utils.ShulkerBoxesUtil.checkIfCanOpen(itemInHand)) {
|
||||
+ me.earthme.lophine.utils.ShulkerBoxesUtil.openShulkerBox(player, itemInHand, hand);
|
||||
+ }
|
||||
+ // Lophine end - better shulker box
|
||||
return InteractionResult.PASS;
|
||||
}
|
||||
}
|
||||
diff --git a/net/minecraft/world/item/component/ItemContainerContents.java b/net/minecraft/world/item/component/ItemContainerContents.java
|
||||
index ecf794f94177fc7b6df483516d920719fbc6fa43..4fda59a4c17009a0009f3d3c13258f4ffcb300c3 100644
|
||||
--- a/net/minecraft/world/item/component/ItemContainerContents.java
|
||||
+++ b/net/minecraft/world/item/component/ItemContainerContents.java
|
||||
@@ -33,7 +33,7 @@ public final class ItemContainerContents implements TooltipProvider {
|
||||
public final NonNullList<ItemStack> items;
|
||||
private final int hashCode;
|
||||
|
||||
- private ItemContainerContents(NonNullList<ItemStack> items) {
|
||||
+ public ItemContainerContents(NonNullList<ItemStack> items) { // Lophine - better shulker box
|
||||
if (items.size() > 256) {
|
||||
throw new IllegalArgumentException("Got " + items.size() + " items, but maximum is 256");
|
||||
} else {
|
||||
diff --git a/net/minecraft/world/level/block/entity/ShulkerBoxBlockEntity.java b/net/minecraft/world/level/block/entity/ShulkerBoxBlockEntity.java
|
||||
index 87ebdb6deb66662a38b3eec0dae27eaf859ecabb..e22114dbfbfd1619260a45929397c11c9356b8a0 100644
|
||||
--- a/net/minecraft/world/level/block/entity/ShulkerBoxBlockEntity.java
|
||||
+++ b/net/minecraft/world/level/block/entity/ShulkerBoxBlockEntity.java
|
||||
@@ -46,6 +46,11 @@ public class ShulkerBoxBlockEntity extends RandomizableContainerBlockEntity impl
|
||||
private ShulkerBoxBlockEntity.AnimationStatus animationStatus = ShulkerBoxBlockEntity.AnimationStatus.CLOSED;
|
||||
private float progress;
|
||||
private float progressOld;
|
||||
+ // Lophine start - better shulker box
|
||||
+ public boolean haveRealBlock = true;
|
||||
+ public Player createPlayer;
|
||||
+ public ItemStack item;
|
||||
+ // Lophine end - better shulker box
|
||||
@Nullable
|
||||
private final DyeColor color;
|
||||
|
||||
@@ -236,6 +241,15 @@ public class ShulkerBoxBlockEntity extends RandomizableContainerBlockEntity impl
|
||||
return Component.translatable("container.shulkerBox");
|
||||
}
|
||||
|
||||
+ // Lophine start - better shulker box
|
||||
+ public void setItem(int index, ItemStack stack) {
|
||||
+ super.setItem(index, stack);
|
||||
+ if (!createPlayer.pendingClosingShulker && !haveRealBlock && me.earthme.lophine.config.modules.misc.ContainerExpansionConfig.betterShulker) {
|
||||
+ me.earthme.lophine.utils.ShulkerBoxesUtil.shulkerBoxEntityCallBack(this);
|
||||
+ }
|
||||
+ }
|
||||
+ // Lophine end - better shulker box
|
||||
+
|
||||
@Override
|
||||
protected void loadAdditional(CompoundTag tag, HolderLookup.Provider registries) {
|
||||
super.loadAdditional(tag, registries);
|
||||
@@ -258,12 +272,12 @@ public class ShulkerBoxBlockEntity extends RandomizableContainerBlockEntity impl
|
||||
}
|
||||
|
||||
@Override
|
||||
- protected NonNullList<ItemStack> getItems() {
|
||||
+ public NonNullList<ItemStack> getItems() { // Lophine - better shulker box
|
||||
return this.itemStacks;
|
||||
}
|
||||
|
||||
@Override
|
||||
- protected void setItems(NonNullList<ItemStack> items) {
|
||||
+ public void setItems(NonNullList<ItemStack> items) { // Lophine - better shulker box
|
||||
this.itemStacks = items;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,216 @@
|
||||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: Helvetica Volubi <suisuroru@blue-millennium.fun>
|
||||
Date: Fri, 11 Apr 2025 16:53:57 +0800
|
||||
Subject: [PATCH] Leaves: Old raid behavior
|
||||
|
||||
Co-authored by: huanli233 <392352840@qq.com>
|
||||
As a part of : Leaves (https://github.com/LeavesMC/Leaves/blob/79d9ef74c2684eb49060f07e1ab31c827326c9a5/leaves-server/minecraft-patches/features/0106-Old-raid-behavior.patch)
|
||||
Licensed under: GPL-3.0 (https://www.gnu.org/licenses/gpl-3.0.html)
|
||||
|
||||
diff --git a/net/minecraft/world/effect/BadOmenMobEffect.java b/net/minecraft/world/effect/BadOmenMobEffect.java
|
||||
index 80f17f33f670018240c854df589cf90cdeab6e70..cbfb4ae2dc30c2ee98ef37f44a95434a7968e320 100644
|
||||
--- a/net/minecraft/world/effect/BadOmenMobEffect.java
|
||||
+++ b/net/minecraft/world/effect/BadOmenMobEffect.java
|
||||
@@ -22,6 +22,11 @@ class BadOmenMobEffect extends MobEffect {
|
||||
&& !serverPlayer.isSpectator()
|
||||
&& level.getDifficulty() != Difficulty.PEACEFUL
|
||||
&& level.isVillage(serverPlayer.blockPosition())) {
|
||||
+ // Leaves start - Revert raid changes
|
||||
+ if (fun.bm.lophine.config.modules.misc.OldFeatureConfig.oldRaidBehavior) {
|
||||
+ return level.getRaids().createOrExtendRaid(serverPlayer, serverPlayer.blockPosition()) == null;
|
||||
+ }
|
||||
+ // Leaves end - Revert raid changes
|
||||
Raid raidAt = level.getRaidAt(serverPlayer.blockPosition());
|
||||
if (raidAt == null || raidAt.getRaidOmenLevel() < raidAt.getMaxRaidOmenLevel()) {
|
||||
serverPlayer.addEffect(new MobEffectInstance(MobEffects.RAID_OMEN, 600, amplifier));
|
||||
diff --git a/net/minecraft/world/entity/raid/Raid.java b/net/minecraft/world/entity/raid/Raid.java
|
||||
index 1910bff3f08147bfd63703097e30b9f88c84e337..5188ae86aff3051d45fe5e7d2fcc51233b7074d4 100644
|
||||
--- a/net/minecraft/world/entity/raid/Raid.java
|
||||
+++ b/net/minecraft/world/entity/raid/Raid.java
|
||||
@@ -263,7 +263,7 @@ public class Raid {
|
||||
}
|
||||
|
||||
public boolean absorbRaidOmen(ServerPlayer player) {
|
||||
- MobEffectInstance effect = player.getEffect(MobEffects.RAID_OMEN);
|
||||
+ MobEffectInstance effect = player.getEffect(fun.bm.lophine.config.modules.misc.OldFeatureConfig.oldRaidBehavior ? MobEffects.BAD_OMEN : MobEffects.RAID_OMEN); // Leaves - old Raid Behavior
|
||||
if (effect == null) {
|
||||
return false;
|
||||
} else {
|
||||
@@ -340,7 +340,13 @@ public class Raid {
|
||||
}
|
||||
|
||||
if (flag1) {
|
||||
- this.waveSpawnPos = this.getValidSpawnPos(level);
|
||||
+ // Leaves Start - old FindSpawnPosition
|
||||
+ if (fun.bm.lophine.config.modules.misc.OldFeatureConfig.oldRaidBehavior) {
|
||||
+ this.waveSpawnPos = this.preCalcRavagerSpawnLocation(level, this.raidCooldownTicks < 100 ? 1 : 0);
|
||||
+ } else {
|
||||
+ this.waveSpawnPos = this.getValidSpawnPos(level);
|
||||
+ }
|
||||
+ // Leaves End - old FindSpawnPosition
|
||||
}
|
||||
|
||||
if (this.raidCooldownTicks == 300 || this.raidCooldownTicks % 20 == 0) {
|
||||
@@ -375,7 +381,14 @@ public class Raid {
|
||||
int i = 0;
|
||||
|
||||
while (this.shouldSpawnGroup()) {
|
||||
- BlockPos blockPos = this.waveSpawnPos.orElseGet(() -> this.findRandomSpawnPos(level, 20));
|
||||
+ // Leaves Start - old FindSpawnPosition
|
||||
+ BlockPos blockPos;
|
||||
+ if (fun.bm.lophine.config.modules.misc.OldFeatureConfig.oldRaidBehavior) {
|
||||
+ blockPos = this.getRavagerSpawnLocation(level, i, 20);
|
||||
+ } else {
|
||||
+ blockPos = this.waveSpawnPos.orElseGet(() -> this.findRandomSpawnPos(level, 20));
|
||||
+ }
|
||||
+ // Leaves End - old FindSpawnPosition
|
||||
if (blockPos != null) {
|
||||
this.started = true;
|
||||
this.spawnGroup(level, blockPos);
|
||||
@@ -387,7 +400,7 @@ public class Raid {
|
||||
i++;
|
||||
}
|
||||
|
||||
- if (i > 5) {
|
||||
+ if (i > (fun.bm.lophine.config.modules.misc.OldFeatureConfig.oldRaidBehavior ? 3 : 5)) { // Leaves - old FindSpawnPosition
|
||||
org.bukkit.craftbukkit.event.CraftEventFactory.callRaidStopEvent(level, this, org.bukkit.event.raid.RaidStopEvent.Reason.UNSPAWNABLE); // CraftBukkit
|
||||
this.stop();
|
||||
break;
|
||||
@@ -683,7 +696,7 @@ public class Raid {
|
||||
int i2 = this.center.getX() + Mth.floor(Mth.cos(f2) * 32.0F * f) + level.random.nextInt(3) * Mth.floor(f);
|
||||
int i3 = this.center.getZ() + Mth.floor(Mth.sin(f2) * 32.0F * f) + level.random.nextInt(3) * Mth.floor(f);
|
||||
int height = level.getHeight(Heightmap.Types.WORLD_SURFACE, i2, i3);
|
||||
- if (Mth.abs(height - this.center.getY()) <= 96) {
|
||||
+ if (fun.bm.lophine.config.modules.misc.OldFeatureConfig.oldRaidBehavior || Mth.abs(height - this.center.getY()) <= 96) { // Leaves - skippable
|
||||
mutableBlockPos.set(i2, height, i3);
|
||||
if (!level.isVillage(mutableBlockPos) || i <= 7) {
|
||||
int i4 = 10;
|
||||
@@ -702,6 +715,36 @@ public class Raid {
|
||||
return null;
|
||||
}
|
||||
|
||||
+ // Leaves Start - old FindSpawnPosition
|
||||
+ @Nullable
|
||||
+ private BlockPos findRandomSpawnPos(ServerLevel level, int n, int n2) {
|
||||
+ int n3 = 2 - n;
|
||||
+ BlockPos.MutableBlockPos mutableBlockPos = new BlockPos.MutableBlockPos();
|
||||
+ SpawnPlacementType spawnPlacementType = SpawnPlacements.getPlacementType(EntityType.RAVAGER);
|
||||
+ for (int i = 0; i < n2; ++i) {
|
||||
+ float f = level.random.nextFloat() * ((float)Math.PI * 2);
|
||||
+ int n4 = this.center.getX() + Mth.floor(Mth.cos(f) * 32.0f * (float)n3) + level.random.nextInt(5);
|
||||
+ int n5 = this.center.getZ() + Mth.floor(Mth.sin(f) * 32.0f * (float)n3) + level.random.nextInt(5);
|
||||
+ int n6 = level.getHeight(Heightmap.Types.WORLD_SURFACE, n4, n5);
|
||||
+ mutableBlockPos.set(n4, n6, n5);
|
||||
+ if (level.isVillage(mutableBlockPos) && n < 2) continue;
|
||||
+ if (!level.hasChunksAt(mutableBlockPos.getX() - 10, mutableBlockPos.getZ() - 10, mutableBlockPos.getX() + 10, mutableBlockPos.getZ() + 10) || !level.isPositionEntityTicking(mutableBlockPos) || !spawnPlacementType.isSpawnPositionOk(level, mutableBlockPos, EntityType.RAVAGER) && (!level.getBlockState((BlockPos)mutableBlockPos.below()).is(Blocks.SNOW) || !level.getBlockState(mutableBlockPos).isAir())) continue;
|
||||
+ return mutableBlockPos;
|
||||
+ }
|
||||
+ return null;
|
||||
+ }
|
||||
+
|
||||
+ private Optional<BlockPos> getValidSpawnPos(ServerLevel level, int n) {
|
||||
+ for (int i = 0; i < 3; ++i) {
|
||||
+ BlockPos blockPos = this.findRandomSpawnPos(level, n, 1);
|
||||
+ if (blockPos == null) continue;
|
||||
+ return Optional.of(blockPos);
|
||||
+ }
|
||||
+ return Optional.empty();
|
||||
+ }
|
||||
+ // Leaves End - old FindSpawnPosition
|
||||
+
|
||||
+
|
||||
private boolean addWaveMob(ServerLevel level, int wave, Raider raider) {
|
||||
// Folia start - make raids thread-safe
|
||||
if (!this.ownsRaid(level)) {
|
||||
@@ -864,4 +907,43 @@ public class Raid {
|
||||
this.spawnsPerWaveBeforeBonus = spawnsPerWaveBeforeBonus;
|
||||
}
|
||||
}
|
||||
+
|
||||
+ // Leaves start - old FindSpawnPosition
|
||||
+ private BlockPos getRavagerSpawnLocation(ServerLevel serverWorld, int proximity, int tries) {
|
||||
+ int i = proximity == 0 ? 2 : 2 - proximity;
|
||||
+ BlockPos.MutableBlockPos mutable = new BlockPos.MutableBlockPos();
|
||||
+ SpawnPlacementType spawnLocation = SpawnPlacements.getPlacementType(EntityType.RAVAGER);
|
||||
+
|
||||
+ for (int j = 0; j < tries; j++) {
|
||||
+ float f = serverWorld.random.nextFloat() * (float) (Math.PI * 2);
|
||||
+ int k = this.center.getX() + Mth.floor(Mth.cos(f) * 32.0F * (float) i + serverWorld.random.nextInt(5));
|
||||
+ int l = this.center.getZ() + Mth.floor(Mth.sin(f) * 32.0F * (float) i + serverWorld.random.nextInt(5));
|
||||
+ int m = serverWorld.getHeight(Heightmap.Types.WORLD_SURFACE, k, l);
|
||||
+ mutable.set(k, m, l);
|
||||
+
|
||||
+ if (serverWorld.isVillage(mutable) && proximity < 2) {
|
||||
+ continue;
|
||||
+ }
|
||||
+ if (serverWorld.hasChunksAt(mutable.getX() - 10, mutable.getZ() - 10, mutable.getX() + 10, mutable.getZ() + 10)
|
||||
+ && serverWorld.isPositionEntityTicking(mutable)
|
||||
+ && (spawnLocation.isSpawnPositionOk(serverWorld, mutable, EntityType.RAVAGER)
|
||||
+ || serverWorld.getBlockState(mutable.below()).is(Blocks.SNOW)
|
||||
+ && serverWorld.getBlockState(mutable).isAir())
|
||||
+ ) {
|
||||
+ return mutable;
|
||||
+ }
|
||||
+ }
|
||||
+ return null;
|
||||
+ }
|
||||
+
|
||||
+ private Optional<BlockPos> preCalcRavagerSpawnLocation(ServerLevel serverWorld, int proximity) {
|
||||
+ for (int i = 0; i < 3; i++) {
|
||||
+ BlockPos blockPos = this.getRavagerSpawnLocation(serverWorld, proximity, 1);
|
||||
+ if (blockPos != null) {
|
||||
+ return Optional.of(blockPos);
|
||||
+ }
|
||||
+ }
|
||||
+ return Optional.empty();
|
||||
+ }
|
||||
+ // Leaves end - old FindSpawnPosition
|
||||
}
|
||||
diff --git a/net/minecraft/world/entity/raid/Raider.java b/net/minecraft/world/entity/raid/Raider.java
|
||||
index 3e30989c99cd11c8c94d6fd194d2969e3a7efda6..b1f1bcf7f5ac002550077d5e1d4efd5c67d18063 100644
|
||||
--- a/net/minecraft/world/entity/raid/Raider.java
|
||||
+++ b/net/minecraft/world/entity/raid/Raider.java
|
||||
@@ -128,6 +128,43 @@ public abstract class Raider extends PatrollingMonster {
|
||||
|
||||
currentRaid.removeFromRaid(serverLevel, this, false);
|
||||
}
|
||||
+
|
||||
+ // Leaves start - Revert raid changes
|
||||
+ if (this.level() instanceof ServerLevel) {
|
||||
+ if (fun.bm.lophine.config.modules.misc.OldFeatureConfig.oldRaidBehavior && !this.hasRaid()) {
|
||||
+ ItemStack itemstack = this.getItemBySlot(EquipmentSlot.HEAD);
|
||||
+ net.minecraft.world.entity.player.Player entityhuman = null;
|
||||
+ if (entity instanceof net.minecraft.world.entity.player.Player player) {
|
||||
+ entityhuman = player;
|
||||
+ } else if (entity instanceof net.minecraft.world.entity.animal.wolf.Wolf wolf) {
|
||||
+ LivingEntity entityliving = wolf.getOwner();
|
||||
+ if (wolf.isTame() && entityliving instanceof net.minecraft.world.entity.player.Player player) {
|
||||
+ entityhuman = player;
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ if (entityhuman != null && !itemstack.isEmpty() && this.isCaptain()) {
|
||||
+ net.minecraft.world.effect.MobEffectInstance mobeffect = entityhuman.getEffect(net.minecraft.world.effect.MobEffects.BAD_OMEN);
|
||||
+ int i = 1;
|
||||
+
|
||||
+ if (mobeffect != null) {
|
||||
+ i += mobeffect.getAmplifier();
|
||||
+ entityhuman.removeEffectNoUpdate(net.minecraft.world.effect.MobEffects.BAD_OMEN);
|
||||
+ } else {
|
||||
+ --i;
|
||||
+ }
|
||||
+
|
||||
+ i = net.minecraft.util.Mth.clamp(i, 0, 4);
|
||||
+ net.minecraft.world.effect.MobEffectInstance mobeffect1 = new net.minecraft.world.effect.MobEffectInstance(net.minecraft.world.effect.MobEffects.BAD_OMEN, 120000, i, false, false, true);
|
||||
+
|
||||
+ if (!serverLevel.getGameRules().getBoolean(net.minecraft.world.level.GameRules.RULE_DISABLE_RAIDS)) {
|
||||
+ entityhuman.addEffect(mobeffect1, org.bukkit.event.entity.EntityPotionEffectEvent.Cause.PATROL_CAPTAIN); // CraftBukkit
|
||||
+ }
|
||||
+ this.setPatrolLeader(false);
|
||||
+ }
|
||||
+ }
|
||||
+ }
|
||||
+ // Leaves end - Revert raid changes
|
||||
}
|
||||
|
||||
super.die(cause);
|
||||
@@ -0,0 +1,44 @@
|
||||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: Helvetica Volubi <suisuroru@blue-millennium.fun>
|
||||
Date: Wed, 23 Jul 2025 20:28:40 +0800
|
||||
Subject: [PATCH] Leaves: Redstone Shears Wrench
|
||||
|
||||
Co-authored by: violetc <58360096+s-yh-china@users.noreply.github.com>
|
||||
As a part of : Leaves (https://github.com/LeavesMC/Leaves/blob/79d9ef74c2684eb49060f07e1ab31c827326c9a5/leaves-server/minecraft-patches/features/0009-Redstone-Shears-Wrench.patch)
|
||||
Licensed under: GPL-3.0 (https://www.gnu.org/licenses/gpl-3.0.html)
|
||||
|
||||
diff --git a/net/minecraft/world/item/BlockItem.java b/net/minecraft/world/item/BlockItem.java
|
||||
index 6db566adf2d0df1d26221eda04aa01738df6d3d2..24ebfdf98e343a7892af621d032c35819156deec 100644
|
||||
--- a/net/minecraft/world/item/BlockItem.java
|
||||
+++ b/net/minecraft/world/item/BlockItem.java
|
||||
@@ -56,6 +56,14 @@ public class BlockItem extends Item {
|
||||
if (blockPlaceContext == null) {
|
||||
return InteractionResult.FAIL;
|
||||
} else {
|
||||
+ // Leaves start - shears wrench
|
||||
+ if (org.leavesmc.leaves.util.ShearsWrenchUtil.shouldSkipPlace(context)) {
|
||||
+ if (context.getPlayer() != null) {
|
||||
+ context.getPlayer().containerMenu.forceHeldSlot(blockPlaceContext.getHand());
|
||||
+ }
|
||||
+ return InteractionResult.FAIL;
|
||||
+ }
|
||||
+ // Leaves end - shears wrench
|
||||
BlockState placementState = this.getPlacementState(blockPlaceContext);
|
||||
// CraftBukkit start - special case for handling block placement with water lilies and snow buckets
|
||||
org.bukkit.block.BlockState bukkitState = null;
|
||||
diff --git a/net/minecraft/world/item/ShearsItem.java b/net/minecraft/world/item/ShearsItem.java
|
||||
index 8cf3e51e12f9cf98836657e722edb23943f9e866..5fd5e9fd4e1a79dd1a9d62a5cf0c308805979420 100644
|
||||
--- a/net/minecraft/world/item/ShearsItem.java
|
||||
+++ b/net/minecraft/world/item/ShearsItem.java
|
||||
@@ -80,7 +80,10 @@ public class ShearsItem extends Item {
|
||||
|
||||
return InteractionResult.SUCCESS;
|
||||
} else {
|
||||
- return super.useOn(context);
|
||||
+ // Leaves start - shears wrench
|
||||
+ InteractionResult result = org.leavesmc.leaves.util.ShearsWrenchUtil.tryApplyRotate(context, blockState);
|
||||
+ return result == null ? super.useOn(context) : result;
|
||||
+ // Leaves end - shears wrench
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: Helvetica Volubi <suisuroru@blue-millennium.fun>
|
||||
Date: Sun, 3 Aug 2025 15:19:23 +0800
|
||||
Subject: [PATCH] Leaves: Servux Protocol
|
||||
|
||||
Co-authored by: violetc <58360096+s-yh-china@users.noreply.github.com>
|
||||
As a part of : Leaves (https://github.com/LeavesMC/Leaves/blob/ea91106ae57fc4cc14e2e0225009cf9919072f7f/leaves-server/minecraft-patches/features/0090-Servux-Protocol.patch)
|
||||
Licensed under: GPL-3.0 (https://www.gnu.org/licenses/gpl-3.0.html)
|
||||
|
||||
diff --git a/net/minecraft/server/ServerTickRateManager.java b/net/minecraft/server/ServerTickRateManager.java
|
||||
index d0d90a25a10bbecfffceee1992af88c60d14fd87..187e48e78e69c80b25710f193813335ec643957f 100644
|
||||
--- a/net/minecraft/server/ServerTickRateManager.java
|
||||
+++ b/net/minecraft/server/ServerTickRateManager.java
|
||||
@@ -134,4 +134,10 @@ public class ServerTickRateManager extends TickRateManager {
|
||||
player.connection.send(ClientboundTickingStatePacket.from(this));
|
||||
player.connection.send(ClientboundTickingStepPacket.from(this));
|
||||
}
|
||||
+
|
||||
+ // Leaves start - servux
|
||||
+ public long getRemainingSprintTicks() {
|
||||
+ return remainingSprintTicks;
|
||||
+ }
|
||||
+ // Leaves end - servux
|
||||
}
|
||||
diff --git a/net/minecraft/server/level/ServerLevel.java b/net/minecraft/server/level/ServerLevel.java
|
||||
index eb3aef1f5198592eda5dafe84b91271e861b4135..576dde41a4b48aac9079cca902f4371a31470af3 100644
|
||||
--- a/net/minecraft/server/level/ServerLevel.java
|
||||
+++ b/net/minecraft/server/level/ServerLevel.java
|
||||
@@ -2368,6 +2368,7 @@ public class ServerLevel extends Level implements ServerEntityGetter, WorldGenLe
|
||||
}
|
||||
|
||||
this.lastSpawnChunkRadius = i;
|
||||
+ org.leavesmc.leaves.protocol.servux.ServuxHudDataProtocol.refreshSpawnMetadata = true; // Leaves - servux
|
||||
}
|
||||
|
||||
public LongSet getForceLoadedChunks() {
|
||||
@@ -1,20 +0,0 @@
|
||||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: Helvetica Volubi <suisuroru@blue-millennium.fun>
|
||||
Date: Sun, 18 May 2025 23:48:59 +0800
|
||||
Subject: [PATCH] Old nether portal collision
|
||||
|
||||
This patch should removed in 1.21.6 when mojang revert it.
|
||||
|
||||
diff --git a/net/minecraft/world/level/block/NetherPortalBlock.java b/net/minecraft/world/level/block/NetherPortalBlock.java
|
||||
index 421ea9c748159ac989900427a13abe9b244dc7aa..ecdecf0484137e0f98bf41d09e0dc7dc7715fd35 100644
|
||||
--- a/net/minecraft/world/level/block/NetherPortalBlock.java
|
||||
+++ b/net/minecraft/world/level/block/NetherPortalBlock.java
|
||||
@@ -65,7 +65,7 @@ public class NetherPortalBlock extends Block implements Portal {
|
||||
|
||||
@Override
|
||||
protected VoxelShape getEntityInsideCollisionShape(BlockState state, BlockGetter level, BlockPos pos, Entity entity) {
|
||||
- return state.getShape(level, pos);
|
||||
+ return me.earthme.lophine.config.modules.misc.OldFeatureConfig.oldNetherPortalCollision ? Shapes.block() : state.getShape(level, pos); // Lophine - Old nether portal collision
|
||||
}
|
||||
|
||||
@Override
|
||||
+18
-18
@@ -8,25 +8,25 @@ As part of: Purpur (https://github.com/PurpurMC/Purpur/blob/09f547de09fc5d886f18
|
||||
Licensed under: MIT (https://github.com/PurpurMC/Purpur/blob/09f547de09fc5d886f18f6d99ff389289766ec9d/LICENSE)
|
||||
|
||||
diff --git a/net/minecraft/server/players/PlayerList.java b/net/minecraft/server/players/PlayerList.java
|
||||
index 001483b7098523f3645a95c81fcf0e38b551705e..3a83ba2fab530e9e64d53167d296c1f2ca1b9e4b 100644
|
||||
index 593dbcda4b497291eb1ecdc64b947a8d279f8be6..661f797b39853104b16d75ca7ba0eda156a74af3 100644
|
||||
--- a/net/minecraft/server/players/PlayerList.java
|
||||
+++ b/net/minecraft/server/players/PlayerList.java
|
||||
@@ -1116,6 +1116,10 @@ public abstract class PlayerList {
|
||||
@@ -1087,6 +1087,10 @@ public abstract class PlayerList {
|
||||
player.getBukkitEntity().recalculatePermissions(); // CraftBukkit
|
||||
this.server.getCommands().sendCommands(player);
|
||||
} // Paper - Add sendOpLevel API
|
||||
+
|
||||
+ // Purpur start - Barrels and enderchests 6 rows
|
||||
+ player.enderChestSlotCount = me.earthme.lophine.config.modules.misc.ContainerExpansionConfig.enderchestRows < 7 && me.earthme.lophine.config.modules.misc.ContainerExpansionConfig.enderchestRows > 0 ? 9 * me.earthme.lophine.config.modules.misc.ContainerExpansionConfig.enderchestRows : 27;
|
||||
+ player.enderChestSlotCount = fun.bm.lophine.config.modules.misc.ContainerExpansionConfig.enderchestRows < 7 && fun.bm.lophine.config.modules.misc.ContainerExpansionConfig.enderchestRows > 0 ? 9 * fun.bm.lophine.config.modules.misc.ContainerExpansionConfig.enderchestRows : 27;
|
||||
+ // Purpur end - Barrels and enderchests 6 rows
|
||||
}
|
||||
|
||||
public boolean isWhiteListed(GameProfile profile) {
|
||||
// Paper start - whitelist verify event / login event
|
||||
diff --git a/net/minecraft/world/entity/player/Player.java b/net/minecraft/world/entity/player/Player.java
|
||||
index 34c4c195519de03ec11897160b98c80353eb3d6c..27e335e01d8f09f05a4fea5f15407dbe3a4555cb 100644
|
||||
index 09b549fa9568aa1ae13b1b975db14a6268fdaa46..6c933c27ce155eb516a9ede96fb2c3bd19251bfa 100644
|
||||
--- a/net/minecraft/world/entity/player/Player.java
|
||||
+++ b/net/minecraft/world/entity/player/Player.java
|
||||
@@ -210,6 +210,7 @@ public abstract class Player extends LivingEntity {
|
||||
@@ -219,6 +219,7 @@ public abstract class Player extends LivingEntity {
|
||||
private int currentImpulseContextResetGraceTime = 0;
|
||||
public boolean affectsSpawning = true; // Paper - Affects Spawning API
|
||||
public net.kyori.adventure.util.TriState flyingFallDamage = net.kyori.adventure.util.TriState.NOT_SET; // Paper - flying fall damage
|
||||
@@ -70,15 +70,15 @@ index 0fffa384f928ab84451331380968fb4650eafe26..d84a10396395939149da88bcb01be59e
|
||||
return new ChestMenu(MenuType.GENERIC_9x6, containerId, playerInventory, container, 6);
|
||||
}
|
||||
diff --git a/net/minecraft/world/inventory/PlayerEnderChestContainer.java b/net/minecraft/world/inventory/PlayerEnderChestContainer.java
|
||||
index bc2b95973192069fc64581b59583b19df876f55d..d80a3760c0a84df19d0b2063c648f08ecef44f02 100644
|
||||
index 51148b7b95c93a9e25da70ac9275c63f1458d84c..b6bceae1ec26685af485bfe9d846f7584d5012cd 100644
|
||||
--- a/net/minecraft/world/inventory/PlayerEnderChestContainer.java
|
||||
+++ b/net/minecraft/world/inventory/PlayerEnderChestContainer.java
|
||||
@@ -25,11 +25,18 @@ public class PlayerEnderChestContainer extends SimpleContainer {
|
||||
@@ -26,11 +26,18 @@ public class PlayerEnderChestContainer extends SimpleContainer {
|
||||
}
|
||||
|
||||
public PlayerEnderChestContainer(Player owner) {
|
||||
- super(27);
|
||||
+ super(me.earthme.lophine.config.modules.misc.ContainerExpansionConfig.enderchestRows < 7 && me.earthme.lophine.config.modules.misc.ContainerExpansionConfig.enderchestRows > 0 ? 9 * me.earthme.lophine.config.modules.misc.ContainerExpansionConfig.enderchestRows : 27);
|
||||
+ super(fun.bm.lophine.config.modules.misc.ContainerExpansionConfig.enderchestRows < 7 && fun.bm.lophine.config.modules.misc.ContainerExpansionConfig.enderchestRows > 0 ? 9 * fun.bm.lophine.config.modules.misc.ContainerExpansionConfig.enderchestRows : 27);
|
||||
this.owner = owner;
|
||||
// CraftBukkit end
|
||||
}
|
||||
@@ -94,7 +94,7 @@ index bc2b95973192069fc64581b59583b19df876f55d..d80a3760c0a84df19d0b2063c648f08e
|
||||
this.activeChest = enderChestBlockEntity;
|
||||
}
|
||||
diff --git a/net/minecraft/world/level/block/EnderChestBlock.java b/net/minecraft/world/level/block/EnderChestBlock.java
|
||||
index 5077a9ff7b78801bdc53536a37aee07b8d86ee4d..c7866aa9a3647d3285eb0996fbc55e1b15a4c40c 100644
|
||||
index 5077a9ff7b78801bdc53536a37aee07b8d86ee4d..fe374f08b7829b846b2a1c2ac3ca500ca3e48684 100644
|
||||
--- a/net/minecraft/world/level/block/EnderChestBlock.java
|
||||
+++ b/net/minecraft/world/level/block/EnderChestBlock.java
|
||||
@@ -85,8 +85,14 @@ public class EnderChestBlock extends AbstractChestBlock<EnderChestBlockEntity> i
|
||||
@@ -103,7 +103,7 @@ index 5077a9ff7b78801bdc53536a37aee07b8d86ee4d..c7866aa9a3647d3285eb0996fbc55e1b
|
||||
new SimpleMenuProvider(
|
||||
- (containerId, playerInventory, player1) -> ChestMenu.threeRows(containerId, playerInventory, enderChestInventory), CONTAINER_TITLE
|
||||
- )
|
||||
+ (containerId, playerInventory, player1) -> switch (me.earthme.lophine.config.modules.misc.ContainerExpansionConfig.enderchestRows) {
|
||||
+ (containerId, playerInventory, player1) -> switch (fun.bm.lophine.config.modules.misc.ContainerExpansionConfig.enderchestRows) {
|
||||
+ case 6 -> ChestMenu.sixRows(containerId, playerInventory, enderChestInventory);
|
||||
+ case 5 -> ChestMenu.fiveRows(containerId, playerInventory, enderChestInventory);
|
||||
+ case 4 -> ChestMenu.fourRows(containerId, playerInventory, enderChestInventory);
|
||||
@@ -115,16 +115,16 @@ index 5077a9ff7b78801bdc53536a37aee07b8d86ee4d..c7866aa9a3647d3285eb0996fbc55e1b
|
||||
// Paper end - Fix InventoryOpenEvent cancellation - moved up;
|
||||
player.awardStat(Stats.OPEN_ENDERCHEST);
|
||||
diff --git a/net/minecraft/world/level/block/entity/BarrelBlockEntity.java b/net/minecraft/world/level/block/entity/BarrelBlockEntity.java
|
||||
index 027502d0af5512c31878978c4d05c52fa3029cca..4716015da322ed1f41c9f81d5ff0695914e7b6e3 100644
|
||||
index de99652629c7301a4bc0e9ec48bed9a26a338f0d..4383e87560bb05acf9c50758f38a3cb69ee5c5c7 100644
|
||||
--- a/net/minecraft/world/level/block/entity/BarrelBlockEntity.java
|
||||
+++ b/net/minecraft/world/level/block/entity/BarrelBlockEntity.java
|
||||
@@ -56,7 +56,17 @@ public class BarrelBlockEntity extends RandomizableContainerBlockEntity {
|
||||
@@ -56,7 +56,17 @@ public class BarrelBlockEntity extends RandomizableContainerBlockEntity implemen
|
||||
}
|
||||
// CraftBukkit end
|
||||
|
||||
- private NonNullList<ItemStack> items = NonNullList.withSize(27, ItemStack.EMPTY);
|
||||
+ // Purpur start - Barrels and enderchests 6 rows
|
||||
+ private NonNullList<ItemStack> items = NonNullList.withSize(switch (me.earthme.lophine.config.modules.misc.ContainerExpansionConfig.barrelRows) {
|
||||
+ private NonNullList<ItemStack> items = NonNullList.withSize(switch (fun.bm.lophine.config.modules.misc.ContainerExpansionConfig.barrelRows) {
|
||||
+ case 6 -> 54;
|
||||
+ case 5 -> 45;
|
||||
+ case 4 -> 36;
|
||||
@@ -137,13 +137,13 @@ index 027502d0af5512c31878978c4d05c52fa3029cca..4716015da322ed1f41c9f81d5ff06959
|
||||
public final ContainerOpenersCounter openersCounter = new ContainerOpenersCounter() {
|
||||
@Override
|
||||
protected void onOpen(Level level, BlockPos pos, BlockState state) {
|
||||
@@ -108,7 +118,16 @@ public class BarrelBlockEntity extends RandomizableContainerBlockEntity {
|
||||
@@ -108,7 +118,16 @@ public class BarrelBlockEntity extends RandomizableContainerBlockEntity implemen
|
||||
|
||||
@Override
|
||||
public int getContainerSize() {
|
||||
- return 27;
|
||||
+ // Purpur start - Barrels and enderchests 6 rows
|
||||
+ return switch (me.earthme.lophine.config.modules.misc.ContainerExpansionConfig.barrelRows) {
|
||||
+ return switch (fun.bm.lophine.config.modules.misc.ContainerExpansionConfig.barrelRows) {
|
||||
+ case 6 -> 54;
|
||||
+ case 5 -> 45;
|
||||
+ case 4 -> 36;
|
||||
@@ -155,13 +155,13 @@ index 027502d0af5512c31878978c4d05c52fa3029cca..4716015da322ed1f41c9f81d5ff06959
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -128,7 +147,16 @@ public class BarrelBlockEntity extends RandomizableContainerBlockEntity {
|
||||
@@ -129,7 +148,16 @@ public class BarrelBlockEntity extends RandomizableContainerBlockEntity implemen
|
||||
|
||||
@Override
|
||||
protected AbstractContainerMenu createMenu(int id, Inventory player) {
|
||||
- return ChestMenu.threeRows(id, player, this);
|
||||
+ // Purpur start - Barrels and enderchests 6 rows
|
||||
+ return switch (me.earthme.lophine.config.modules.misc.ContainerExpansionConfig.barrelRows) {
|
||||
+ return switch (fun.bm.lophine.config.modules.misc.ContainerExpansionConfig.barrelRows) {
|
||||
+ case 6 -> ChestMenu.sixRows(id, player, this);
|
||||
+ case 5 -> ChestMenu.fiveRows(id, player, this);
|
||||
+ case 4 -> ChestMenu.fourRows(id, player, this);
|
||||
+54
@@ -0,0 +1,54 @@
|
||||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: Helvetica Volubi <suisuroru@blue-millennium.fun>
|
||||
Date: Fri, 15 Aug 2025 02:29:30 +0800
|
||||
Subject: [PATCH] Compatibility fix for Raid Revert and Cross Region Damage
|
||||
Trace
|
||||
|
||||
|
||||
diff --git a/net/minecraft/world/entity/LivingEntity.java b/net/minecraft/world/entity/LivingEntity.java
|
||||
index 3f4b1c54f9207ba1854a8c8da965cbaca5ea517d..a8348a073682eec31990e9ab963b11ad32cf8bf8 100644
|
||||
--- a/net/minecraft/world/entity/LivingEntity.java
|
||||
+++ b/net/minecraft/world/entity/LivingEntity.java
|
||||
@@ -1216,6 +1216,29 @@ public abstract class LivingEntity extends Entity implements Attackable, Waypoin
|
||||
}
|
||||
}
|
||||
|
||||
+ // Lophine Start - raid revert adapt Cross Region Damage trace
|
||||
+ public boolean addEffect(MobEffectInstance effectInstance, EntityPotionEffectEvent.Cause cause, boolean fireEvent, boolean async) {
|
||||
+ if (!async || ca.spottedleaf.moonrise.common.util.TickThread.isTickThreadFor(this)) {
|
||||
+ return addEffect(effectInstance, this, cause, fireEvent);
|
||||
+ } else if (fun.bm.lophine.config.modules.experiment.EntityDamageSourceTraceConfig.enabled) {
|
||||
+ postToAddEffect(effectInstance, this, cause, fireEvent);
|
||||
+ return true;
|
||||
+ }
|
||||
+ return false;
|
||||
+ }
|
||||
+
|
||||
+ private void postToAddEffect(MobEffectInstance effectInstance, @Nullable Entity entity, EntityPotionEffectEvent.Cause cause, boolean fireEvent) {
|
||||
+ if (entity != null)
|
||||
+ entity.getBukkitEntity().taskScheduler.schedule((Entity nmsEntity) -> {
|
||||
+ try {
|
||||
+ addEffect(effectInstance, nmsEntity, cause, fireEvent);
|
||||
+ } catch (Throwable ex) {
|
||||
+ LOGGER.error(ex.getMessage(), ex);
|
||||
+ }
|
||||
+ }, null, 1L );
|
||||
+ }
|
||||
+ // Lophine End - raid revert adapt Cross Region Damage trace
|
||||
+
|
||||
public boolean canBeAffected(MobEffectInstance effectInstance) {
|
||||
if (this.getType().is(EntityTypeTags.IMMUNE_TO_INFESTED)) {
|
||||
return !effectInstance.is(MobEffects.INFESTED);
|
||||
diff --git a/net/minecraft/world/entity/raid/Raider.java b/net/minecraft/world/entity/raid/Raider.java
|
||||
index b1f1bcf7f5ac002550077d5e1d4efd5c67d18063..6120f7b970ff05867524ee9f1433d46843d57483 100644
|
||||
--- a/net/minecraft/world/entity/raid/Raider.java
|
||||
+++ b/net/minecraft/world/entity/raid/Raider.java
|
||||
@@ -158,7 +158,7 @@ public abstract class Raider extends PatrollingMonster {
|
||||
net.minecraft.world.effect.MobEffectInstance mobeffect1 = new net.minecraft.world.effect.MobEffectInstance(net.minecraft.world.effect.MobEffects.BAD_OMEN, 120000, i, false, false, true);
|
||||
|
||||
if (!serverLevel.getGameRules().getBoolean(net.minecraft.world.level.GameRules.RULE_DISABLE_RAIDS)) {
|
||||
- entityhuman.addEffect(mobeffect1, org.bukkit.event.entity.EntityPotionEffectEvent.Cause.PATROL_CAPTAIN); // CraftBukkit
|
||||
+ entityhuman.addEffect(mobeffect1, org.bukkit.event.entity.EntityPotionEffectEvent.Cause.PATROL_CAPTAIN, true, fun.bm.lophine.config.modules.experiment.EntityDamageSourceTraceConfig.enabled); // Lophine - raid revert adapt Cross Region Damage trace
|
||||
}
|
||||
this.setPatrolLeader(false);
|
||||
}
|
||||
@@ -1,18 +0,0 @@
|
||||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: Helvetica Volubi <suisuroru@blue-millennium.fun>
|
||||
Date: Thu, 12 Jun 2025 04:44:24 +0800
|
||||
Subject: [PATCH] I18n support
|
||||
|
||||
|
||||
diff --git a/net/minecraft/server/dedicated/DedicatedServer.java b/net/minecraft/server/dedicated/DedicatedServer.java
|
||||
index ba0cdba050cd3bf9231e15dd9543d34f37d9dae5..33e7cb15ceafdb246bb9cc1a43d9968d3f28ca6b 100644
|
||||
--- a/net/minecraft/server/dedicated/DedicatedServer.java
|
||||
+++ b/net/minecraft/server/dedicated/DedicatedServer.java
|
||||
@@ -172,6 +172,7 @@ public class DedicatedServer extends MinecraftServer implements ServerInterface
|
||||
this.paperConfigurations.initializeWorldDefaultsConfiguration(this.registryAccess());
|
||||
// Paper end - initialize global and world-defaults configuration
|
||||
me.earthme.lophine.config.LophineConfig.loadConfigFiles(); //Luminol - load config file // Lophine - load config file
|
||||
+ me.earthme.lophine.utils.ServerI18nUtil.init(); // Lophine - I18n support
|
||||
if (false) this.server.spark.enableEarlyIfRequested(); // Paper - spark // Luminol - Force disable builtin spark
|
||||
// Paper start - fix converting txt to json file; convert old users earlier after PlayerList creation but before file load/save
|
||||
if (this.convertOldUsers()) {
|
||||
@@ -0,0 +1,55 @@
|
||||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: Helvetica Volubi <suisuroru@blue-millennium.fun>
|
||||
Date: Sun, 3 Aug 2025 14:26:37 +0800
|
||||
Subject: [PATCH] Disable some check for operators
|
||||
|
||||
|
||||
diff --git a/net/minecraft/server/network/ServerGamePacketListenerImpl.java b/net/minecraft/server/network/ServerGamePacketListenerImpl.java
|
||||
index d63d8f7523938088bf9efc1c1b8ad9f0583338bb..b40059553089207ab1c6628beefadc674ec43d4e 100644
|
||||
--- a/net/minecraft/server/network/ServerGamePacketListenerImpl.java
|
||||
+++ b/net/minecraft/server/network/ServerGamePacketListenerImpl.java
|
||||
@@ -367,7 +367,7 @@ public class ServerGamePacketListenerImpl
|
||||
this.tickCount++;
|
||||
this.knownMovePacketCount = this.receivedMovePacketCount;
|
||||
if (this.clientIsFloating && !this.player.isSleeping() && !this.player.isPassenger() && !this.player.isDeadOrDying()) {
|
||||
- if (++this.aboveGroundTickCount > this.getMaximumFlyingTicks(this.player)) {
|
||||
+ if (++this.aboveGroundTickCount > this.getMaximumFlyingTicks(this.player) && !(fun.bm.lophine.config.modules.misc.DisableCheckConfig.disableOpFlyCheck && this.player.hasPermissions(2))) { // Lophine - disable fly check for operator
|
||||
// LOGGER.warn("{} was kicked for floating too long!", this.player.getName().getString()); // Paper - Logging moved to net.minecraft.server.network.ServerCommonPacketListenerImpl.disconnect()
|
||||
this.disconnect(io.papermc.paper.configuration.GlobalConfiguration.get().messages.kick.flyingPlayer, org.bukkit.event.player.PlayerKickEvent.Cause.FLYING_PLAYER); // Paper - use configurable kick message & kick event cause
|
||||
return;
|
||||
@@ -386,7 +386,7 @@ public class ServerGamePacketListenerImpl
|
||||
this.vehicleLastGoodY = this.lastVehicle.getY();
|
||||
this.vehicleLastGoodZ = this.lastVehicle.getZ();
|
||||
if (this.clientVehicleIsFloating && this.lastVehicle.getControllingPassenger() == this.player) {
|
||||
- if (++this.aboveGroundVehicleTickCount > this.getMaximumFlyingTicks(this.lastVehicle)) {
|
||||
+ if (++this.aboveGroundVehicleTickCount > this.getMaximumFlyingTicks(this.lastVehicle) && !(fun.bm.lophine.config.modules.misc.DisableCheckConfig.disableOpFlyCheck && this.player.hasPermissions(2))) { // Lophine - disable fly check for operator
|
||||
// LOGGER.warn("{} was kicked for floating a vehicle too long!", this.player.getName().getString()); // Paper - Logging moved to net.minecraft.server.network.ServerCommonPacketListenerImpl.disconnect()
|
||||
this.disconnect(io.papermc.paper.configuration.GlobalConfiguration.get().messages.kick.flyingVehicle, org.bukkit.event.player.PlayerKickEvent.Cause.FLYING_VEHICLE); // Paper - use configurable kick message & kick event cause
|
||||
return;
|
||||
@@ -644,7 +644,7 @@ public class ServerGamePacketListenerImpl
|
||||
d5 = d2 - rootVehicle.getZ();
|
||||
d7 = d3 * d3 + d4 * d4 + d5 * d5;
|
||||
boolean flag1 = false;
|
||||
- if (!me.earthme.luminol.config.modules.misc.DisableMovedWronglyThreshold.enabled && d7 > org.spigotmc.SpigotConfig.movedWronglyThreshold) { // Spigot
|
||||
+ if (!me.earthme.luminol.config.modules.misc.DisableMovedWronglyThreshold.enabled && d7 > org.spigotmc.SpigotConfig.movedWronglyThreshold && !(fun.bm.lophine.config.modules.misc.DisableCheckConfig.disableOpMoveCheck && this.player.hasPermissions(2))) { // Spigot // Lophine - disable move check for operator
|
||||
flag1 = true; // Paper - diff on change, this should be moved wrongly
|
||||
LOGGER.warn("{} (vehicle of {}) moved wrongly! {}", rootVehicle.getName().getString(), this.player.getName().getString(), Math.sqrt(d7));
|
||||
}
|
||||
@@ -1540,7 +1540,7 @@ public class ServerGamePacketListenerImpl
|
||||
// Paper start - Add fail move event
|
||||
io.papermc.paper.event.player.PlayerFailMoveEvent event = fireFailMove(io.papermc.paper.event.player.PlayerFailMoveEvent.FailReason.MOVED_TOO_QUICKLY,
|
||||
toX, toY, toZ, toYaw, toPitch, true);
|
||||
- if (!event.isAllowed()) {
|
||||
+ if (!event.isAllowed() && !(fun.bm.lophine.config.modules.misc.DisableCheckConfig.disableOpMoveCheck && this.player.hasPermissions(2))) { // Lophine - disable move check for operator
|
||||
if (event.getLogWarning()) {
|
||||
LOGGER.warn("{} moved too quickly! {},{},{}", this.player.getName().getString(), d3, d4, d5);
|
||||
}
|
||||
@@ -1618,7 +1618,7 @@ public class ServerGamePacketListenerImpl
|
||||
// Paper start - Add fail move event
|
||||
io.papermc.paper.event.player.PlayerFailMoveEvent event = fireFailMove(io.papermc.paper.event.player.PlayerFailMoveEvent.FailReason.MOVED_WRONGLY,
|
||||
toX, toY, toZ, toYaw, toPitch, true);
|
||||
- if (!event.isAllowed()) {
|
||||
+ if (!event.isAllowed() && !(fun.bm.lophine.config.modules.misc.DisableCheckConfig.disableOpMoveCheck && this.player.hasPermissions(2))) { // Lophine - disable move check for operator
|
||||
movedWrongly = true;
|
||||
if (event.getLogWarning())
|
||||
// Paper end
|
||||
@@ -0,0 +1,44 @@
|
||||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: Helvetica Volubi <suisuroru@blue-millennium.fun>
|
||||
Date: Thu, 12 Jun 2025 04:44:24 +0800
|
||||
Subject: [PATCH] I18n support
|
||||
|
||||
|
||||
diff --git a/net/minecraft/locale/Language.java b/net/minecraft/locale/Language.java
|
||||
index 7b9e2a1a208b46a69c16e6afd8b502259893574f..db46a31ea8a36562e548eb07f0c4cf9b9e8c3360 100644
|
||||
--- a/net/minecraft/locale/Language.java
|
||||
+++ b/net/minecraft/locale/Language.java
|
||||
@@ -65,7 +65,7 @@ public abstract class Language {
|
||||
};
|
||||
}
|
||||
|
||||
- private static void parseTranslations(BiConsumer<String, String> output, String languagePath) {
|
||||
+ public static void parseTranslations(BiConsumer<String, String> output, String languagePath) { // Lophine - private -> public
|
||||
try (InputStream resourceAsStream = Language.class.getResourceAsStream(languagePath)) {
|
||||
loadFromJson(resourceAsStream, output);
|
||||
} catch (JsonParseException | IOException var7) {
|
||||
diff --git a/net/minecraft/server/Main.java b/net/minecraft/server/Main.java
|
||||
index cf72cb8b418239b56c755486b03a321672e16bc3..b69c06607ca900beeebf4892a978442dad2db19b 100644
|
||||
--- a/net/minecraft/server/Main.java
|
||||
+++ b/net/minecraft/server/Main.java
|
||||
@@ -155,6 +155,8 @@ public class Main {
|
||||
return;
|
||||
}
|
||||
|
||||
+ fun.bm.lophine.utils.ServerI18nUtil.preInit(); // Lophine - I18n support
|
||||
+
|
||||
// Paper start - Detect headless JRE
|
||||
String awtException = io.papermc.paper.util.ServerEnvironment.awtDependencyCheck();
|
||||
if (awtException != null) {
|
||||
diff --git a/net/minecraft/server/dedicated/DedicatedServer.java b/net/minecraft/server/dedicated/DedicatedServer.java
|
||||
index ec07d916ff52f5a866218c2fa4d632b83675faf2..eabaae86e4951046db5d1bae9abb92f1bece100f 100644
|
||||
--- a/net/minecraft/server/dedicated/DedicatedServer.java
|
||||
+++ b/net/minecraft/server/dedicated/DedicatedServer.java
|
||||
@@ -172,6 +172,7 @@ public class DedicatedServer extends MinecraftServer implements ServerInterface
|
||||
this.paperConfigurations.initializeWorldDefaultsConfiguration(this.registryAccess());
|
||||
// Paper end - initialize global and world-defaults configuration
|
||||
me.earthme.luminol.config.ConfigManager.loadConfigFiles(); // Luminol - load config file
|
||||
+ fun.bm.lophine.utils.ServerI18nUtil.init(); // Lophine - I18n support
|
||||
if (false) this.server.spark.enableEarlyIfRequested(); // Paper - spark // Luminol - Force disable builtin spark
|
||||
// Paper start - fix converting txt to json file; convert old users earlier after PlayerList creation but before file load/save
|
||||
if (this.convertOldUsers()) {
|
||||
+3
-3
@@ -5,15 +5,15 @@ Subject: [PATCH] Old zombie reinforcement
|
||||
|
||||
|
||||
diff --git a/net/minecraft/world/entity/monster/Zombie.java b/net/minecraft/world/entity/monster/Zombie.java
|
||||
index 4395947fc8c719864ac2afde5e6bbb53da5129c2..348e2b6020de8584b512ec849a5fba484380930e 100644
|
||||
index 14d0758aca54d9a25e369dfa8c89c930f27946c9..6f1f26b7f759e56f843e674167470da943785728 100644
|
||||
--- a/net/minecraft/world/entity/monster/Zombie.java
|
||||
+++ b/net/minecraft/world/entity/monster/Zombie.java
|
||||
@@ -342,7 +342,7 @@ public class Zombie extends Monster {
|
||||
@@ -341,7 +341,7 @@ public class Zombie extends Monster {
|
||||
int floor = Mth.floor(this.getX());
|
||||
int floor1 = Mth.floor(this.getY());
|
||||
int floor2 = Mth.floor(this.getZ());
|
||||
- EntityType<? extends Zombie> type = this.getType();
|
||||
+ EntityType<? extends Zombie> type = me.earthme.lophine.config.modules.misc.OldFeatureConfig.oldZombieReinforcement ? EntityType.ZOMBIE : this.getType(); // Lophine - old zombie reinforcement
|
||||
+ EntityType<? extends Zombie> type = fun.bm.lophine.config.modules.misc.OldFeatureConfig.oldZombieReinforcement ? EntityType.ZOMBIE : this.getType(); // Lophine - old zombie reinforcement
|
||||
Zombie zombie = type.create(level, EntitySpawnReason.REINFORCEMENT);
|
||||
if (zombie == null) {
|
||||
return true;
|
||||
+6
-6
@@ -5,10 +5,10 @@ Subject: [PATCH] Spawn invulnerable time
|
||||
|
||||
|
||||
diff --git a/net/minecraft/server/level/ServerPlayer.java b/net/minecraft/server/level/ServerPlayer.java
|
||||
index 1764875b9024569671bf2be537afb8b492f79102..473f9de0908d3fb2cfb640ff7c6f7b78c1280721 100644
|
||||
index 6b7c92fdf562dfa46eee8e8acbc82b02fc3226c5..144b2c9396b8162f9cce8b343d998907bf7fb5aa 100644
|
||||
--- a/net/minecraft/server/level/ServerPlayer.java
|
||||
+++ b/net/minecraft/server/level/ServerPlayer.java
|
||||
@@ -221,6 +221,7 @@ public class ServerPlayer extends Player implements ca.spottedleaf.moonrise.patc
|
||||
@@ -232,6 +232,7 @@ public class ServerPlayer extends Player implements ca.spottedleaf.moonrise.patc
|
||||
private int lastSentFood = -99999999;
|
||||
private boolean lastFoodSaturationZero = true;
|
||||
public int lastSentExp = -99999999;
|
||||
@@ -16,20 +16,20 @@ index 1764875b9024569671bf2be537afb8b492f79102..473f9de0908d3fb2cfb640ff7c6f7b78
|
||||
private ChatVisiblity chatVisibility = ChatVisiblity.FULL;
|
||||
public ParticleStatus particleStatus = ParticleStatus.ALL;
|
||||
private boolean canChatColor = true;
|
||||
@@ -923,6 +924,7 @@ public class ServerPlayer extends Player implements ca.spottedleaf.moonrise.patc
|
||||
@@ -925,6 +926,7 @@ public class ServerPlayer extends Player implements ca.spottedleaf.moonrise.patc
|
||||
this.tickClientLoadTimeout();
|
||||
this.gameMode.tick();
|
||||
this.wardenSpawnTracker.tick();
|
||||
+ if (me.earthme.lophine.config.modules.misc.OldFeatureConfig.spawnInvulnerableTime && this.invulnerableTime > 0) --this.spawnInvulnerableTime; // Lophine - spawn invulnerable time
|
||||
+ if (this.spawnInvulnerableTime > 0) --this.spawnInvulnerableTime; // Lophine - spawn invulnerable time
|
||||
if (this.invulnerableTime > 0) {
|
||||
this.invulnerableTime--;
|
||||
}
|
||||
@@ -1420,6 +1422,13 @@ public class ServerPlayer extends Player implements ca.spottedleaf.moonrise.patc
|
||||
@@ -1435,6 +1437,13 @@ public class ServerPlayer extends Player implements ca.spottedleaf.moonrise.patc
|
||||
if (this.isInvulnerableTo(level, damageSource)) {
|
||||
return false;
|
||||
} else {
|
||||
+ // Lophine start - spawn invulnerable time
|
||||
+ if (me.earthme.lophine.config.modules.misc.OldFeatureConfig.spawnInvulnerableTime) {
|
||||
+ if (fun.bm.lophine.config.modules.misc.OldFeatureConfig.spawnInvulnerableTime) {
|
||||
+ if (this.spawnInvulnerableTime > 0 && !damageSource.is(net.minecraft.tags.DamageTypeTags.BYPASSES_INVULNERABILITY)) {
|
||||
+ return false;
|
||||
+ }
|
||||
File diff suppressed because it is too large
Load Diff
+8
-5
@@ -1,21 +1,24 @@
|
||||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: Suisuroru <qwertyuiop14077@qq.com>
|
||||
Date: Thu, 20 Feb 2025 01:00:29 +0800
|
||||
Subject: [PATCH] Purpur-Barrels-and-enderchests-6-rows
|
||||
Subject: [PATCH] Purpur: Barrels and enderchests 6 rows
|
||||
|
||||
Co-authored by: William Blake Galbreath <Blake.Galbreath@GMail.com>
|
||||
As part of: Purpur (https://github.com/PurpurMC/Purpur/blob/09f547de09fc5d886f18f6d99ff389289766ec9d/purpur-server/paper-patches/features/0003-Barrels-and-enderchests-6-rows.patch)
|
||||
Licensed under: MIT (https://github.com/PurpurMC/Purpur/blob/09f547de09fc5d886f18f6d99ff389289766ec9d/LICENSE)
|
||||
|
||||
diff --git a/src/main/java/org/bukkit/craftbukkit/inventory/CraftContainer.java b/src/main/java/org/bukkit/craftbukkit/inventory/CraftContainer.java
|
||||
index c00ddfe41439954fa0fd87c0933f274c8a752eb6..ac0218fcdf9e9c77a30b7f09b2a5e557ef0f7591 100644
|
||||
index 2f41a92465b9da28e026297cc3528898bb1c8412..fb5300ec1bd19b3dfd6a2b4106440b9f73c48205 100644
|
||||
--- a/src/main/java/org/bukkit/craftbukkit/inventory/CraftContainer.java
|
||||
+++ b/src/main/java/org/bukkit/craftbukkit/inventory/CraftContainer.java
|
||||
@@ -150,9 +150,26 @@ public class CraftContainer extends AbstractContainerMenu {
|
||||
@@ -148,9 +148,26 @@ public class CraftContainer extends AbstractContainerMenu {
|
||||
case PLAYER:
|
||||
case CHEST:
|
||||
case ENDER_CHEST:
|
||||
- case BARREL:
|
||||
- this.delegate = new ChestMenu(net.minecraft.world.inventory.MenuType.GENERIC_9x3, windowId, bottom, top, top.getContainerSize() / 9);
|
||||
+ // Purpur start - Barrels and enderchests 6 rows
|
||||
+ this.delegate = new ChestMenu(switch (me.earthme.lophine.config.modules.misc.ContainerExpansionConfig.enderchestRows) {
|
||||
+ this.delegate = new ChestMenu(switch (fun.bm.lophine.config.modules.misc.ContainerExpansionConfig.enderchestRows) {
|
||||
+ case 6 -> net.minecraft.world.inventory.MenuType.GENERIC_9x6;
|
||||
+ case 5 -> net.minecraft.world.inventory.MenuType.GENERIC_9x5;
|
||||
+ case 4 -> net.minecraft.world.inventory.MenuType.GENERIC_9x4;
|
||||
@@ -25,7 +28,7 @@ index c00ddfe41439954fa0fd87c0933f274c8a752eb6..ac0218fcdf9e9c77a30b7f09b2a5e557
|
||||
+ }, windowId, bottom, top, top.getContainerSize() / 9);
|
||||
break;
|
||||
+ case BARREL:
|
||||
+ this.delegate = new ChestMenu(switch (me.earthme.lophine.config.modules.misc.ContainerExpansionConfig.barrelRows) {
|
||||
+ this.delegate = new ChestMenu(switch (fun.bm.lophine.config.modules.misc.ContainerExpansionConfig.barrelRows) {
|
||||
+ case 6 -> net.minecraft.world.inventory.MenuType.GENERIC_9x6;
|
||||
+ case 5 -> net.minecraft.world.inventory.MenuType.GENERIC_9x5;
|
||||
+ case 4 -> net.minecraft.world.inventory.MenuType.GENERIC_9x4;
|
||||
|
||||
@@ -0,0 +1,29 @@
|
||||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: Helvetica Volubi <suisuroru@blue-millennium.fun>
|
||||
Date: Sun, 3 Aug 2025 15:57:08 +0800
|
||||
Subject: [PATCH] Leaves: Leaves Protocol Core
|
||||
|
||||
Co-authored by: violetc <58360096+s-yh-china@users.noreply.github.com>
|
||||
As a part of : Leaves (https://github.com/LeavesMC/Leaves/blob/ea91106ae57fc4cc14e2e0225009cf9919072f7f/leaves-server/paper-patches/features/0004-Leaves-Protocol-Core.patch)
|
||||
Licensed under: GPL-3.0 (https://www.gnu.org/licenses/gpl-3.0.html)
|
||||
|
||||
diff --git a/src/main/java/org/bukkit/craftbukkit/CraftServer.java b/src/main/java/org/bukkit/craftbukkit/CraftServer.java
|
||||
index 4f7077b592f84334483f8edf3656fccd5b90a9ca..26d5a96edd283a8f8fd50222e2671cef2c4b257b 100644
|
||||
--- a/src/main/java/org/bukkit/craftbukkit/CraftServer.java
|
||||
+++ b/src/main/java/org/bukkit/craftbukkit/CraftServer.java
|
||||
@@ -497,6 +497,7 @@ public final class CraftServer implements Server {
|
||||
this.potionBrewer = new io.papermc.paper.potion.PaperPotionBrewer(console); // Paper - custom potion mixes
|
||||
datapackManager = new io.papermc.paper.datapack.PaperDatapackManager(console.getPackRepository()); // Paper
|
||||
if (false) this.spark = new io.papermc.paper.SparksFly(this); // Paper - spark // Luminol - Force disable builtin spark
|
||||
+ org.leavesmc.leaves.protocol.core.LeavesProtocolManager.init(); // Leaves - protocol
|
||||
}
|
||||
|
||||
public boolean getCommandBlockOverride(String command) {
|
||||
@@ -1115,6 +1116,7 @@ public final class CraftServer implements Server {
|
||||
if (false) this.spark.registerCommandBeforePlugins(this); // Paper - spark // Luminol - Force disable builtin spark
|
||||
this.overrideAllCommandBlockCommands = this.commandsConfiguration.getStringList("command-block-overrides").contains("*");
|
||||
this.ignoreVanillaPermissions = this.commandsConfiguration.getBoolean("ignore-vanilla-permissions");
|
||||
+ org.leavesmc.leaves.protocol.core.LeavesProtocolManager.handleServerReload(); // Leaves - protocol
|
||||
|
||||
int pollCount = 0;
|
||||
|
||||
-154
@@ -1,154 +0,0 @@
|
||||
--- /dev/null
|
||||
+++ b/src/main/java/dev/tr7zw/entityculling/CullTask.java
|
||||
@@ -1,0 +_,151 @@
|
||||
+package dev.tr7zw.entityculling;
|
||||
+
|
||||
+import ca.spottedleaf.moonrise.common.util.TickThread;
|
||||
+import com.logisticscraft.occlusionculling.OcclusionCullingInstance;
|
||||
+import com.logisticscraft.occlusionculling.util.Vec3d;
|
||||
+import dev.tr7zw.entityculling.versionless.access.Cullable;
|
||||
+import me.earthme.lophine.config.modules.experiment.RayTrackingEntityTrackerConfig;
|
||||
+import net.minecraft.world.entity.Entity;
|
||||
+import net.minecraft.world.entity.decoration.ArmorStand;
|
||||
+import net.minecraft.world.entity.player.Player;
|
||||
+import net.minecraft.world.phys.AABB;
|
||||
+import net.minecraft.world.phys.Vec3;
|
||||
+
|
||||
+import java.util.concurrent.CompletableFuture;
|
||||
+import java.util.concurrent.Executor;
|
||||
+import java.util.concurrent.Executors;
|
||||
+import java.util.concurrent.TimeUnit;
|
||||
+
|
||||
+public class CullTask implements Runnable {
|
||||
+
|
||||
+ private volatile boolean requestCull = false;
|
||||
+ private volatile boolean scheduleNext = true;
|
||||
+ private volatile boolean inited = false;
|
||||
+
|
||||
+ private final OcclusionCullingInstance culling;
|
||||
+ private final Player checkTarget;
|
||||
+
|
||||
+ private final int hitboxLimit;
|
||||
+
|
||||
+ public long lastCheckedTime = 0;
|
||||
+
|
||||
+ // reused preallocated vars
|
||||
+ private final Vec3d lastPos = new Vec3d(0, 0, 0);
|
||||
+ private final Vec3d aabbMin = new Vec3d(0, 0, 0);
|
||||
+ private final Vec3d aabbMax = new Vec3d(0, 0, 0);
|
||||
+
|
||||
+ private static final Executor backgroundWorker = Executors.newCachedThreadPool(task -> {
|
||||
+ final TickThread worker = new TickThread("EntityCulling") {
|
||||
+ @Override
|
||||
+ public void run() {
|
||||
+ task.run();
|
||||
+ }
|
||||
+ };
|
||||
+
|
||||
+ worker.setDaemon(true);
|
||||
+
|
||||
+ return worker;
|
||||
+ });
|
||||
+
|
||||
+ private final Executor worker;
|
||||
+
|
||||
+ public CullTask(
|
||||
+ OcclusionCullingInstance culling,
|
||||
+ Player checkTarget,
|
||||
+ int hitboxLimit,
|
||||
+ long checkIntervalMs
|
||||
+ ) {
|
||||
+ this.culling = culling;
|
||||
+ this.checkTarget = checkTarget;
|
||||
+ this.hitboxLimit = hitboxLimit;
|
||||
+ this.worker = CompletableFuture.delayedExecutor(checkIntervalMs, TimeUnit.MILLISECONDS, backgroundWorker);
|
||||
+ }
|
||||
+
|
||||
+ public void requestCullSignal() {
|
||||
+ this.requestCull = true;
|
||||
+ }
|
||||
+
|
||||
+ public void signalStop() {
|
||||
+ this.scheduleNext = false;
|
||||
+ }
|
||||
+
|
||||
+ public void setup() {
|
||||
+ if (!this.inited)
|
||||
+ this.inited = true;
|
||||
+ else
|
||||
+ return;
|
||||
+ this.worker.execute(this);
|
||||
+ }
|
||||
+
|
||||
+ @Override
|
||||
+ public void run() {
|
||||
+ try {
|
||||
+ if (this.checkTarget.tickCount > 10) {
|
||||
+ // getEyePosition can use a fixed delta as its debug only anyway
|
||||
+ Vec3 cameraMC = this.checkTarget.getEyePosition(0);
|
||||
+ if (requestCull || !(cameraMC.x == lastPos.x && cameraMC.y == lastPos.y && cameraMC.z == lastPos.z)) {
|
||||
+ long start = System.currentTimeMillis();
|
||||
+
|
||||
+ requestCull = false;
|
||||
+
|
||||
+ lastPos.set(cameraMC.x, cameraMC.y, cameraMC.z);
|
||||
+ culling.resetCache();
|
||||
+
|
||||
+ cullEntities(cameraMC, lastPos);
|
||||
+
|
||||
+ lastCheckedTime = (System.currentTimeMillis() - start);
|
||||
+ }
|
||||
+ }
|
||||
+ }finally {
|
||||
+ if (this.scheduleNext) {
|
||||
+ this.worker.execute(this);
|
||||
+ }
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ private void cullEntities(Vec3 cameraMC, Vec3d camera) {
|
||||
+ for (Entity entity : this.checkTarget.level().getEntities().getAll()) {
|
||||
+ if (!(entity instanceof Cullable cullable)) {
|
||||
+ continue; // Not sure how this could happen outside from mixin screwing up the inject into
|
||||
+ // Entity
|
||||
+ }
|
||||
+
|
||||
+ if (entity.getType().skipRaytracningCheck) {
|
||||
+ continue;
|
||||
+ }
|
||||
+
|
||||
+ if (!cullable.isForcedVisible()) {
|
||||
+ if (entity.isCurrentlyGlowing() || isSkippableArmorstand(entity)) {
|
||||
+ cullable.setCulled(false);
|
||||
+ continue;
|
||||
+ }
|
||||
+
|
||||
+ if (!entity.position().closerThan(cameraMC, RayTrackingEntityTrackerConfig.tracingDistance)) {
|
||||
+ cullable.setCulled(false); // If your entity view distance is larger than tracingDistance just
|
||||
+ // render it
|
||||
+ continue;
|
||||
+ }
|
||||
+
|
||||
+ AABB boundingBox = entity.getBoundingBox();
|
||||
+ if (boundingBox.getXsize() > hitboxLimit || boundingBox.getYsize() > hitboxLimit
|
||||
+ || boundingBox.getZsize() > hitboxLimit) {
|
||||
+ cullable.setCulled(false); // Too big to bother to cull
|
||||
+ continue;
|
||||
+ }
|
||||
+
|
||||
+ aabbMin.set(boundingBox.minX, boundingBox.minY, boundingBox.minZ);
|
||||
+ aabbMax.set(boundingBox.maxX, boundingBox.maxY, boundingBox.maxZ);
|
||||
+
|
||||
+ boolean visible = culling.isAABBVisible(aabbMin, aabbMax, camera);
|
||||
+
|
||||
+ cullable.setCulled(!visible);
|
||||
+ }
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ private boolean isSkippableArmorstand(Entity entity) {
|
||||
+ if (!RayTrackingEntityTrackerConfig.skipMarkerArmorStands)
|
||||
+ return false;
|
||||
+ return entity instanceof ArmorStand && entity.isInvisible();
|
||||
+ }
|
||||
+}
|
||||
-45
@@ -1,45 +0,0 @@
|
||||
--- /dev/null
|
||||
+++ b/src/main/java/dev/tr7zw/entityculling/DefaultChunkDataProvider.java
|
||||
@@ -1,0 +_,42 @@
|
||||
+package dev.tr7zw.entityculling;
|
||||
+
|
||||
+import com.logisticscraft.occlusionculling.DataProvider;
|
||||
+import net.minecraft.core.BlockPos;
|
||||
+import net.minecraft.world.level.Level;
|
||||
+import net.minecraft.world.level.block.Blocks;
|
||||
+import net.minecraft.world.level.chunk.ChunkAccess;
|
||||
+
|
||||
+public class DefaultChunkDataProvider implements DataProvider {
|
||||
+ private final Level level;
|
||||
+
|
||||
+ public DefaultChunkDataProvider(Level level) {
|
||||
+ this.level = level;
|
||||
+ }
|
||||
+
|
||||
+ @Override
|
||||
+ public boolean prepareChunk(int chunkX, int chunkZ) {
|
||||
+ return this.level.getChunkIfLoaded(chunkX, chunkZ) != null;
|
||||
+ }
|
||||
+
|
||||
+ @Override
|
||||
+ public boolean isOpaqueFullCube(int x, int y, int z) {
|
||||
+ BlockPos pos = new BlockPos(x, y, z);
|
||||
+
|
||||
+ final ChunkAccess access = this.level.getChunkIfLoaded(pos);
|
||||
+ if (access == null) {
|
||||
+ return false;
|
||||
+ }
|
||||
+
|
||||
+ if (this.level.isOutsideBuildHeight(pos)) {
|
||||
+ return Blocks.VOID_AIR.defaultBlockState().isSolidRender();
|
||||
+ } else {
|
||||
+ return access.getBlockState(pos).isSolidRender();// 好孩子不要学坏叔叔这样绕过异步拦截()
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ @Override
|
||||
+ public void cleanup() {
|
||||
+ DataProvider.super.cleanup();
|
||||
+ }
|
||||
+
|
||||
+}
|
||||
-20
@@ -1,20 +0,0 @@
|
||||
--- /dev/null
|
||||
+++ b/src/main/java/dev/tr7zw/entityculling/versionless/access/Cullable.java
|
||||
@@ -1,0 +_,17 @@
|
||||
+package dev.tr7zw.entityculling.versionless.access;
|
||||
+
|
||||
+public interface Cullable {
|
||||
+
|
||||
+ public void setTimeout();
|
||||
+
|
||||
+ public boolean isForcedVisible();
|
||||
+
|
||||
+ public void setCulled(boolean value);
|
||||
+
|
||||
+ public boolean isCulled();
|
||||
+
|
||||
+ public void setOutOfCamera(boolean value);
|
||||
+
|
||||
+ public boolean isOutOfCamera();
|
||||
+
|
||||
+}
|
||||
-36
@@ -1,36 +0,0 @@
|
||||
--- /dev/null
|
||||
+++ b/src/main/java/me/earthme/lophine/config/LophineConfig.java
|
||||
@@ -1,0 +_,33 @@
|
||||
+package me.earthme.lophine.config;
|
||||
+
|
||||
+import me.earthme.luminol.config.LuminolConfig;
|
||||
+
|
||||
+import java.io.File;
|
||||
+import java.io.IOException;
|
||||
+import java.util.HashMap;
|
||||
+import java.util.Map;
|
||||
+
|
||||
+public class LophineConfig {
|
||||
+ public static final Map<String, LuminolConfig> configfiles = new HashMap<>();
|
||||
+ private static final File luminolConfigFolder = new File("luminol_config");
|
||||
+ private static final File lophineConfigFolder = new File("lophine_config");
|
||||
+
|
||||
+ public static void initConfigs() throws IOException {
|
||||
+ configfiles.put("luminol", new LuminolConfig(luminolConfigFolder, "luminol", "me.earthme.luminol.config.modules"));
|
||||
+ configfiles.put("lophine", new LuminolConfig(lophineConfigFolder, "lophine", "me.earthme.lophine.config.modules"));
|
||||
+ preLoad();
|
||||
+ }
|
||||
+
|
||||
+ public static void preLoad() throws IOException {
|
||||
+ for (LuminolConfig config : configfiles.values()) {
|
||||
+ config.preLoadConfig();
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ public static void loadConfigFiles() {
|
||||
+ for (LuminolConfig config : configfiles.values()) {
|
||||
+ config.finalizeLoadConfig();
|
||||
+ config.setupLatch();
|
||||
+ }
|
||||
+ }
|
||||
+}
|
||||
-30
@@ -1,30 +0,0 @@
|
||||
--- /dev/null
|
||||
+++ b/src/main/java/me/earthme/lophine/config/modules/experiment/CommandConfig.java
|
||||
@@ -1,0 +_,27 @@
|
||||
+package me.earthme.lophine.config.modules.experiment;
|
||||
+
|
||||
+import me.earthme.luminol.config.EnumConfigCategory;
|
||||
+import me.earthme.luminol.config.IConfigModule;
|
||||
+import me.earthme.luminol.config.flags.ConfigInfo;
|
||||
+
|
||||
+public class CommandConfig implements IConfigModule {
|
||||
+ @ConfigInfo(baseName = "command_block_enabled", comments =
|
||||
+ """
|
||||
+ Allow to use command block""")
|
||||
+ public static boolean block = false;
|
||||
+
|
||||
+ @ConfigInfo(baseName = "tick_command_enabled", comments =
|
||||
+ """
|
||||
+ Allow to use tick command""")
|
||||
+ public static boolean tick = false;
|
||||
+
|
||||
+ @Override
|
||||
+ public EnumConfigCategory getCategory() {
|
||||
+ return EnumConfigCategory.EXPERIMENT;
|
||||
+ }
|
||||
+
|
||||
+ @Override
|
||||
+ public String getBaseName() {
|
||||
+ return "command";
|
||||
+ }
|
||||
+}
|
||||
-25
@@ -1,25 +0,0 @@
|
||||
--- /dev/null
|
||||
+++ b/src/main/java/me/earthme/lophine/config/modules/experiment/EntityDamageSourceTraceConfig.java
|
||||
@@ -1,0 +_,22 @@
|
||||
+package me.earthme.lophine.config.modules.experiment;
|
||||
+
|
||||
+import me.earthme.luminol.config.EnumConfigCategory;
|
||||
+import me.earthme.luminol.config.IConfigModule;
|
||||
+import me.earthme.luminol.config.flags.ConfigInfo;
|
||||
+
|
||||
+public class EntityDamageSourceTraceConfig implements IConfigModule {
|
||||
+ @ConfigInfo(baseName = "enabled", comments =
|
||||
+ """
|
||||
+ Allow trace damage source cross different Region Scheduler.""")
|
||||
+ public static boolean enabled = false;
|
||||
+
|
||||
+ @Override
|
||||
+ public EnumConfigCategory getCategory() {
|
||||
+ return EnumConfigCategory.EXPERIMENT;
|
||||
+ }
|
||||
+
|
||||
+ @Override
|
||||
+ public String getBaseName() {
|
||||
+ return "entity_damage_source_trace";
|
||||
+ }
|
||||
+}
|
||||
-31
@@ -1,31 +0,0 @@
|
||||
--- /dev/null
|
||||
+++ b/src/main/java/me/earthme/lophine/config/modules/experiment/RayTrackingEntityTrackerConfig.java
|
||||
@@ -1,0 +_,28 @@
|
||||
+package me.earthme.lophine.config.modules.experiment;
|
||||
+
|
||||
+import me.earthme.luminol.config.EnumConfigCategory;
|
||||
+import me.earthme.luminol.config.IConfigModule;
|
||||
+import me.earthme.luminol.config.flags.ConfigInfo;
|
||||
+
|
||||
+public class RayTrackingEntityTrackerConfig implements IConfigModule {
|
||||
+ @ConfigInfo(baseName = "enabled")
|
||||
+ public static boolean enabled = false;
|
||||
+ @ConfigInfo(baseName = "skip_marker_armor_stands")
|
||||
+ public static boolean skipMarkerArmorStands = true;
|
||||
+ @ConfigInfo(baseName = "check_interval_ms")
|
||||
+ public static int checkIntervalMs = 10;
|
||||
+ @ConfigInfo(baseName = "tracing_distance")
|
||||
+ public static int tracingDistance = 48;
|
||||
+ @ConfigInfo(baseName = "hitbox_limit")
|
||||
+ public static int hitboxLimit = 50;
|
||||
+
|
||||
+ @Override
|
||||
+ public EnumConfigCategory getCategory() {
|
||||
+ return EnumConfigCategory.EXPERIMENT;
|
||||
+ }
|
||||
+
|
||||
+ @Override
|
||||
+ public String getBaseName() {
|
||||
+ return "ray_tracking_entity_tracker";
|
||||
+ }
|
||||
+}
|
||||
-40
@@ -1,40 +0,0 @@
|
||||
--- /dev/null
|
||||
+++ b/src/main/java/me/earthme/lophine/config/modules/misc/ContainerExpansionConfig.java
|
||||
@@ -1,0 +_,37 @@
|
||||
+package me.earthme.lophine.config.modules.misc;
|
||||
+
|
||||
+import me.earthme.luminol.config.EnumConfigCategory;
|
||||
+import me.earthme.luminol.config.IConfigModule;
|
||||
+import me.earthme.luminol.config.flags.ConfigInfo;
|
||||
+
|
||||
+public class ContainerExpansionConfig implements IConfigModule {
|
||||
+ @ConfigInfo(baseName = "barrel_rows", comments =
|
||||
+ """
|
||||
+ range: 1~6""")
|
||||
+ public static int barrelRows = 3;
|
||||
+
|
||||
+ @ConfigInfo(baseName = "enderchest_rows", comments =
|
||||
+ """
|
||||
+ range: 1~6""")
|
||||
+ public static int enderchestRows = 3;
|
||||
+
|
||||
+ @ConfigInfo(baseName = "shulker_stackable_count", comments =
|
||||
+ """
|
||||
+ range: 1~64""")
|
||||
+ public static int shulkerCount = 1;
|
||||
+
|
||||
+ @ConfigInfo(baseName = "better_shulker_box", comments =
|
||||
+ """
|
||||
+ Enable sneak + use to open shulker box.""")
|
||||
+ public static boolean betterShulker = false;
|
||||
+
|
||||
+ @Override
|
||||
+ public EnumConfigCategory getCategory() {
|
||||
+ return EnumConfigCategory.MISC;
|
||||
+ }
|
||||
+
|
||||
+ @Override
|
||||
+ public String getBaseName() {
|
||||
+ return "container_expansion";
|
||||
+ }
|
||||
+}
|
||||
-26
@@ -1,26 +0,0 @@
|
||||
--- /dev/null
|
||||
+++ b/src/main/java/me/earthme/lophine/config/modules/misc/DisableEndCrystalCheckConfig.java
|
||||
@@ -1,0 +_,23 @@
|
||||
+package me.earthme.lophine.config.modules.misc;
|
||||
+
|
||||
+import me.earthme.luminol.config.EnumConfigCategory;
|
||||
+import me.earthme.luminol.config.IConfigModule;
|
||||
+import me.earthme.luminol.config.flags.ConfigInfo;
|
||||
+
|
||||
+public class DisableEndCrystalCheckConfig implements IConfigModule {
|
||||
+ @ConfigInfo(baseName = "disable_end_crystal_check", comments =
|
||||
+ """
|
||||
+ Disable paper's End Crystal position check.
|
||||
+ It reverts to vanilla respawn dragon logic.""")
|
||||
+ public static boolean disableCheck = false;
|
||||
+
|
||||
+ @Override
|
||||
+ public EnumConfigCategory getCategory() {
|
||||
+ return EnumConfigCategory.MISC;
|
||||
+ }
|
||||
+
|
||||
+ @Override
|
||||
+ public String getBaseName() {
|
||||
+ return "end_crystal";
|
||||
+ }
|
||||
+}
|
||||
-29
@@ -1,29 +0,0 @@
|
||||
--- /dev/null
|
||||
+++ b/src/main/java/me/earthme/lophine/config/modules/misc/OldFeatureConfig.java
|
||||
@@ -1,0 +_,26 @@
|
||||
+package me.earthme.lophine.config.modules.misc;
|
||||
+
|
||||
+import me.earthme.luminol.config.EnumConfigCategory;
|
||||
+import me.earthme.luminol.config.IConfigModule;
|
||||
+import me.earthme.luminol.config.flags.ConfigInfo;
|
||||
+
|
||||
+public class OldFeatureConfig implements IConfigModule {
|
||||
+ @ConfigInfo(baseName = "old_nether_portal_collision")
|
||||
+ public static boolean oldNetherPortalCollision = false;
|
||||
+
|
||||
+ @ConfigInfo(baseName = "spawn_invulnerable_time")
|
||||
+ public static boolean spawnInvulnerableTime = false;
|
||||
+
|
||||
+ @ConfigInfo(baseName = "old_zombie_reinforcement")
|
||||
+ public static boolean oldZombieReinforcement = false;
|
||||
+
|
||||
+ @Override
|
||||
+ public EnumConfigCategory getCategory() {
|
||||
+ return EnumConfigCategory.MISC;
|
||||
+ }
|
||||
+
|
||||
+ @Override
|
||||
+ public String getBaseName() {
|
||||
+ return "old-feature";
|
||||
+ }
|
||||
+}
|
||||
-57
@@ -1,57 +0,0 @@
|
||||
--- /dev/null
|
||||
+++ b/src/main/java/me/earthme/lophine/config/modules/misc/RaidChangesConfig.java
|
||||
@@ -1,0 +_,54 @@
|
||||
+package me.earthme.lophine.config.modules.misc;
|
||||
+
|
||||
+import me.earthme.luminol.config.EnumConfigCategory;
|
||||
+import me.earthme.luminol.config.IConfigModule;
|
||||
+import me.earthme.luminol.config.flags.ConfigInfo;
|
||||
+
|
||||
+public class RaidChangesConfig implements IConfigModule {
|
||||
+ @ConfigInfo(baseName = "allow_skip_cooldown", comments =
|
||||
+ """
|
||||
+ Allow players with bad omen to skip a
|
||||
+ 30-second cooldown and trigger attacks directly""")
|
||||
+ public static boolean trigger = false;
|
||||
+
|
||||
+ @ConfigInfo(baseName = "give_bad_omen_when_kill_raid_captain", comments =
|
||||
+ """
|
||||
+ Enable players to obtain a bad omen
|
||||
+ effect when killing the raid captain""")
|
||||
+ public static boolean effect = false;
|
||||
+
|
||||
+ @ConfigInfo(baseName = "bad_omen_infinite", comments =
|
||||
+ """
|
||||
+ Enable bad omen effect infinite time
|
||||
+ --- this config is not old version's function""")
|
||||
+ public static boolean infinite = false;
|
||||
+
|
||||
+ @ConfigInfo(baseName = "skip_height_check", comments =
|
||||
+ """
|
||||
+ Disable y <= 96 check.
|
||||
+ If you enabled use_old_position_find, this config
|
||||
+ will useless and always behavior of enabled""")
|
||||
+ public static boolean heightCheck = false;
|
||||
+
|
||||
+ @ConfigInfo(baseName = "skip_self_raid_check", comments =
|
||||
+ """
|
||||
+ Disable raid self check
|
||||
+ --- this config is not old version's function""")
|
||||
+ public static boolean selfCheck = false;
|
||||
+
|
||||
+ @ConfigInfo(baseName = "use_old_position_find", comments =
|
||||
+ """
|
||||
+ Revert Old raid's find spawn position logic
|
||||
+ --- This revert MC-274911""")
|
||||
+ public static boolean posRevert = false;
|
||||
+
|
||||
+ @Override
|
||||
+ public EnumConfigCategory getCategory() {
|
||||
+ return EnumConfigCategory.MISC;
|
||||
+ }
|
||||
+
|
||||
+ @Override
|
||||
+ public String getBaseName() {
|
||||
+ return "revert_raid_changes";
|
||||
+ }
|
||||
+}
|
||||
-25
@@ -1,25 +0,0 @@
|
||||
--- /dev/null
|
||||
+++ b/src/main/java/me/earthme/lophine/config/modules/optimizations/LanguageConfig.java
|
||||
@@ -1,0 +_,22 @@
|
||||
+package me.earthme.lophine.config.modules.optimizations;
|
||||
+
|
||||
+import me.earthme.luminol.config.EnumConfigCategory;
|
||||
+import me.earthme.luminol.config.IConfigModule;
|
||||
+import me.earthme.luminol.config.flags.ConfigInfo;
|
||||
+
|
||||
+public class LanguageConfig implements IConfigModule {
|
||||
+ @ConfigInfo(baseName = "lang", comments = """
|
||||
+ Please use the key from https://minecraft.wiki/w/Language
|
||||
+ Sample of format: en_us zh_cn zh_hk zh_tw""")
|
||||
+ public static String lang = "en_us";
|
||||
+
|
||||
+ @Override
|
||||
+ public EnumConfigCategory getCategory() {
|
||||
+ return EnumConfigCategory.OPTIMIZATIONS;
|
||||
+ }
|
||||
+
|
||||
+ @Override
|
||||
+ public String getBaseName() {
|
||||
+ return "language";
|
||||
+ }
|
||||
+}
|
||||
-27
@@ -1,27 +0,0 @@
|
||||
--- /dev/null
|
||||
+++ b/src/main/java/me/earthme/lophine/config/modules/removed/RemovedConfig.java
|
||||
@@ -1,0 +_,24 @@
|
||||
+package me.earthme.lophine.config.modules.removed;
|
||||
+
|
||||
+import me.earthme.luminol.config.EnumConfigCategory;
|
||||
+import me.earthme.luminol.config.IConfigModule;
|
||||
+import me.earthme.luminol.config.flags.ConfigInfo;
|
||||
+import me.earthme.luminol.config.flags.TransformedConfig;
|
||||
+
|
||||
+public class RemovedConfig implements IConfigModule {
|
||||
+ @TransformedConfig(name = "example", category = {"removed", "example"}, transform = false)
|
||||
+ @ConfigInfo(baseName = "removed", comments =
|
||||
+ """
|
||||
+ RemovedConfig redirect to here, no any function.""")
|
||||
+ public static boolean enabled = true;
|
||||
+
|
||||
+ @Override
|
||||
+ public EnumConfigCategory getCategory() {
|
||||
+ return EnumConfigCategory.REMOVED;
|
||||
+ }
|
||||
+
|
||||
+ @Override
|
||||
+ public String getBaseName() {
|
||||
+ return "removed_config";
|
||||
+ }
|
||||
+}
|
||||
-254
@@ -1,254 +0,0 @@
|
||||
--- /dev/null
|
||||
+++ b/src/main/java/me/earthme/lophine/utils/ServerI18nUtil.java
|
||||
@@ -1,0 +_,251 @@
|
||||
+package me.earthme.lophine.utils;
|
||||
+
|
||||
+import com.google.gson.JsonElement;
|
||||
+import com.google.gson.JsonObject;
|
||||
+import com.google.gson.JsonParser;
|
||||
+import com.google.gson.JsonSyntaxException;
|
||||
+import me.earthme.lophine.config.modules.optimizations.LanguageConfig;
|
||||
+import net.minecraft.locale.DeprecatedTranslationsInfo;
|
||||
+import net.minecraft.locale.Language;
|
||||
+import net.minecraft.network.chat.FormattedText;
|
||||
+import net.minecraft.network.chat.Style;
|
||||
+import net.minecraft.util.FormattedCharSequence;
|
||||
+import net.minecraft.util.StringDecomposer;
|
||||
+import org.apache.commons.io.FileUtils;
|
||||
+import org.jetbrains.annotations.NotNull;
|
||||
+
|
||||
+import java.io.IOException;
|
||||
+import java.io.InputStream;
|
||||
+import java.io.UnsupportedEncodingException;
|
||||
+import java.net.URI;
|
||||
+import java.net.http.HttpClient;
|
||||
+import java.net.http.HttpRequest;
|
||||
+import java.net.http.HttpResponse;
|
||||
+import java.nio.file.*;
|
||||
+import java.util.HashMap;
|
||||
+import java.util.Map;
|
||||
+import java.util.Objects;
|
||||
+import java.util.Optional;
|
||||
+import java.util.concurrent.CompletableFuture;
|
||||
+import java.util.function.BiConsumer;
|
||||
+import java.util.logging.Logger;
|
||||
+
|
||||
+/*
|
||||
+* authored by: Helvetica Volubi <suisuroru@blue-millennium.fun>
|
||||
+* modified by: Lumine1909 <133463833+Lumine1909@users.noreply.github.com>
|
||||
+* Some of diff form Leaves
|
||||
+*/
|
||||
+public class ServerI18nUtil {
|
||||
+
|
||||
+ private static final Logger logger = Logger.getLogger("LangLoader");
|
||||
+ private static final String VERSION = "1.21.5";
|
||||
+ private static final String BASE_PATH = "cache/lophine/" + VERSION + "/";
|
||||
+ private static final String manifestUrl = "https://launchermeta.mojang.com/mc/game/version_manifest.json";
|
||||
+ private static final String resourceBaseUrl = "https://resources.download.minecraft.net/";
|
||||
+
|
||||
+ private static String langPath;
|
||||
+ private static String assetsPath;
|
||||
+ private static String versionPath;
|
||||
+ private static String manifestPath;
|
||||
+ private static String langJsonPath;
|
||||
+
|
||||
+ public static void init() {
|
||||
+ if (Objects.equals(LanguageConfig.lang, "en_us")) {
|
||||
+ return;
|
||||
+ }
|
||||
+ langPath = BASE_PATH + "lang/" + LanguageConfig.lang + ".json";
|
||||
+ assetsPath = BASE_PATH + "assets.json";
|
||||
+ versionPath = BASE_PATH + VERSION + ".json";
|
||||
+ manifestPath = BASE_PATH + "manifest.json";
|
||||
+ langJsonPath = "minecraft/lang/" + LanguageConfig.lang + ".json";
|
||||
+ logger.info("Starting load language: " + LanguageConfig.lang);
|
||||
+ CompletableFuture.runAsync(() -> loadI18n(LanguageConfig.lang, 2));
|
||||
+ }
|
||||
+
|
||||
+ private static void loadI18n(String lang, int retryTime) {
|
||||
+ try {
|
||||
+ if (!Files.exists(Path.of(langPath))) {
|
||||
+ downloadLang(true);
|
||||
+ }
|
||||
+ Language.inject(createLangInstance());
|
||||
+ logger.info("Successfully loaded language: " + lang);
|
||||
+ } catch (Exception e) {
|
||||
+ logger.warning("Failed to load language file for " + lang + "\n" + e);
|
||||
+ if (retryTime > 0) {
|
||||
+ cleanCache();
|
||||
+ loadI18n(lang, retryTime - 1);
|
||||
+ } else {
|
||||
+ logger.severe("Failed to load for many times, use default lang \"en_us\" instead");
|
||||
+ cleanCache();
|
||||
+ }
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ private static void downloadLang(boolean fetchFromAssets) throws Exception {
|
||||
+ JsonObject json;
|
||||
+ if (!Files.exists(Path.of(assetsPath)) || (json = loadJson(assetsPath)) == null) {
|
||||
+ if (fetchFromAssets) {
|
||||
+ downloadAssets(true);
|
||||
+ downloadLang(false);
|
||||
+ }
|
||||
+ return;
|
||||
+ }
|
||||
+
|
||||
+ JsonObject langEntry = json.getAsJsonObject("objects").getAsJsonObject(langJsonPath);
|
||||
+
|
||||
+ String hash = langEntry.get("hash").getAsString();
|
||||
+ if (hash == null || hash.length() < 2) {
|
||||
+ throw new IllegalArgumentException("Invalid hash value");
|
||||
+ }
|
||||
+
|
||||
+ String langUrl = resourceBaseUrl + hash.substring(0, 2) + "/" + hash;
|
||||
+ fetchAndSave(langUrl, langPath);
|
||||
+ }
|
||||
+
|
||||
+ private static void downloadAssets(boolean fetchFromVersion) throws Exception {
|
||||
+ JsonObject json;
|
||||
+ if (!Files.exists(Path.of(versionPath)) || (json = loadJson(versionPath)) == null) {
|
||||
+ if (fetchFromVersion) {
|
||||
+ downloadVersion(true);
|
||||
+ downloadAssets(false);
|
||||
+ }
|
||||
+ return;
|
||||
+ }
|
||||
+
|
||||
+ JsonObject assetIndex = json.getAsJsonObject("assetIndex");
|
||||
+ String assetUrl = assetIndex.get("url").getAsString();
|
||||
+ fetchAndSave(assetUrl, assetsPath);
|
||||
+ }
|
||||
+
|
||||
+ private static void downloadVersion(boolean fetchFromManifest) throws Exception {
|
||||
+ JsonObject json;
|
||||
+ if (!Files.exists(Path.of(manifestPath)) || (json = loadJson(manifestPath)) == null) {
|
||||
+ if (fetchFromManifest) {
|
||||
+ fetchAndSave(manifestUrl, manifestPath);
|
||||
+ downloadVersion(false);
|
||||
+ }
|
||||
+ return;
|
||||
+ }
|
||||
+
|
||||
+ String versionUrl = null;
|
||||
+ for (JsonElement element : json.getAsJsonArray("versions")) {
|
||||
+ String id = element.getAsJsonObject().get("id").getAsString();
|
||||
+ String url = element.getAsJsonObject().get("url").getAsString();
|
||||
+ if (VERSION.equals(id)) {
|
||||
+ versionUrl = url;
|
||||
+ break;
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ if (versionUrl == null) {
|
||||
+ throw new RuntimeException("Could not find version URL");
|
||||
+ }
|
||||
+
|
||||
+ fetchAndSave(versionUrl, versionPath);
|
||||
+ }
|
||||
+
|
||||
+ private static String createHttpResponse(String path) throws IOException, InterruptedException {
|
||||
+ try {
|
||||
+ HttpResponse<String> response;
|
||||
+ try (HttpClient httpClient = HttpClient.newHttpClient()) {
|
||||
+
|
||||
+ HttpRequest request = HttpRequest.newBuilder()
|
||||
+ .uri(URI.create(path))
|
||||
+ .build();
|
||||
+
|
||||
+ response = httpClient.send(request, HttpResponse.BodyHandlers.ofString());
|
||||
+ }
|
||||
+
|
||||
+ int responseCode = response.statusCode();
|
||||
+ if (responseCode != 200) {
|
||||
+ logger.info("Unexpected response code: " + responseCode);
|
||||
+ logger.info("Response body: " + response.body());
|
||||
+ throw new UnsupportedEncodingException("Unexpected response code");
|
||||
+ } else {
|
||||
+ return response.body();
|
||||
+ }
|
||||
+ } catch (Exception e) {
|
||||
+ logger.warning("Error in getting info: " + e.getMessage());
|
||||
+ throw e;
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ private static byte[] fetch(String urlString) throws IOException, InterruptedException {
|
||||
+ String ret = createHttpResponse(urlString);
|
||||
+ return ret.getBytes();
|
||||
+ }
|
||||
+
|
||||
+ private static void fetchAndSave(String url, String savePath) throws IOException, InterruptedException {
|
||||
+ byte[] data = fetch(url);
|
||||
+ Path outputPath = Path.of(savePath);
|
||||
+ Files.createDirectories(outputPath.getParent());
|
||||
+ Files.write(outputPath, data, StandardOpenOption.CREATE, StandardOpenOption.WRITE);
|
||||
+ }
|
||||
+
|
||||
+ private static void cleanCache() {
|
||||
+ try {
|
||||
+ FileUtils.deleteDirectory(Path.of(BASE_PATH).toFile());
|
||||
+ } catch (IOException e) {
|
||||
+ logger.severe("Cache cleanup failed: " + e);
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ private static JsonObject loadJson(String path) {
|
||||
+ try {
|
||||
+ byte[] data = Files.readAllBytes(Paths.get(path));
|
||||
+ return JsonParser.parseString(new String(data)).getAsJsonObject();
|
||||
+ } catch (JsonSyntaxException e) {
|
||||
+ logger.warning("Corrupt json file: " + e);
|
||||
+ throw e;
|
||||
+ } catch (Exception e) {
|
||||
+ logger.warning("Failed to load local JSON: " + e);
|
||||
+ return null;
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ private static Language createLangInstance() throws IOException {
|
||||
+ DeprecatedTranslationsInfo deprecatedTranslationsInfo = DeprecatedTranslationsInfo.loadFromDefaultResource();
|
||||
+ Map<String, String> map = new HashMap<>();
|
||||
+ parseTranslations(map::put);
|
||||
+ deprecatedTranslationsInfo.applyToMap(map);
|
||||
+ final Map<String, String> map1 = Map.copyOf(map);
|
||||
+ return new Language() {
|
||||
+ @Override
|
||||
+ public @NotNull String getOrDefault(@NotNull String key, @NotNull String defaultValue) {
|
||||
+ return map1.getOrDefault(key, defaultValue);
|
||||
+ }
|
||||
+
|
||||
+ @Override
|
||||
+ public boolean has(@NotNull String id) {
|
||||
+ return map1.containsKey(id);
|
||||
+ }
|
||||
+
|
||||
+ @Override
|
||||
+ public boolean isDefaultRightToLeft() {
|
||||
+ return false;
|
||||
+ }
|
||||
+
|
||||
+ @Override
|
||||
+ public @NotNull FormattedCharSequence getVisualOrder(@NotNull FormattedText text) {
|
||||
+ return sink -> text.visit(
|
||||
+ (style, content) -> StringDecomposer.iterateFormatted(content, style, sink) ? Optional.empty() : FormattedText.STOP_ITERATION,
|
||||
+ Style.EMPTY
|
||||
+ )
|
||||
+ .isPresent();
|
||||
+ }
|
||||
+ };
|
||||
+ }
|
||||
+
|
||||
+ private static void parseTranslations(BiConsumer<String, String> output) throws IOException {
|
||||
+ Path filePath = Path.of(langPath);
|
||||
+ try (InputStream fileStream = Files.newInputStream(filePath)) {
|
||||
+ Language.loadFromJson(fileStream, output);
|
||||
+ } catch (NoSuchFileException noSuchFileException) {
|
||||
+ logger.warning("Couldn't find language file: " + langPath);
|
||||
+ throw noSuchFileException;
|
||||
+ } catch (Exception e) {
|
||||
+ logger.warning("Failed to load language from filesystem " + filePath + "\n" + e);
|
||||
+ throw e;
|
||||
+ }
|
||||
+ }
|
||||
+}
|
||||
-156
@@ -1,156 +0,0 @@
|
||||
--- /dev/null
|
||||
+++ b/src/main/java/me/earthme/lophine/utils/ShulkerBoxesUtil.java
|
||||
@@ -1,0 +_,153 @@
|
||||
+package me.earthme.lophine.utils;
|
||||
+
|
||||
+import me.earthme.lophine.config.modules.misc.ContainerExpansionConfig;
|
||||
+import net.minecraft.core.NonNullList;
|
||||
+import net.minecraft.core.component.DataComponents;
|
||||
+import net.minecraft.nbt.CompoundTag;
|
||||
+import net.minecraft.server.level.ServerPlayer;
|
||||
+import net.minecraft.stats.Stats;
|
||||
+import net.minecraft.world.InteractionHand;
|
||||
+import net.minecraft.world.entity.player.Player;
|
||||
+import net.minecraft.world.item.BlockItem;
|
||||
+import net.minecraft.world.item.ItemStack;
|
||||
+import net.minecraft.world.item.component.CustomData;
|
||||
+import net.minecraft.world.item.component.ItemContainerContents;
|
||||
+import net.minecraft.world.level.block.ShulkerBoxBlock;
|
||||
+import net.minecraft.world.level.block.entity.ShulkerBoxBlockEntity;
|
||||
+import org.jetbrains.annotations.NotNull;
|
||||
+
|
||||
+import java.util.Objects;
|
||||
+import java.util.Optional;
|
||||
+
|
||||
+import static net.minecraft.world.inventory.AbstractContainerMenu.dropOrPlaceInInventory;
|
||||
+
|
||||
+public class ShulkerBoxesUtil {
|
||||
+ // Stackable ShulkerBoxes part
|
||||
+ public static boolean shouldCheck() {
|
||||
+ return ContainerExpansionConfig.shulkerCount > 1 && ContainerExpansionConfig.shulkerCount <= 64;
|
||||
+ }
|
||||
+
|
||||
+ public static boolean checkShulkerBox(ItemStack itemStack) {
|
||||
+ return shouldCheck() && checkIsShulkerBox(itemStack);
|
||||
+ }
|
||||
+
|
||||
+ public static boolean checkIsShulkerBox(ItemStack itemStack) {
|
||||
+ return itemStack.getItem() instanceof BlockItem b && b.getBlock() instanceof ShulkerBoxBlock;
|
||||
+ }
|
||||
+
|
||||
+ public static int getItemMaxCount(ItemStack itemStack) {
|
||||
+ if (checkShulkerBox(itemStack)) {
|
||||
+ return Math.clamp(ContainerExpansionConfig.shulkerCount, 1, 64);
|
||||
+ }
|
||||
+ return itemStack.getMaxStackSize();
|
||||
+ }
|
||||
+
|
||||
+ public static int getShulkerBoxesMaxCountUnsafe() {
|
||||
+ return Math.clamp(ContainerExpansionConfig.shulkerCount, 1, 64);
|
||||
+ }
|
||||
+
|
||||
+ public static boolean emptyShulkerBoxCheck(@NotNull ItemStack stack) {
|
||||
+ return stack.getComponents().getOrDefault(DataComponents.CONTAINER, ItemContainerContents.EMPTY).stream().findAny().isEmpty();
|
||||
+ }
|
||||
+
|
||||
+ public static boolean isStackable(ItemStack itemStack) {
|
||||
+ return getItemMaxCount(itemStack) > 1 && (!itemStack.isDamageableItem() || !itemStack.isDamaged());
|
||||
+ }
|
||||
+
|
||||
+ public static int getItemStackMaxCountReal(ItemStack stack) {
|
||||
+ CompoundTag nbt = Optional.ofNullable(stack.get(DataComponents.CUSTOM_DATA)).orElse(CustomData.EMPTY).copyTag();
|
||||
+ return nbt.getInt("Lophine.RealStackSize").orElse(stack.getMaxStackSize());
|
||||
+ }
|
||||
+
|
||||
+ public static ItemStack encodeMaxStackSize(ItemStack itemStack) {
|
||||
+ int realMaxStackSize = getItemStackMaxCountReal(itemStack);
|
||||
+ int modifiedMaxStackSize = getItemMaxCount(itemStack);
|
||||
+ if (itemStack.getMaxStackSize() != modifiedMaxStackSize) {
|
||||
+ itemStack.set(DataComponents.MAX_STACK_SIZE, modifiedMaxStackSize);
|
||||
+ CompoundTag nbt = itemStack.getOrDefault(DataComponents.CUSTOM_DATA, CustomData.EMPTY).copyTag();
|
||||
+ nbt.putInt("Lophine.RealStackSize", realMaxStackSize);
|
||||
+ itemStack.set(DataComponents.CUSTOM_DATA, CustomData.of(nbt));
|
||||
+ }
|
||||
+ return itemStack;
|
||||
+ }
|
||||
+
|
||||
+ public static ItemStack decodeMaxStackSize(ItemStack itemStack) {
|
||||
+ int realMaxStackSize = getItemStackMaxCountReal(itemStack);
|
||||
+ if (itemStack.getMaxStackSize() != realMaxStackSize) {
|
||||
+ itemStack.set(DataComponents.MAX_STACK_SIZE, realMaxStackSize);
|
||||
+ CompoundTag nbt = itemStack.getOrDefault(DataComponents.CUSTOM_DATA, CustomData.EMPTY).copyTag();
|
||||
+ nbt.remove("Lophine.RealStackSize");
|
||||
+ if (nbt.isEmpty()) {
|
||||
+ itemStack.remove(DataComponents.CUSTOM_DATA);
|
||||
+ } else {
|
||||
+ itemStack.set(DataComponents.CUSTOM_DATA, CustomData.of(nbt));
|
||||
+ }
|
||||
+ }
|
||||
+ return itemStack;
|
||||
+ }
|
||||
+
|
||||
+ // Better ShulkerBox part
|
||||
+ public static boolean checkIfCanOpen(ItemStack itemStack) {
|
||||
+ return checkIsShulkerBox(itemStack) && itemStack.getCount() == 1;
|
||||
+ }
|
||||
+
|
||||
+ public static void openShulkerBox(Player player, ItemStack itemInHand, InteractionHand hand) {
|
||||
+ ShulkerBoxBlockEntity shulkerBoxEntity = new ShulkerBoxBlockEntity(
|
||||
+ player.blockPosition(),
|
||||
+ ((BlockItem) itemInHand.getItem()).getBlock().defaultBlockState()
|
||||
+ );
|
||||
+ ItemContainerContents container = itemInHand.getOrDefault(
|
||||
+ DataComponents.CONTAINER,
|
||||
+ ItemContainerContents.EMPTY
|
||||
+ );
|
||||
+
|
||||
+ NonNullList<ItemStack> items = NonNullList.withSize(27, ItemStack.EMPTY);
|
||||
+ for (int i = 0; i < container.items.size(); i++) {
|
||||
+ items.set(i, container.items.get(i));
|
||||
+ }
|
||||
+
|
||||
+ shulkerBoxEntity.setItems(items);
|
||||
+
|
||||
+ shulkerBoxEntity.setLevel(player.level());
|
||||
+ shulkerBoxEntity.haveRealBlock = false;
|
||||
+ shulkerBoxEntity.createPlayer = player;
|
||||
+ player.shulkerHand = hand;
|
||||
+ player.shulkerOpen = true;
|
||||
+
|
||||
+ if (player.openMenu(shulkerBoxEntity).isPresent()) {
|
||||
+ player.awardStat(Stats.OPEN_SHULKER_BOX);
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ public static void shulkerBoxEntityCallBack(ShulkerBoxBlockEntity shulkerBoxEntity) {
|
||||
+ Player player = shulkerBoxEntity.createPlayer;
|
||||
+ if (player.shulkerOpen) {
|
||||
+ InteractionHand hand = player.shulkerHand;
|
||||
+ ItemStack currentItem = player.getItemInHand(hand);
|
||||
+
|
||||
+ currentItem.set(DataComponents.CONTAINER, ItemContainerContents.fromItems(shulkerBoxEntity.getItems()));
|
||||
+
|
||||
+ player.setItemInHand(hand, currentItem);
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ public static void inventoryCallBack(boolean isMainHand, Player player) {
|
||||
+ if (player.shulkerOpen) {
|
||||
+ if (isMainHand == Objects.equals(player.shulkerHand, InteractionHand.MAIN_HAND)) {
|
||||
+ player.pendingClosingShulker = true;
|
||||
+ closeScreen(player);
|
||||
+ player.pendingClosingShulker = false;
|
||||
+ }
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ public static void closeScreen(Player player) {
|
||||
+ if (player instanceof ServerPlayer) {
|
||||
+ ItemStack stack = player.containerMenu.getCarried();
|
||||
+ if (!stack.isEmpty()) {
|
||||
+ dropOrPlaceInInventory(player, stack);
|
||||
+ }
|
||||
+ }
|
||||
+ player.closeContainer();
|
||||
+ }
|
||||
+}
|
||||
@@ -0,0 +1,34 @@
|
||||
package com.logisticscraft.occlusionculling;
|
||||
|
||||
import com.logisticscraft.occlusionculling.util.Vec3d;
|
||||
|
||||
public interface DataProvider {
|
||||
|
||||
/**
|
||||
* Prepares the requested chunk. Returns true if the chunk is ready, false when
|
||||
* not loaded. Should not reload the chunk when the x and y are the same as the
|
||||
* last request!
|
||||
*
|
||||
* @param chunkX
|
||||
* @param chunkZ
|
||||
* @return
|
||||
*/
|
||||
boolean prepareChunk(int chunkX, int chunkZ);
|
||||
|
||||
/**
|
||||
* Location is inside the chunk.
|
||||
*
|
||||
* @param x
|
||||
* @param y
|
||||
* @param z
|
||||
* @return
|
||||
*/
|
||||
boolean isOpaqueFullCube(int x, int y, int z);
|
||||
|
||||
default void cleanup() {
|
||||
}
|
||||
|
||||
default void checkingPosition(Vec3d[] targetPoints, int size, Vec3d viewerPosition) {
|
||||
}
|
||||
|
||||
}
|
||||
+515
@@ -0,0 +1,515 @@
|
||||
package com.logisticscraft.occlusionculling;
|
||||
|
||||
import com.logisticscraft.occlusionculling.cache.ArrayOcclusionCache;
|
||||
import com.logisticscraft.occlusionculling.cache.OcclusionCache;
|
||||
import com.logisticscraft.occlusionculling.util.MathUtilities;
|
||||
import com.logisticscraft.occlusionculling.util.Vec3d;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.BitSet;
|
||||
|
||||
public class OcclusionCullingInstance {
|
||||
|
||||
private static final int ON_MIN_X = 0x01;
|
||||
private static final int ON_MAX_X = 0x02;
|
||||
private static final int ON_MIN_Y = 0x04;
|
||||
private static final int ON_MAX_Y = 0x08;
|
||||
private static final int ON_MIN_Z = 0x10;
|
||||
private static final int ON_MAX_Z = 0x20;
|
||||
|
||||
private final int reach;
|
||||
private final double aabbExpansion;
|
||||
private final DataProvider provider;
|
||||
private final OcclusionCache cache;
|
||||
|
||||
// Reused allocated data structures
|
||||
private final BitSet skipList = new BitSet(); // Grows bigger in case some mod introduces giant hitboxes
|
||||
private final Vec3d[] targetPoints = new Vec3d[15];
|
||||
private final Vec3d targetPos = new Vec3d(0, 0, 0);
|
||||
private final int[] cameraPos = new int[3];
|
||||
private final boolean[] dotselectors = new boolean[14];
|
||||
private boolean allowRayChecks = false;
|
||||
private final int[] lastHitBlock = new int[3];
|
||||
private boolean allowWallClipping = false;
|
||||
|
||||
|
||||
public OcclusionCullingInstance(int maxDistance, DataProvider provider) {
|
||||
this(maxDistance, provider, new ArrayOcclusionCache(maxDistance), 0.5);
|
||||
}
|
||||
|
||||
public OcclusionCullingInstance(int maxDistance, DataProvider provider, OcclusionCache cache, double aabbExpansion) {
|
||||
this.reach = maxDistance;
|
||||
this.provider = provider;
|
||||
this.cache = cache;
|
||||
this.aabbExpansion = aabbExpansion;
|
||||
for (int i = 0; i < targetPoints.length; i++) {
|
||||
targetPoints[i] = new Vec3d(0, 0, 0);
|
||||
}
|
||||
}
|
||||
|
||||
public boolean isAABBVisible(Vec3d aabbMin, Vec3d aabbMax, Vec3d viewerPosition) {
|
||||
try {
|
||||
int maxX = MathUtilities.floor(aabbMax.x
|
||||
+ aabbExpansion);
|
||||
int maxY = MathUtilities.floor(aabbMax.y
|
||||
+ aabbExpansion);
|
||||
int maxZ = MathUtilities.floor(aabbMax.z
|
||||
+ aabbExpansion);
|
||||
int minX = MathUtilities.floor(aabbMin.x
|
||||
- aabbExpansion);
|
||||
int minY = MathUtilities.floor(aabbMin.y
|
||||
- aabbExpansion);
|
||||
int minZ = MathUtilities.floor(aabbMin.z
|
||||
- aabbExpansion);
|
||||
|
||||
cameraPos[0] = MathUtilities.floor(viewerPosition.x);
|
||||
cameraPos[1] = MathUtilities.floor(viewerPosition.y);
|
||||
cameraPos[2] = MathUtilities.floor(viewerPosition.z);
|
||||
|
||||
Relative relX = Relative.from(minX, maxX, cameraPos[0]);
|
||||
Relative relY = Relative.from(minY, maxY, cameraPos[1]);
|
||||
Relative relZ = Relative.from(minZ, maxZ, cameraPos[2]);
|
||||
|
||||
if (relX == Relative.INSIDE && relY == Relative.INSIDE && relZ == Relative.INSIDE) {
|
||||
return true; // We are inside of the AABB, don't cull
|
||||
}
|
||||
|
||||
skipList.clear();
|
||||
|
||||
// Just check the cache first
|
||||
int id = 0;
|
||||
for (int x = minX; x <= maxX; x++) {
|
||||
for (int y = minY; y <= maxY; y++) {
|
||||
for (int z = minZ; z <= maxZ; z++) {
|
||||
int cachedValue = getCacheValue(x, y, z);
|
||||
|
||||
if (cachedValue == 1) {
|
||||
// non-occluding
|
||||
return true;
|
||||
}
|
||||
|
||||
if (cachedValue != 0) {
|
||||
// was checked and it wasn't visible
|
||||
skipList.set(id);
|
||||
}
|
||||
id++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// only after the first hit wall the cache becomes valid.
|
||||
allowRayChecks = false;
|
||||
|
||||
// since the cache wasn't helpfull
|
||||
id = 0;
|
||||
for (int x = minX; x <= maxX; x++) {
|
||||
byte visibleOnFaceX = 0;
|
||||
byte faceEdgeDataX = 0;
|
||||
faceEdgeDataX |= (x == minX) ? ON_MIN_X : 0;
|
||||
faceEdgeDataX |= (x == maxX) ? ON_MAX_X : 0;
|
||||
visibleOnFaceX |= (x == minX && relX == Relative.POSITIVE) ? ON_MIN_X : 0;
|
||||
visibleOnFaceX |= (x == maxX && relX == Relative.NEGATIVE) ? ON_MAX_X : 0;
|
||||
for (int y = minY; y <= maxY; y++) {
|
||||
byte faceEdgeDataY = faceEdgeDataX;
|
||||
byte visibleOnFaceY = visibleOnFaceX;
|
||||
faceEdgeDataY |= (y == minY) ? ON_MIN_Y : 0;
|
||||
faceEdgeDataY |= (y == maxY) ? ON_MAX_Y : 0;
|
||||
visibleOnFaceY |= (y == minY && relY == Relative.POSITIVE) ? ON_MIN_Y : 0;
|
||||
visibleOnFaceY |= (y == maxY && relY == Relative.NEGATIVE) ? ON_MAX_Y : 0;
|
||||
for (int z = minZ; z <= maxZ; z++) {
|
||||
byte faceEdgeData = faceEdgeDataY;
|
||||
byte visibleOnFace = visibleOnFaceY;
|
||||
faceEdgeData |= (z == minZ) ? ON_MIN_Z : 0;
|
||||
faceEdgeData |= (z == maxZ) ? ON_MAX_Z : 0;
|
||||
visibleOnFace |= (z == minZ && relZ == Relative.POSITIVE) ? ON_MIN_Z : 0;
|
||||
visibleOnFace |= (z == maxZ && relZ == Relative.NEGATIVE) ? ON_MAX_Z : 0;
|
||||
if (skipList.get(id)) { // was checked and it wasn't visible
|
||||
id++;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (visibleOnFace != 0) {
|
||||
targetPos.set(x, y, z);
|
||||
if (isVoxelVisible(viewerPosition, targetPos, faceEdgeData, visibleOnFace)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
id++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
} catch (Throwable t) {
|
||||
// Failsafe
|
||||
t.printStackTrace();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param viewerPosition
|
||||
* @param position
|
||||
* @param faceData contains rather this Block is on the outside for a given face
|
||||
* @param visibleOnFace contains rather a face should be concidered
|
||||
* @return
|
||||
*/
|
||||
private boolean isVoxelVisible(Vec3d viewerPosition, Vec3d position, byte faceData, byte visibleOnFace) {
|
||||
int targetSize = 0;
|
||||
Arrays.fill(dotselectors, false);
|
||||
if ((visibleOnFace & ON_MIN_X) == ON_MIN_X) {
|
||||
dotselectors[0] = true;
|
||||
if ((faceData & ~ON_MIN_X) != 0) {
|
||||
dotselectors[1] = true;
|
||||
dotselectors[4] = true;
|
||||
dotselectors[5] = true;
|
||||
}
|
||||
dotselectors[8] = true;
|
||||
}
|
||||
if ((visibleOnFace & ON_MIN_Y) == ON_MIN_Y) {
|
||||
dotselectors[0] = true;
|
||||
if ((faceData & ~ON_MIN_Y) != 0) {
|
||||
dotselectors[3] = true;
|
||||
dotselectors[4] = true;
|
||||
dotselectors[7] = true;
|
||||
}
|
||||
dotselectors[9] = true;
|
||||
}
|
||||
if ((visibleOnFace & ON_MIN_Z) == ON_MIN_Z) {
|
||||
dotselectors[0] = true;
|
||||
if ((faceData & ~ON_MIN_Z) != 0) {
|
||||
dotselectors[1] = true;
|
||||
dotselectors[4] = true;
|
||||
dotselectors[5] = true;
|
||||
}
|
||||
dotselectors[10] = true;
|
||||
}
|
||||
if ((visibleOnFace & ON_MAX_X) == ON_MAX_X) {
|
||||
dotselectors[4] = true;
|
||||
if ((faceData & ~ON_MAX_X) != 0) {
|
||||
dotselectors[5] = true;
|
||||
dotselectors[6] = true;
|
||||
dotselectors[7] = true;
|
||||
}
|
||||
dotselectors[11] = true;
|
||||
}
|
||||
if ((visibleOnFace & ON_MAX_Y) == ON_MAX_Y) {
|
||||
dotselectors[1] = true;
|
||||
if ((faceData & ~ON_MAX_Y) != 0) {
|
||||
dotselectors[2] = true;
|
||||
dotselectors[5] = true;
|
||||
dotselectors[6] = true;
|
||||
}
|
||||
dotselectors[12] = true;
|
||||
}
|
||||
if ((visibleOnFace & ON_MAX_Z) == ON_MAX_Z) {
|
||||
dotselectors[2] = true;
|
||||
if ((faceData & ~ON_MAX_Z) != 0) {
|
||||
dotselectors[3] = true;
|
||||
dotselectors[6] = true;
|
||||
dotselectors[7] = true;
|
||||
}
|
||||
dotselectors[13] = true;
|
||||
}
|
||||
|
||||
if (dotselectors[0]) targetPoints[targetSize++].setAdd(position, 0.05, 0.05, 0.05);
|
||||
if (dotselectors[1]) targetPoints[targetSize++].setAdd(position, 0.05, 0.95, 0.05);
|
||||
if (dotselectors[2]) targetPoints[targetSize++].setAdd(position, 0.05, 0.95, 0.95);
|
||||
if (dotselectors[3]) targetPoints[targetSize++].setAdd(position, 0.05, 0.05, 0.95);
|
||||
if (dotselectors[4]) targetPoints[targetSize++].setAdd(position, 0.95, 0.05, 0.05);
|
||||
if (dotselectors[5]) targetPoints[targetSize++].setAdd(position, 0.95, 0.95, 0.05);
|
||||
if (dotselectors[6]) targetPoints[targetSize++].setAdd(position, 0.95, 0.95, 0.95);
|
||||
if (dotselectors[7]) targetPoints[targetSize++].setAdd(position, 0.95, 0.05, 0.95);
|
||||
// middle points
|
||||
if (dotselectors[8]) targetPoints[targetSize++].setAdd(position, 0.05, 0.5, 0.5);
|
||||
if (dotselectors[9]) targetPoints[targetSize++].setAdd(position, 0.5, 0.05, 0.5);
|
||||
if (dotselectors[10]) targetPoints[targetSize++].setAdd(position, 0.5, 0.5, 0.05);
|
||||
if (dotselectors[11]) targetPoints[targetSize++].setAdd(position, 0.95, 0.5, 0.5);
|
||||
if (dotselectors[12]) targetPoints[targetSize++].setAdd(position, 0.5, 0.95, 0.5);
|
||||
if (dotselectors[13]) targetPoints[targetSize++].setAdd(position, 0.5, 0.5, 0.95);
|
||||
|
||||
return isVisible(viewerPosition, targetPoints, targetSize);
|
||||
}
|
||||
|
||||
private boolean rayIntersection(int[] b, Vec3d rayOrigin, Vec3d rayDir) {
|
||||
Vec3d rInv = new Vec3d(1, 1, 1).div(rayDir);
|
||||
|
||||
double t1 = (b[0] - rayOrigin.x) * rInv.x;
|
||||
double t2 = (b[0] + 1 - rayOrigin.x) * rInv.x;
|
||||
double t3 = (b[1] - rayOrigin.y) * rInv.y;
|
||||
double t4 = (b[1] + 1 - rayOrigin.y) * rInv.y;
|
||||
double t5 = (b[2] - rayOrigin.z) * rInv.z;
|
||||
double t6 = (b[2] + 1 - rayOrigin.z) * rInv.z;
|
||||
|
||||
double tmin = Math.max(Math.max(Math.min(t1, t2), Math.min(t3, t4)), Math.min(t5, t6));
|
||||
double tmax = Math.min(Math.min(Math.max(t1, t2), Math.max(t3, t4)), Math.max(t5, t6));
|
||||
|
||||
// if tmax > 0, ray (line) is intersecting AABB, but the whole AABB is behind us
|
||||
if (tmax > 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// if tmin > tmax, ray doesn't intersect AABB
|
||||
if (tmin > tmax) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* returns the grid cells that intersect with this Vec3d<br>
|
||||
* <a href=
|
||||
* "http://playtechs.blogspot.de/2007/03/raytracing-on-grid.html">http://playtechs.blogspot.de/2007/03/raytracing-on-grid.html</a>
|
||||
* <p>
|
||||
* Caching assumes that all Vec3d's are inside the same block
|
||||
*/
|
||||
private boolean isVisible(Vec3d start, Vec3d[] targets, int size) {
|
||||
// start cell coordinate
|
||||
int x = cameraPos[0];
|
||||
int y = cameraPos[1];
|
||||
int z = cameraPos[2];
|
||||
|
||||
for (int v = 0; v < size; v++) {
|
||||
// ray-casting target
|
||||
Vec3d target = targets[v];
|
||||
|
||||
double relativeX = start.x - target.getX();
|
||||
double relativeY = start.y - target.getY();
|
||||
double relativeZ = start.z - target.getZ();
|
||||
|
||||
if (allowRayChecks && rayIntersection(lastHitBlock, start, new Vec3d(relativeX, relativeY, relativeZ).normalize())) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// horizontal and vertical cell amount spanned
|
||||
double dimensionX = Math.abs(relativeX);
|
||||
double dimensionY = Math.abs(relativeY);
|
||||
double dimensionZ = Math.abs(relativeZ);
|
||||
|
||||
// distance between horizontal intersection points with cell border as a
|
||||
// fraction of the total Vec3d length
|
||||
double dimFracX = 1f / dimensionX;
|
||||
// distance between vertical intersection points with cell border as a fraction
|
||||
// of the total Vec3d length
|
||||
double dimFracY = 1f / dimensionY;
|
||||
double dimFracZ = 1f / dimensionZ;
|
||||
|
||||
// total amount of intersected cells
|
||||
int intersectCount = 1;
|
||||
|
||||
// 1, 0 or -1
|
||||
// determines the direction of the next cell (horizontally / vertically)
|
||||
int x_inc, y_inc, z_inc;
|
||||
|
||||
// the distance to the next horizontal / vertical intersection point with a cell
|
||||
// border as a fraction of the total Vec3d length
|
||||
double t_next_y, t_next_x, t_next_z;
|
||||
|
||||
if (dimensionX == 0f) {
|
||||
x_inc = 0;
|
||||
t_next_x = dimFracX; // don't increment horizontally because the Vec3d is perfectly vertical
|
||||
} else if (target.x > start.x) {
|
||||
x_inc = 1; // target point is horizontally greater than starting point so increment every
|
||||
// step by 1
|
||||
intersectCount += MathUtilities.floor(target.x) - x; // increment total amount of intersecting cells
|
||||
t_next_x = (float) ((x + 1 - start.x) * dimFracX); // calculate the next horizontal
|
||||
// intersection
|
||||
// point based on the position inside
|
||||
// the first cell
|
||||
} else {
|
||||
x_inc = -1; // target point is horizontally smaller than starting point so reduce every step
|
||||
// by 1
|
||||
intersectCount += x - MathUtilities.floor(target.x); // increment total amount of intersecting cells
|
||||
t_next_x = (float) ((start.x - x)
|
||||
* dimFracX); // calculate the next horizontal
|
||||
// intersection point
|
||||
// based on the position inside
|
||||
// the first cell
|
||||
}
|
||||
|
||||
if (dimensionY == 0f) {
|
||||
y_inc = 0;
|
||||
t_next_y = dimFracY; // don't increment vertically because the Vec3d is perfectly horizontal
|
||||
} else if (target.y > start.y) {
|
||||
y_inc = 1; // target point is vertically greater than starting point so increment every
|
||||
// step by 1
|
||||
intersectCount += MathUtilities.floor(target.y) - y; // increment total amount of intersecting cells
|
||||
t_next_y = (float) ((y + 1 - start.y)
|
||||
* dimFracY); // calculate the next vertical
|
||||
// intersection
|
||||
// point based on the position inside
|
||||
// the first cell
|
||||
} else {
|
||||
y_inc = -1; // target point is vertically smaller than starting point so reduce every step
|
||||
// by 1
|
||||
intersectCount += y - MathUtilities.floor(target.y); // increment total amount of intersecting cells
|
||||
t_next_y = (float) ((start.y - y)
|
||||
* dimFracY); // calculate the next vertical intersection
|
||||
// point
|
||||
// based on the position inside
|
||||
// the first cell
|
||||
}
|
||||
|
||||
if (dimensionZ == 0f) {
|
||||
z_inc = 0;
|
||||
t_next_z = dimFracZ; // don't increment vertically because the Vec3d is perfectly horizontal
|
||||
} else if (target.z > start.z) {
|
||||
z_inc = 1; // target point is vertically greater than starting point so increment every
|
||||
// step by 1
|
||||
intersectCount += MathUtilities.floor(target.z) - z; // increment total amount of intersecting cells
|
||||
t_next_z = (float) ((z + 1 - start.z)
|
||||
* dimFracZ); // calculate the next vertical
|
||||
// intersection
|
||||
// point based on the position inside
|
||||
// the first cell
|
||||
} else {
|
||||
z_inc = -1; // target point is vertically smaller than starting point so reduce every step
|
||||
// by 1
|
||||
intersectCount += z - MathUtilities.floor(target.z); // increment total amount of intersecting cells
|
||||
t_next_z = (float) ((start.z - z)
|
||||
* dimFracZ); // calculate the next vertical intersection
|
||||
// point
|
||||
// based on the position inside
|
||||
// the first cell
|
||||
}
|
||||
|
||||
boolean finished = stepRay(start, x, y, z,
|
||||
dimFracX, dimFracY, dimFracZ, intersectCount, x_inc, y_inc,
|
||||
z_inc, t_next_y, t_next_x, t_next_z);
|
||||
provider.cleanup();
|
||||
if (finished) {
|
||||
cacheResult(targets[0], true);
|
||||
return true;
|
||||
} else {
|
||||
allowRayChecks = true;
|
||||
}
|
||||
}
|
||||
cacheResult(targets[0], false);
|
||||
return false;
|
||||
}
|
||||
|
||||
private boolean stepRay(Vec3d start, int currentX, int currentY,
|
||||
int currentZ, double distInX, double distInY,
|
||||
double distInZ, int n, int x_inc, int y_inc,
|
||||
int z_inc, double t_next_y, double t_next_x,
|
||||
double t_next_z) {
|
||||
allowWallClipping = true; // initially allow rays to go through walls till they are on the outside
|
||||
// iterate through all intersecting cells (n times)
|
||||
for (; n > 1; n--) { // n-1 times because we don't want to check the last block
|
||||
// towards - where from
|
||||
|
||||
|
||||
// get cached value, 0 means uncached (default)
|
||||
int cVal = getCacheValue(currentX, currentY, currentZ);
|
||||
|
||||
if (cVal == 2 && !allowWallClipping) {
|
||||
// block cached as occluding, stop ray
|
||||
lastHitBlock[0] = currentX;
|
||||
lastHitBlock[1] = currentY;
|
||||
lastHitBlock[2] = currentZ;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (cVal == 0) {
|
||||
// save current cell
|
||||
int chunkX = currentX >> 4;
|
||||
int chunkZ = currentZ >> 4;
|
||||
|
||||
if (!provider.prepareChunk(chunkX, chunkZ)) { // Chunk not ready
|
||||
return false;
|
||||
}
|
||||
|
||||
if (provider.isOpaqueFullCube(currentX, currentY, currentZ)) {
|
||||
if (!allowWallClipping) {
|
||||
cache.setLastHidden();
|
||||
lastHitBlock[0] = currentX;
|
||||
lastHitBlock[1] = currentY;
|
||||
lastHitBlock[2] = currentZ;
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
// outside of wall, now clipping is not allowed
|
||||
allowWallClipping = false;
|
||||
cache.setLastVisible();
|
||||
}
|
||||
}
|
||||
|
||||
if (cVal == 1) {
|
||||
// outside of wall, now clipping is not allowed
|
||||
allowWallClipping = false;
|
||||
}
|
||||
|
||||
|
||||
if (t_next_y < t_next_x && t_next_y < t_next_z) { // next cell is upwards/downwards because the distance to
|
||||
// the next vertical
|
||||
// intersection point is smaller than to the next horizontal intersection point
|
||||
currentY += y_inc; // move up/down
|
||||
t_next_y += distInY; // update next vertical intersection point
|
||||
} else if (t_next_x < t_next_y && t_next_x < t_next_z) { // next cell is right/left
|
||||
currentX += x_inc; // move right/left
|
||||
t_next_x += distInX; // update next horizontal intersection point
|
||||
} else {
|
||||
currentZ += z_inc; // move right/left
|
||||
t_next_z += distInZ; // update next horizontal intersection point
|
||||
}
|
||||
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// -1 = invalid location, 0 = not checked yet, 1 = visible, 2 = occluding
|
||||
private int getCacheValue(int x, int y, int z) {
|
||||
x -= cameraPos[0];
|
||||
y -= cameraPos[1];
|
||||
z -= cameraPos[2];
|
||||
if (Math.abs(x) > reach - 2 || Math.abs(y) > reach - 2
|
||||
|| Math.abs(z) > reach - 2) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
// check if target is already known
|
||||
return cache.getState(x + reach, y + reach, z + reach);
|
||||
}
|
||||
|
||||
|
||||
private void cacheResult(int x, int y, int z, boolean result) {
|
||||
int cx = x - cameraPos[0] + reach;
|
||||
int cy = y - cameraPos[1] + reach;
|
||||
int cz = z - cameraPos[2] + reach;
|
||||
if (result) {
|
||||
cache.setVisible(cx, cy, cz);
|
||||
} else {
|
||||
cache.setHidden(cx, cy, cz);
|
||||
}
|
||||
}
|
||||
|
||||
private void cacheResult(Vec3d vector, boolean result) {
|
||||
int cx = MathUtilities.floor(vector.x) - cameraPos[0] + reach;
|
||||
int cy = MathUtilities.floor(vector.y) - cameraPos[1] + reach;
|
||||
int cz = MathUtilities.floor(vector.z) - cameraPos[2] + reach;
|
||||
if (result) {
|
||||
cache.setVisible(cx, cy, cz);
|
||||
} else {
|
||||
cache.setHidden(cx, cy, cz);
|
||||
}
|
||||
}
|
||||
|
||||
public void resetCache() {
|
||||
this.cache.resetCache();
|
||||
}
|
||||
|
||||
private enum Relative {
|
||||
INSIDE, POSITIVE, NEGATIVE;
|
||||
|
||||
public static Relative from(int min, int max, int pos) {
|
||||
if (max > pos && min > pos) {
|
||||
return POSITIVE;
|
||||
} else if (min < pos && max < pos) {
|
||||
return NEGATIVE;
|
||||
}
|
||||
return INSIDE;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
Vendored
+57
@@ -0,0 +1,57 @@
|
||||
package com.logisticscraft.occlusionculling.cache;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
public class ArrayOcclusionCache implements OcclusionCache {
|
||||
|
||||
private final int reachX2;
|
||||
private final byte[] cache;
|
||||
private int positionKey;
|
||||
private int entry;
|
||||
private int offset;
|
||||
|
||||
public ArrayOcclusionCache(int reach) {
|
||||
this.reachX2 = reach * 2;
|
||||
this.cache = new byte[(reachX2 * reachX2 * reachX2) / 4];
|
||||
}
|
||||
|
||||
@Override
|
||||
public void resetCache() {
|
||||
Arrays.fill(cache, (byte) 0);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setVisible(int x, int y, int z) {
|
||||
positionKey = x + y * reachX2 + z * reachX2 * reachX2;
|
||||
entry = positionKey / 4;
|
||||
offset = (positionKey % 4) * 2;
|
||||
cache[entry] |= 1 << offset;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setHidden(int x, int y, int z) {
|
||||
positionKey = x + y * reachX2 + z * reachX2 * reachX2;
|
||||
entry = positionKey / 4;
|
||||
offset = (positionKey % 4) * 2;
|
||||
cache[entry] |= 1 << offset + 1;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getState(int x, int y, int z) {
|
||||
positionKey = x + y * reachX2 + z * reachX2 * reachX2;
|
||||
entry = positionKey / 4;
|
||||
offset = (positionKey % 4) * 2;
|
||||
return cache[entry] >> offset & 3;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setLastVisible() {
|
||||
cache[entry] |= 1 << offset;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setLastHidden() {
|
||||
cache[entry] |= 1 << offset + 1;
|
||||
}
|
||||
|
||||
}
|
||||
+17
@@ -0,0 +1,17 @@
|
||||
package com.logisticscraft.occlusionculling.cache;
|
||||
|
||||
public interface OcclusionCache {
|
||||
|
||||
void resetCache();
|
||||
|
||||
void setVisible(int x, int y, int z);
|
||||
|
||||
void setHidden(int x, int y, int z);
|
||||
|
||||
int getState(int x, int y, int z);
|
||||
|
||||
void setLastHidden();
|
||||
|
||||
void setLastVisible();
|
||||
|
||||
}
|
||||
+25
@@ -0,0 +1,25 @@
|
||||
package com.logisticscraft.occlusionculling.util;
|
||||
|
||||
/**
|
||||
* Contains MathHelper methods
|
||||
*/
|
||||
public final class MathUtilities {
|
||||
|
||||
private MathUtilities() {
|
||||
}
|
||||
|
||||
public static int floor(double d) {
|
||||
int i = (int) d;
|
||||
return d < (double) i ? i - 1 : i;
|
||||
}
|
||||
|
||||
public static int fastFloor(double d) {
|
||||
return (int) (d + 1024.0) - 1024;
|
||||
}
|
||||
|
||||
public static int ceil(double d) {
|
||||
int i = (int) d;
|
||||
return d > (double) i ? i + 1 : i;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,87 @@
|
||||
package com.logisticscraft.occlusionculling.util;
|
||||
|
||||
public class Vec3d {
|
||||
|
||||
public double x;
|
||||
public double y;
|
||||
public double z;
|
||||
|
||||
public Vec3d(double x, double y, double z) {
|
||||
this.x = x;
|
||||
this.y = y;
|
||||
this.z = z;
|
||||
}
|
||||
|
||||
public double getX() {
|
||||
return x;
|
||||
}
|
||||
|
||||
public double getY() {
|
||||
return y;
|
||||
}
|
||||
|
||||
public double getZ() {
|
||||
return z;
|
||||
}
|
||||
|
||||
public void set(double x, double y, double z) {
|
||||
this.x = x;
|
||||
this.y = y;
|
||||
this.z = z;
|
||||
}
|
||||
|
||||
public void setAdd(Vec3d vec, double x, double y, double z) {
|
||||
this.x = vec.x + x;
|
||||
this.y = vec.y + y;
|
||||
this.z = vec.z + z;
|
||||
}
|
||||
|
||||
public Vec3d div(Vec3d rayDir) {
|
||||
this.x /= rayDir.x;
|
||||
this.z /= rayDir.z;
|
||||
this.y /= rayDir.y;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Vec3d normalize() {
|
||||
double mag = Math.sqrt(x * x + y * y + z * z);
|
||||
this.x /= mag;
|
||||
this.y /= mag;
|
||||
this.z /= mag;
|
||||
return this;
|
||||
}
|
||||
|
||||
public boolean equals(Object other) {
|
||||
if (this == other) {
|
||||
return true;
|
||||
}
|
||||
if (!(other instanceof Vec3d)) {
|
||||
return false;
|
||||
}
|
||||
Vec3d vec3d = (Vec3d) other;
|
||||
if (Double.compare(vec3d.x, x) != 0) {
|
||||
return false;
|
||||
}
|
||||
if (Double.compare(vec3d.y, y) != 0) {
|
||||
return false;
|
||||
}
|
||||
return Double.compare(vec3d.z, z) == 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
long l = Double.doubleToLongBits(x);
|
||||
int i = (int) (l ^ l >>> 32);
|
||||
l = Double.doubleToLongBits(y);
|
||||
i = 31 * i + (int) (l ^ l >>> 32);
|
||||
l = Double.doubleToLongBits(z);
|
||||
i = 31 * i + (int) (l ^ l >>> 32);
|
||||
return i;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "(" + x + ", " + y + ", " + z + ")";
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,151 @@
|
||||
package dev.tr7zw.entityculling;
|
||||
|
||||
import ca.spottedleaf.moonrise.common.util.TickThread;
|
||||
import com.logisticscraft.occlusionculling.OcclusionCullingInstance;
|
||||
import com.logisticscraft.occlusionculling.util.Vec3d;
|
||||
import dev.tr7zw.entityculling.versionless.access.Cullable;
|
||||
import fun.bm.lophine.config.modules.experiment.RayTrackingEntityTrackerConfig;
|
||||
import net.minecraft.world.entity.Entity;
|
||||
import net.minecraft.world.entity.decoration.ArmorStand;
|
||||
import net.minecraft.world.entity.player.Player;
|
||||
import net.minecraft.world.phys.AABB;
|
||||
import net.minecraft.world.phys.Vec3;
|
||||
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.concurrent.Executor;
|
||||
import java.util.concurrent.Executors;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
public class CullTask implements Runnable {
|
||||
|
||||
private volatile boolean requestCull = false;
|
||||
private volatile boolean scheduleNext = true;
|
||||
private volatile boolean inited = false;
|
||||
|
||||
private final OcclusionCullingInstance culling;
|
||||
private final Player checkTarget;
|
||||
|
||||
private final int hitboxLimit;
|
||||
|
||||
public long lastCheckedTime = 0;
|
||||
|
||||
// reused preallocated vars
|
||||
private final Vec3d lastPos = new Vec3d(0, 0, 0);
|
||||
private final Vec3d aabbMin = new Vec3d(0, 0, 0);
|
||||
private final Vec3d aabbMax = new Vec3d(0, 0, 0);
|
||||
|
||||
private static final Executor backgroundWorker = Executors.newCachedThreadPool(task -> {
|
||||
final TickThread worker = new TickThread("EntityCulling") {
|
||||
@Override
|
||||
public void run() {
|
||||
task.run();
|
||||
}
|
||||
};
|
||||
|
||||
worker.setDaemon(true);
|
||||
|
||||
return worker;
|
||||
});
|
||||
|
||||
private final Executor worker;
|
||||
|
||||
public CullTask(
|
||||
OcclusionCullingInstance culling,
|
||||
Player checkTarget,
|
||||
int hitboxLimit,
|
||||
long checkIntervalMs
|
||||
) {
|
||||
this.culling = culling;
|
||||
this.checkTarget = checkTarget;
|
||||
this.hitboxLimit = hitboxLimit;
|
||||
this.worker = CompletableFuture.delayedExecutor(checkIntervalMs, TimeUnit.MILLISECONDS, backgroundWorker);
|
||||
}
|
||||
|
||||
public void requestCullSignal() {
|
||||
this.requestCull = true;
|
||||
}
|
||||
|
||||
public void signalStop() {
|
||||
this.scheduleNext = false;
|
||||
}
|
||||
|
||||
public void setup() {
|
||||
if (!this.inited)
|
||||
this.inited = true;
|
||||
else
|
||||
return;
|
||||
this.worker.execute(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
try {
|
||||
if (this.checkTarget.tickCount > 10) {
|
||||
// getEyePosition can use a fixed delta as its debug only anyway
|
||||
Vec3 cameraMC = this.checkTarget.getEyePosition(0);
|
||||
if (requestCull || !(cameraMC.x == lastPos.x && cameraMC.y == lastPos.y && cameraMC.z == lastPos.z)) {
|
||||
long start = System.currentTimeMillis();
|
||||
|
||||
requestCull = false;
|
||||
|
||||
lastPos.set(cameraMC.x, cameraMC.y, cameraMC.z);
|
||||
culling.resetCache();
|
||||
|
||||
cullEntities(cameraMC, lastPos);
|
||||
|
||||
lastCheckedTime = (System.currentTimeMillis() - start);
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
if (this.scheduleNext) {
|
||||
this.worker.execute(this);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void cullEntities(Vec3 cameraMC, Vec3d camera) {
|
||||
for (Entity entity : this.checkTarget.level().getEntities().getAll()) {
|
||||
if (!(entity instanceof Cullable cullable)) {
|
||||
continue; // Not sure how this could happen outside from mixin screwing up the inject into
|
||||
// Entity
|
||||
}
|
||||
|
||||
if (entity.getType().skipRaytracningCheck) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!cullable.isForcedVisible()) {
|
||||
if (entity.isCurrentlyGlowing() || isSkippableArmorstand(entity)) {
|
||||
cullable.setCulled(false);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!entity.position().closerThan(cameraMC, RayTrackingEntityTrackerConfig.tracingDistance)) {
|
||||
cullable.setCulled(false); // If your entity view distance is larger than tracingDistance just
|
||||
// render it
|
||||
continue;
|
||||
}
|
||||
|
||||
AABB boundingBox = entity.getBoundingBox();
|
||||
if (boundingBox.getXsize() > hitboxLimit || boundingBox.getYsize() > hitboxLimit
|
||||
|| boundingBox.getZsize() > hitboxLimit) {
|
||||
cullable.setCulled(false); // Too big to bother to cull
|
||||
continue;
|
||||
}
|
||||
|
||||
aabbMin.set(boundingBox.minX, boundingBox.minY, boundingBox.minZ);
|
||||
aabbMax.set(boundingBox.maxX, boundingBox.maxY, boundingBox.maxZ);
|
||||
|
||||
boolean visible = culling.isAABBVisible(aabbMin, aabbMax, camera);
|
||||
|
||||
cullable.setCulled(!visible);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private boolean isSkippableArmorstand(Entity entity) {
|
||||
if (!RayTrackingEntityTrackerConfig.skipMarkerArmorStands)
|
||||
return false;
|
||||
return entity instanceof ArmorStand && entity.isInvisible();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
package dev.tr7zw.entityculling;
|
||||
|
||||
import com.logisticscraft.occlusionculling.DataProvider;
|
||||
import net.minecraft.core.BlockPos;
|
||||
import net.minecraft.world.level.Level;
|
||||
import net.minecraft.world.level.block.Blocks;
|
||||
import net.minecraft.world.level.chunk.ChunkAccess;
|
||||
|
||||
public class DefaultChunkDataProvider implements DataProvider {
|
||||
private final Level level;
|
||||
|
||||
public DefaultChunkDataProvider(Level level) {
|
||||
this.level = level;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean prepareChunk(int chunkX, int chunkZ) {
|
||||
return this.level.getChunkIfLoaded(chunkX, chunkZ) != null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isOpaqueFullCube(int x, int y, int z) {
|
||||
BlockPos pos = new BlockPos(x, y, z);
|
||||
|
||||
final ChunkAccess access = this.level.getChunkIfLoaded(pos);
|
||||
if (access == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (this.level.isOutsideBuildHeight(pos)) {
|
||||
return Blocks.VOID_AIR.defaultBlockState().isSolidRender();
|
||||
} else {
|
||||
return access.getBlockState(pos).isSolidRender();// 好孩子不要学坏叔叔这样绕过异步拦截()
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void cleanup() {
|
||||
DataProvider.super.cleanup();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
package dev.tr7zw.entityculling.versionless.access;
|
||||
|
||||
public interface Cullable {
|
||||
|
||||
public void setTimeout();
|
||||
|
||||
public boolean isForcedVisible();
|
||||
|
||||
public void setCulled(boolean value);
|
||||
|
||||
public boolean isCulled();
|
||||
|
||||
public void setOutOfCamera(boolean value);
|
||||
|
||||
public boolean isOutOfCamera();
|
||||
|
||||
}
|
||||
+20
@@ -0,0 +1,20 @@
|
||||
package fun.bm.lophine.config.modules.experiment;
|
||||
|
||||
import me.earthme.luminol.config.EnumConfigCategory;
|
||||
import me.earthme.luminol.config.IConfigModule;
|
||||
import me.earthme.luminol.config.flags.ConfigClassInfo;
|
||||
import me.earthme.luminol.config.flags.ConfigInfo;
|
||||
|
||||
@ConfigClassInfo(configAttribution = EnumConfigCategory.EXPERIMENT, mainName = "command")
|
||||
public class CommandConfig implements IConfigModule {
|
||||
@ConfigInfo(baseName = "tick_command_enabled", comments =
|
||||
"""
|
||||
Allow to use tick command""")
|
||||
public static boolean tick = false;
|
||||
|
||||
@ConfigInfo(baseName = "datapack_command_enabled", comments =
|
||||
"""
|
||||
Allow to use datapack command
|
||||
--- datapack hot-update unsupported, please restart server""")
|
||||
public static boolean datapack = false;
|
||||
}
|
||||
+14
@@ -0,0 +1,14 @@
|
||||
package fun.bm.lophine.config.modules.experiment;
|
||||
|
||||
import me.earthme.luminol.config.EnumConfigCategory;
|
||||
import me.earthme.luminol.config.IConfigModule;
|
||||
import me.earthme.luminol.config.flags.ConfigClassInfo;
|
||||
import me.earthme.luminol.config.flags.ConfigInfo;
|
||||
|
||||
@ConfigClassInfo(configAttribution = EnumConfigCategory.EXPERIMENT, mainName = "entity_damage_source_trace")
|
||||
public class EntityDamageSourceTraceConfig implements IConfigModule {
|
||||
@ConfigInfo(baseName = "enabled", comments =
|
||||
"""
|
||||
Allow trace damage source cross different Region Scheduler.""")
|
||||
public static boolean enabled = false;
|
||||
}
|
||||
+20
@@ -0,0 +1,20 @@
|
||||
package fun.bm.lophine.config.modules.experiment;
|
||||
|
||||
import me.earthme.luminol.config.EnumConfigCategory;
|
||||
import me.earthme.luminol.config.IConfigModule;
|
||||
import me.earthme.luminol.config.flags.ConfigClassInfo;
|
||||
import me.earthme.luminol.config.flags.ConfigInfo;
|
||||
|
||||
@ConfigClassInfo(configAttribution = EnumConfigCategory.EXPERIMENT, mainName = "ray_tracking_entity_tracker")
|
||||
public class RayTrackingEntityTrackerConfig implements IConfigModule {
|
||||
@ConfigInfo(baseName = "enabled")
|
||||
public static boolean enabled = false;
|
||||
@ConfigInfo(baseName = "skip_marker_armor_stands")
|
||||
public static boolean skipMarkerArmorStands = true;
|
||||
@ConfigInfo(baseName = "check_interval_ms")
|
||||
public static int checkIntervalMs = 10;
|
||||
@ConfigInfo(baseName = "tracing_distance")
|
||||
public static int tracingDistance = 48;
|
||||
@ConfigInfo(baseName = "hitbox_limit")
|
||||
public static int hitboxLimit = 50;
|
||||
}
|
||||
+16
@@ -0,0 +1,16 @@
|
||||
package fun.bm.lophine.config.modules.experiment;
|
||||
|
||||
import me.earthme.luminol.config.EnumConfigCategory;
|
||||
import me.earthme.luminol.config.IConfigModule;
|
||||
import me.earthme.luminol.config.flags.ConfigClassInfo;
|
||||
import me.earthme.luminol.config.flags.ConfigInfo;
|
||||
import me.earthme.luminol.config.flags.TransformedConfig;
|
||||
|
||||
@ConfigClassInfo(configAttribution = EnumConfigCategory.EXPERIMENT, mainName = "waypoint_bar")
|
||||
public class WaypointConfig implements IConfigModule {
|
||||
@TransformedConfig(name = "enable-waypoint", category = {"experiment", "waypoint bar"})
|
||||
@ConfigInfo(baseName = "enabled", comments =
|
||||
"""
|
||||
Enable waypoint bar unsafe""")
|
||||
public static boolean forceEnableWaypointUnsafe = false;
|
||||
}
|
||||
+27
@@ -0,0 +1,27 @@
|
||||
package fun.bm.lophine.config.modules.misc;
|
||||
|
||||
import me.earthme.luminol.config.EnumConfigCategory;
|
||||
import me.earthme.luminol.config.IConfigModule;
|
||||
import me.earthme.luminol.config.flags.ConfigClassInfo;
|
||||
import me.earthme.luminol.config.flags.ConfigInfo;
|
||||
|
||||
@ConfigClassInfo(configAttribution = EnumConfigCategory.MISC, mainName = "container_expansion")
|
||||
public class ContainerExpansionConfig implements IConfigModule {
|
||||
@ConfigInfo(baseName = "barrel_rows", comments =
|
||||
"""
|
||||
range: 1~6""")
|
||||
public static int barrelRows = 3;
|
||||
|
||||
@ConfigInfo(baseName = "enderchest_rows", comments =
|
||||
"""
|
||||
range: 1~6""")
|
||||
public static int enderchestRows = 3;
|
||||
|
||||
@ConfigInfo(baseName = "shulker_stackable_count", comments =
|
||||
"""
|
||||
range: 1~64""")
|
||||
public static int shulkerCount = 1;
|
||||
|
||||
@ConfigInfo(baseName = "same_nbt_shulker_stackable")
|
||||
public static boolean nbtShulkerStackable = false;
|
||||
}
|
||||
+17
@@ -0,0 +1,17 @@
|
||||
package fun.bm.lophine.config.modules.misc;
|
||||
|
||||
import me.earthme.luminol.config.EnumConfigCategory;
|
||||
import me.earthme.luminol.config.IConfigModule;
|
||||
import me.earthme.luminol.config.flags.ConfigClassInfo;
|
||||
import me.earthme.luminol.config.flags.ConfigInfo;
|
||||
|
||||
@ConfigClassInfo(configAttribution = EnumConfigCategory.MISC, mainName = "disable_check")
|
||||
public class DisableCheckConfig implements IConfigModule {
|
||||
@ConfigInfo(baseName = "disable-op-move-check", comments = """
|
||||
Disable the check for the operator's move check""")
|
||||
public static boolean disableOpMoveCheck = false;
|
||||
|
||||
@ConfigInfo(baseName = "disable-op-fly-check", comments = """
|
||||
Disable the check for the operator's fly check""")
|
||||
public static boolean disableOpFlyCheck = false;
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
package fun.bm.lophine.config.modules.misc;
|
||||
|
||||
import me.earthme.luminol.config.EnumConfigCategory;
|
||||
import me.earthme.luminol.config.IConfigModule;
|
||||
import me.earthme.luminol.config.flags.ConfigClassInfo;
|
||||
import me.earthme.luminol.config.flags.ConfigInfo;
|
||||
import me.earthme.luminol.config.flags.TransformedConfig;
|
||||
|
||||
@ConfigClassInfo(configAttribution = EnumConfigCategory.MISC, mainName = "old_feature")
|
||||
public class OldFeatureConfig implements IConfigModule {
|
||||
@ConfigInfo(baseName = "spawn_invulnerable_time")
|
||||
public static boolean spawnInvulnerableTime = false;
|
||||
|
||||
@ConfigInfo(baseName = "old_zombie_reinforcement")
|
||||
public static boolean oldZombieReinforcement = false;
|
||||
|
||||
@ConfigInfo(baseName = "old_explosion_damage_calculator")
|
||||
public static boolean oldExplosionDamageCalculator = false;
|
||||
|
||||
@TransformedConfig(name = "give_bad_omen_when_kill_raid_captain", category = {"misc", "revert_raid_changes"}, transformComments = false)
|
||||
@ConfigInfo(baseName = "old_raid_behavior")
|
||||
public static boolean oldRaidBehavior = false;
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
package fun.bm.lophine.config.modules.misc;
|
||||
|
||||
import me.earthme.luminol.config.EnumConfigCategory;
|
||||
import me.earthme.luminol.config.IConfigModule;
|
||||
import me.earthme.luminol.config.flags.ConfigClassInfo;
|
||||
import me.earthme.luminol.config.flags.ConfigInfo;
|
||||
import me.earthme.luminol.config.flags.TransformedConfig;
|
||||
|
||||
@ConfigClassInfo(configAttribution = EnumConfigCategory.MISC, mainName = "redstone")
|
||||
public class RedStoneConfig implements IConfigModule {
|
||||
@TransformedConfig(name = "allow_skip_cooldown", category = {"misc", "redstone"})
|
||||
@ConfigInfo(baseName = "shears_rotate", comments =
|
||||
"""
|
||||
Allows you to use the Shears to right-click to rotate the block.""")
|
||||
public static boolean shears = false;
|
||||
}
|
||||
+38
@@ -0,0 +1,38 @@
|
||||
package fun.bm.lophine.config.modules.misc;
|
||||
|
||||
import me.earthme.luminol.config.EnumConfigCategory;
|
||||
import me.earthme.luminol.config.IConfigModule;
|
||||
import me.earthme.luminol.config.flags.ConfigClassInfo;
|
||||
import me.earthme.luminol.config.flags.ConfigInfo;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@ConfigClassInfo(configAttribution = EnumConfigCategory.MISC, mainName = "survux_protocol")
|
||||
public class SurvuxProtocolConfig implements IConfigModule {
|
||||
@ConfigInfo(baseName = "entity-protocol")
|
||||
public static boolean entityProtocol = false;
|
||||
|
||||
@ConfigInfo(baseName = "hud-logger-protocol")
|
||||
public static boolean hudLoggerProtocol = false;
|
||||
|
||||
@ConfigInfo(baseName = "hud-metadata-protocol")
|
||||
public static boolean hudMetadataProtocol = false;
|
||||
|
||||
@ConfigInfo(baseName = "hud-metadata-share-seed")
|
||||
public static boolean hudMetadataShareSeed = false;
|
||||
|
||||
@ConfigInfo(baseName = "structure-protocol")
|
||||
public static boolean structureProtocol = false;
|
||||
|
||||
@ConfigInfo(baseName = "hud-enabled-loggers")
|
||||
public static List<String> hudEnabledLoggers = List.of("tps", "mob_caps");
|
||||
|
||||
@ConfigInfo(baseName = "hud-update-interval")
|
||||
public static int hudUpdateInterval = 1;
|
||||
|
||||
@ConfigInfo(baseName = "litematics-enabled")
|
||||
public static boolean litematicsEnabled = false;
|
||||
|
||||
@ConfigInfo(baseName = "litematics-max-nbt-size")
|
||||
public static int litematicsMaxNbtSize = 2097152;
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
package fun.bm.lophine.config.modules.misc;
|
||||
|
||||
import me.earthme.luminol.config.EnumConfigCategory;
|
||||
import me.earthme.luminol.config.IConfigModule;
|
||||
import me.earthme.luminol.config.flags.ConfigClassInfo;
|
||||
import me.earthme.luminol.config.flags.ConfigInfo;
|
||||
import me.earthme.luminol.config.flags.TransformedConfig;
|
||||
|
||||
@ConfigClassInfo(configAttribution = EnumConfigCategory.MISC, mainName = "villager")
|
||||
public class VillagerConfig implements IConfigModule {
|
||||
@TransformedConfig(name = "villager-infinite-trade", category = {"misc", "villager-config"})
|
||||
@ConfigInfo(baseName = "villager-infinite-trade", comments =
|
||||
"""
|
||||
Allow villager infinite trade (limit of 524288 times)
|
||||
---- we won't edit saved data, only edit in send data to client.""")
|
||||
public static boolean villagerInfiniteTrade = false;
|
||||
}
|
||||
+14
@@ -0,0 +1,14 @@
|
||||
package fun.bm.lophine.config.modules.optimizations;
|
||||
|
||||
import me.earthme.luminol.config.EnumConfigCategory;
|
||||
import me.earthme.luminol.config.IConfigModule;
|
||||
import me.earthme.luminol.config.flags.ConfigClassInfo;
|
||||
import me.earthme.luminol.config.flags.ConfigInfo;
|
||||
|
||||
@ConfigClassInfo(configAttribution = EnumConfigCategory.OPTIMIZATIONS, mainName = "language")
|
||||
public class LanguageConfig implements IConfigModule {
|
||||
@ConfigInfo(baseName = "lang", comments = """
|
||||
Please use the key from https://minecraft.wiki/w/Language
|
||||
Sample of format: en_us zh_cn zh_hk zh_tw""")
|
||||
public static String lang = "en_us";
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
package fun.bm.lophine.config.modules.removed;
|
||||
|
||||
import me.earthme.luminol.config.EnumConfigCategory;
|
||||
import me.earthme.luminol.config.IConfigModule;
|
||||
import me.earthme.luminol.config.flags.ConfigClassInfo;
|
||||
import me.earthme.luminol.config.flags.ConfigInfo;
|
||||
import me.earthme.luminol.config.flags.TransformedConfig;
|
||||
|
||||
@ConfigClassInfo(configAttribution = EnumConfigCategory.REMOVED, mainName = "removed_config")
|
||||
public class RemovedConfig implements IConfigModule {
|
||||
@TransformedConfig(name = "disable_end_crystal_check", category = {"fixes", "end_crystal"}, transform = false)
|
||||
@TransformedConfig(name = "disable_end_crystal_check", category = {"misc", "end_crystal"}, transform = false)
|
||||
@TransformedConfig(name = "allow_skip_cooldown", category = {"misc", "revert_raid_changes"}, transform = false)
|
||||
@TransformedConfig(name = "bad_omen_infinite", category = {"misc", "revert_raid_changes"}, transform = false)
|
||||
@TransformedConfig(name = "skip_height_check", category = {"misc", "revert_raid_changes"}, transform = false)
|
||||
@TransformedConfig(name = "skip_self_raid_check", category = {"misc", "revert_raid_changes"}, transform = false)
|
||||
@TransformedConfig(name = "use_old_position_find", category = {"misc", "revert_raid_changes"}, transform = false)
|
||||
@TransformedConfig(name = "vanilla_hopper", category = {"misc", "redstone"}, transform = false)
|
||||
@TransformedConfig(name = "old_replaceable_by_mushrooms", category = {"misc", "old-feature"}, transform = false)
|
||||
@TransformedConfig(name = "old_nether_portal_collision", category = {"misc", "old-feature"}, transform = false)
|
||||
@TransformedConfig(name = "better_shulker_box", category = {"misc", "container_expansion"}, transform = false)
|
||||
@ConfigInfo(baseName = "removed", comments =
|
||||
"""
|
||||
RemovedConfig redirect to here, no any function.""")
|
||||
public static boolean enabled = true;
|
||||
}
|
||||
@@ -0,0 +1,315 @@
|
||||
package fun.bm.lophine.utils;
|
||||
|
||||
import com.google.gson.JsonElement;
|
||||
import com.google.gson.JsonObject;
|
||||
import com.google.gson.JsonParser;
|
||||
import com.google.gson.JsonSyntaxException;
|
||||
import com.mojang.logging.LogUtils;
|
||||
import fun.bm.lophine.config.modules.optimizations.LanguageConfig;
|
||||
import me.earthme.luminol.config.ConfigManager;
|
||||
import me.earthme.luminol.config.ConfigsInstance;
|
||||
import net.minecraft.DetectedVersion;
|
||||
import net.minecraft.locale.DeprecatedTranslationsInfo;
|
||||
import net.minecraft.locale.Language;
|
||||
import net.minecraft.network.chat.FormattedText;
|
||||
import net.minecraft.network.chat.Style;
|
||||
import net.minecraft.util.FormattedCharSequence;
|
||||
import net.minecraft.util.StringDecomposer;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.slf4j.Logger;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.net.URI;
|
||||
import java.net.http.HttpClient;
|
||||
import java.net.http.HttpRequest;
|
||||
import java.net.http.HttpResponse;
|
||||
import java.nio.file.*;
|
||||
import java.time.Duration;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.Optional;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.function.BiConsumer;
|
||||
|
||||
/*
|
||||
* authored by: Helvetica Volubi <suisuroru@blue-millennium.fun>
|
||||
* modified by: Lumine1909 <133463833+Lumine1909@users.noreply.github.com>
|
||||
* Some of diff form Leaves
|
||||
*/
|
||||
public class ServerI18nUtil {
|
||||
private static final Logger logger = LogUtils.getClassLogger();
|
||||
private static final String VERSION = DetectedVersion.BUILT_IN.name();
|
||||
private static final String BASE_PATH = "cache/lophine/" + VERSION + "/";
|
||||
private static final String defaultLophineLangPath = "/assets/lophine/lang/en_us.json";
|
||||
private static final String manifestUrl = "https://launchermeta.mojang.com/mc/game/version_manifest.json";
|
||||
private static final String resourceBaseUrl = "https://resources.download.minecraft.net/";
|
||||
// pre-load
|
||||
private static CompletableFuture<Void> preloadTask;
|
||||
// paths
|
||||
private static String langPath;
|
||||
private static String assetsPath;
|
||||
private static String versionPath;
|
||||
private static String manifestPath;
|
||||
private static String langJsonPath;
|
||||
private static String lophineLangPath;
|
||||
|
||||
public static void init() {
|
||||
if (Objects.equals(LanguageConfig.lang, "en_us")) {
|
||||
return;
|
||||
}
|
||||
langPath = BASE_PATH + "lang/" + LanguageConfig.lang + ".json";
|
||||
langJsonPath = "minecraft/lang/" + LanguageConfig.lang + ".json";
|
||||
lophineLangPath = "/assets/Lophine/lang/" + LanguageConfig.lang + ".json";
|
||||
logger.info("Starting load language: {}", LanguageConfig.lang);
|
||||
preloadTask.thenAcceptAsync(v -> loadI18n(LanguageConfig.lang, 2));
|
||||
}
|
||||
|
||||
public static void preInit() {
|
||||
assetsPath = BASE_PATH + "assets.json";
|
||||
versionPath = BASE_PATH + VERSION + ".json";
|
||||
manifestPath = BASE_PATH + "manifest.json";
|
||||
preloadTask = CompletableFuture.runAsync(() -> preLoadI18n(2));
|
||||
}
|
||||
|
||||
private static void preLoadI18n(int retryTime) {
|
||||
try {
|
||||
if (!Files.exists(Path.of(assetsPath))) {
|
||||
downloadAssets(true);
|
||||
}
|
||||
} catch (UnsupportedLanguageException e) {
|
||||
logger.warn("Unsupported language: {}", LanguageConfig.lang);
|
||||
// Fallback to English
|
||||
final ConfigsInstance configsInstance = ConfigManager.configfiles.get("lophine");
|
||||
configsInstance.setConfig(new String[]{"optimizations", "lang"}, "en_us");
|
||||
configsInstance.reloadAsync();
|
||||
} catch (Exception e) {
|
||||
if (e instanceof MalformedJsonException malformedJson) {
|
||||
malformedJson.clean();
|
||||
}
|
||||
logger.warn("Failed to download language list file: ", e);
|
||||
if (retryTime > 0) {
|
||||
preLoadI18n(retryTime - 1);
|
||||
} else {
|
||||
logger.error("Failed to download language list file for many times, skip pre-load language.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void loadI18n(String lang, int retryTime) {
|
||||
try {
|
||||
if (!Files.exists(Path.of(langPath))) {
|
||||
downloadLang(true);
|
||||
}
|
||||
Language.inject(createLangInstance());
|
||||
logger.info("Successfully loaded language: {}", lang);
|
||||
} catch (Exception e) {
|
||||
if (e instanceof MalformedJsonException malformedJson) {
|
||||
malformedJson.clean();
|
||||
}
|
||||
logger.warn("Failed to load language file for {}", lang, e);
|
||||
if (retryTime > 0) {
|
||||
loadI18n(lang, retryTime - 1);
|
||||
} else {
|
||||
logger.error("Failed to load for many times, use default lang \"en_us\" instead");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void downloadLang(boolean fetchFromAssets) throws Exception {
|
||||
JsonObject json;
|
||||
if (!Files.exists(Path.of(assetsPath)) || (json = loadJson(assetsPath)) == null) {
|
||||
if (fetchFromAssets) {
|
||||
downloadAssets(true);
|
||||
downloadLang(false);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
JsonObject langEntry = json.getAsJsonObject("objects").getAsJsonObject(langJsonPath);
|
||||
|
||||
if (langEntry == null) {
|
||||
throw new UnsupportedLanguageException();
|
||||
}
|
||||
|
||||
String hash = langEntry.get("hash").getAsString();
|
||||
if (hash == null || hash.length() < 2) {
|
||||
throw new IllegalArgumentException("Invalid hash value");
|
||||
}
|
||||
|
||||
String langUrl = resourceBaseUrl + hash.substring(0, 2) + "/" + hash;
|
||||
fetchAndSave(langUrl, langPath);
|
||||
}
|
||||
|
||||
private static void downloadAssets(boolean fetchFromVersion) throws Exception {
|
||||
JsonObject json;
|
||||
if (!Files.exists(Path.of(versionPath)) || (json = loadJson(versionPath)) == null) {
|
||||
if (fetchFromVersion) {
|
||||
downloadVersion(true);
|
||||
downloadAssets(false);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
JsonObject assetIndex = json.getAsJsonObject("assetIndex");
|
||||
String assetUrl = assetIndex.get("url").getAsString();
|
||||
fetchAndSave(assetUrl, assetsPath);
|
||||
}
|
||||
|
||||
private static void downloadVersion(boolean fetchFromManifest) throws Exception {
|
||||
JsonObject json;
|
||||
if (!Files.exists(Path.of(manifestPath)) || (json = loadJson(manifestPath)) == null) {
|
||||
if (fetchFromManifest) {
|
||||
fetchAndSave(manifestUrl, manifestPath);
|
||||
downloadVersion(false);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
String versionUrl = null;
|
||||
for (JsonElement element : json.getAsJsonArray("versions")) {
|
||||
String id = element.getAsJsonObject().get("id").getAsString();
|
||||
String url = element.getAsJsonObject().get("url").getAsString();
|
||||
if (VERSION.equals(id)) {
|
||||
versionUrl = url;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (versionUrl == null) {
|
||||
throw new RuntimeException("Could not find version URL");
|
||||
}
|
||||
|
||||
fetchAndSave(versionUrl, versionPath);
|
||||
}
|
||||
|
||||
private static byte[] fetch(String urlString) throws IOException, InterruptedException {
|
||||
try {
|
||||
HttpResponse<String> response;
|
||||
try (HttpClient httpClient = HttpClient.newHttpClient()) {
|
||||
HttpRequest request = HttpRequest.newBuilder()
|
||||
.uri(URI.create(urlString))
|
||||
.timeout(Duration.ofSeconds(10))
|
||||
.build();
|
||||
response = httpClient.send(request, HttpResponse.BodyHandlers.ofString());
|
||||
}
|
||||
|
||||
int responseCode = response.statusCode();
|
||||
if (responseCode != 200) {
|
||||
logger.info("Unexpected response code: {}", responseCode);
|
||||
logger.info("Response body: {}", response.body());
|
||||
throw new UnsupportedEncodingException("Unexpected response code");
|
||||
} else {
|
||||
return response.body().getBytes();
|
||||
}
|
||||
} catch (Exception e) {
|
||||
logger.warn("Error in getting info!");
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
private static void fetchAndSave(String url, String savePath) throws IOException, InterruptedException {
|
||||
byte[] data = fetch(url);
|
||||
Path outputPath = Path.of(savePath);
|
||||
Files.createDirectories(outputPath.getParent());
|
||||
Files.write(outputPath, data, StandardOpenOption.CREATE, StandardOpenOption.WRITE);
|
||||
}
|
||||
|
||||
private static JsonObject loadJson(String path) {
|
||||
try {
|
||||
byte[] data = Files.readAllBytes(Paths.get(path));
|
||||
return JsonParser.parseString(new String(data)).getAsJsonObject();
|
||||
} catch (JsonSyntaxException e) {
|
||||
logger.warn("Corrupt json file!");
|
||||
throw new MalformedJsonException(e, path);
|
||||
} catch (Exception e) {
|
||||
logger.warn("Failed to load local JSON!");
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private static Language createLangInstance() throws IOException {
|
||||
DeprecatedTranslationsInfo deprecatedTranslationsInfo = DeprecatedTranslationsInfo.loadFromDefaultResource();
|
||||
Map<String, String> map = new HashMap<>();
|
||||
parseTranslations(map::put);
|
||||
loadLophineI18n(map::put);
|
||||
deprecatedTranslationsInfo.applyToMap(map);
|
||||
final Map<String, String> map1 = Map.copyOf(map);
|
||||
return new Language() {
|
||||
@Override
|
||||
public @NotNull String getOrDefault(@NotNull String key, @NotNull String defaultValue) {
|
||||
return map1.getOrDefault(key, defaultValue);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean has(@NotNull String id) {
|
||||
return map1.containsKey(id);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isDefaultRightToLeft() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull FormattedCharSequence getVisualOrder(@NotNull FormattedText text) {
|
||||
return sink -> text.visit(
|
||||
(style, content) -> StringDecomposer.iterateFormatted(content, style, sink) ? Optional.empty() : FormattedText.STOP_ITERATION,
|
||||
Style.EMPTY
|
||||
)
|
||||
.isPresent();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
private static void loadLophineI18n(BiConsumer<String, String> bi) {
|
||||
if (Language.class.getResource(lophineLangPath) != null) {
|
||||
Language.parseTranslations(bi, lophineLangPath);
|
||||
} else {
|
||||
loadLophineI18nDefault(bi);
|
||||
}
|
||||
}
|
||||
|
||||
public static void loadLophineI18nDefault(BiConsumer<String, String> bi) {
|
||||
if (Language.class.getResource(defaultLophineLangPath) != null) {
|
||||
Language.parseTranslations(bi, defaultLophineLangPath);
|
||||
}
|
||||
}
|
||||
|
||||
private static void parseTranslations(BiConsumer<String, String> output) throws IOException {
|
||||
Path filePath = Path.of(langPath);
|
||||
try (InputStream fileStream = Files.newInputStream(filePath)) {
|
||||
Language.loadFromJson(fileStream, output);
|
||||
} catch (NoSuchFileException e) {
|
||||
logger.warn("Couldn't find language file: {}", langPath);
|
||||
throw e;
|
||||
} catch (JsonSyntaxException e) {
|
||||
throw new MalformedJsonException(e, langPath);
|
||||
} catch (Exception e) {
|
||||
logger.warn("Failed to load language from filesystem {}", filePath);
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
private static class UnsupportedLanguageException extends Exception {
|
||||
}
|
||||
|
||||
private static class MalformedJsonException extends RuntimeException {
|
||||
private final Path path;
|
||||
|
||||
protected MalformedJsonException(Throwable root, String path) {
|
||||
super(root);
|
||||
this.path = Path.of(path);
|
||||
}
|
||||
|
||||
public void clean() {
|
||||
try {
|
||||
Files.delete(path);
|
||||
} catch (IOException e) {
|
||||
logger.info("Failed to delete malformed JSON file: {}", path);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
/*
|
||||
* This file is part of Leaves (https://github.com/LeavesMC/Leaves)
|
||||
*
|
||||
* Leaves is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Leaves is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with Leaves. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package org.leavesmc.leaves.protocol.core;
|
||||
|
||||
import com.mojang.authlib.GameProfile;
|
||||
import net.minecraft.network.Connection;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
public record Context(@NotNull GameProfile profile, Connection connection) {
|
||||
|
||||
}
|
||||
+28
@@ -0,0 +1,28 @@
|
||||
/*
|
||||
* This file is part of Leaves (https://github.com/LeavesMC/Leaves)
|
||||
*
|
||||
* Leaves is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Leaves is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with Leaves. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package org.leavesmc.leaves.protocol.core;
|
||||
|
||||
import net.minecraft.server.level.ServerPlayer;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
public record IdentifierSelector(@Nullable Context context, @Nullable ServerPlayer player) {
|
||||
|
||||
public Object select(ProtocolHandler.Stage stage) {
|
||||
return stage == ProtocolHandler.Stage.CONFIGURATION ? context : player;
|
||||
}
|
||||
}
|
||||
+48
@@ -0,0 +1,48 @@
|
||||
/*
|
||||
* This file is part of Leaves (https://github.com/LeavesMC/Leaves)
|
||||
*
|
||||
* Leaves is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Leaves is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with Leaves. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
|
||||
package org.leavesmc.leaves.protocol.core;
|
||||
|
||||
import net.minecraft.network.protocol.common.custom.CustomPacketPayload;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
public interface LeavesCustomPayload extends CustomPacketPayload {
|
||||
|
||||
Type<? extends CustomPacketPayload> LEAVES_TYPE = new Type<>(ResourceLocation.fromNamespaceAndPath("leaves", "custom_payload"));
|
||||
|
||||
@Override
|
||||
default @NotNull Type<? extends CustomPacketPayload> type() {
|
||||
return LEAVES_TYPE;
|
||||
}
|
||||
|
||||
@Target(ElementType.FIELD)
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@interface ID {
|
||||
}
|
||||
|
||||
@Target(ElementType.FIELD)
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@interface Codec {
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
/*
|
||||
* This file is part of Leaves (https://github.com/LeavesMC/Leaves)
|
||||
*
|
||||
* Leaves is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Leaves is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with Leaves. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
|
||||
package org.leavesmc.leaves.protocol.core;
|
||||
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
public interface LeavesProtocol {
|
||||
|
||||
boolean isActive();
|
||||
|
||||
default int tickerInterval(String tickerID) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
@Target(ElementType.TYPE)
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@interface Register {
|
||||
String namespace();
|
||||
}
|
||||
}
|
||||
+401
@@ -0,0 +1,401 @@
|
||||
/*
|
||||
* This file is part of Leaves (https://github.com/LeavesMC/Leaves)
|
||||
*
|
||||
* Leaves is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Leaves is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with Leaves. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
|
||||
package org.leavesmc.leaves.protocol.core;
|
||||
|
||||
import com.mojang.logging.LogUtils;
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import net.minecraft.network.FriendlyByteBuf;
|
||||
import net.minecraft.network.RegistryFriendlyByteBuf;
|
||||
import net.minecraft.network.codec.StreamCodec;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import net.minecraft.server.level.ServerPlayer;
|
||||
import org.leavesmc.leaves.protocol.core.invoker.*;
|
||||
import org.slf4j.Logger;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.lang.reflect.Constructor;
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.Method;
|
||||
import java.lang.reflect.Modifier;
|
||||
import java.net.JarURLConnection;
|
||||
import java.net.URL;
|
||||
import java.net.URLDecoder;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.*;
|
||||
import java.util.jar.JarEntry;
|
||||
import java.util.jar.JarFile;
|
||||
|
||||
public class LeavesProtocolManager {
|
||||
private static final Logger LOGGER = LogUtils.getClassLogger();
|
||||
|
||||
private static final Map<Class<? extends LeavesCustomPayload>, PayloadReceiverInvokerHolder> PAYLOAD_RECEIVERS = new HashMap<>();
|
||||
private static final Map<Class<? extends LeavesCustomPayload>, ResourceLocation> IDS = new HashMap<>();
|
||||
private static final Map<Class<? extends LeavesCustomPayload>, StreamCodec<? super RegistryFriendlyByteBuf, LeavesCustomPayload>> CODECS = new HashMap<>();
|
||||
private static final Map<ResourceLocation, StreamCodec<? super RegistryFriendlyByteBuf, LeavesCustomPayload>> ID2CODEC = new HashMap<>();
|
||||
|
||||
private static final Map<String, BytebufReceiverInvokerHolder> STRICT_BYTEBUF_RECEIVERS = new HashMap<>();
|
||||
private static final Map<String, BytebufReceiverInvokerHolder> NAMESPACED_BYTEBUF_RECEIVERS = new HashMap<>();
|
||||
private static final List<BytebufReceiverInvokerHolder> GENERIC_BYTEBUF_RECEIVERS = new ArrayList<>();
|
||||
|
||||
private static final Map<String, MinecraftRegisterInvokerHolder> STRICT_MINECRAFT_REGISTER = new HashMap<>();
|
||||
private static final Map<String, MinecraftRegisterInvokerHolder> NAMESPACED_MINECRAFT_REGISTER = new HashMap<>();
|
||||
private static final List<MinecraftRegisterInvokerHolder> WILD_MINECRAFT_REGISTER = new ArrayList<>();
|
||||
|
||||
private static final List<EmptyInvokerHolder<ProtocolHandler.Ticker>> TICKERS = new ArrayList<>();
|
||||
|
||||
private static final List<PlayerInvokerHolder<ProtocolHandler.PlayerJoin>> PLAYER_JOIN = new ArrayList<>();
|
||||
private static final List<PlayerInvokerHolder<ProtocolHandler.PlayerLeave>> PLAYER_LEAVE = new ArrayList<>();
|
||||
private static final List<EmptyInvokerHolder<ProtocolHandler.ReloadServer>> RELOAD_SERVER = new ArrayList<>();
|
||||
private static final List<EmptyInvokerHolder<ProtocolHandler.ReloadDataPack>> RELOAD_DATAPACK = new ArrayList<>();
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public static void init() {
|
||||
for (Class<?> clazz : getClasses("org.leavesmc.leaves.protocol")) {
|
||||
if (LeavesCustomPayload.class.isAssignableFrom(clazz) && !clazz.equals(LeavesCustomPayload.class)) {
|
||||
for (Field field : clazz.getDeclaredFields()) {
|
||||
field.setAccessible(true);
|
||||
if (!Modifier.isStatic(field.getModifiers())) {
|
||||
continue;
|
||||
}
|
||||
try {
|
||||
final LeavesCustomPayload.ID id = field.getAnnotation(LeavesCustomPayload.ID.class);
|
||||
if (id != null && field.getType().equals(ResourceLocation.class)) {
|
||||
IDS.put((Class<? extends LeavesCustomPayload>) clazz, (ResourceLocation) field.get(null));
|
||||
}
|
||||
final LeavesCustomPayload.Codec codec = field.getAnnotation(LeavesCustomPayload.Codec.class);
|
||||
if (codec != null && field.getType().equals(StreamCodec.class)) {
|
||||
CODECS.put((Class<? extends LeavesCustomPayload>) clazz, (StreamCodec<? super RegistryFriendlyByteBuf, LeavesCustomPayload>) field.get(null));
|
||||
}
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
final LeavesProtocol.Register register = clazz.getAnnotation(LeavesProtocol.Register.class);
|
||||
if (register == null) {
|
||||
continue;
|
||||
}
|
||||
LeavesProtocol protocol;
|
||||
try {
|
||||
Constructor<?> constructor = clazz.getDeclaredConstructor();
|
||||
constructor.setAccessible(true);
|
||||
protocol = (LeavesProtocol) constructor.newInstance();
|
||||
} catch (Throwable throwable) {
|
||||
LOGGER.error("Failed to load class {}. {}", clazz.getName(), throwable);
|
||||
return;
|
||||
}
|
||||
|
||||
for (final Method method : clazz.getDeclaredMethods()) {
|
||||
if (method.isBridge() || method.isSynthetic()) {
|
||||
continue;
|
||||
}
|
||||
method.setAccessible(true);
|
||||
|
||||
final ProtocolHandler.Init init = method.getAnnotation(ProtocolHandler.Init.class);
|
||||
if (init != null) {
|
||||
InitInvokerHolder holder = new InitInvokerHolder(protocol, method, init);
|
||||
try {
|
||||
holder.invoke();
|
||||
} catch (RuntimeException exception) {
|
||||
LOGGER.error("Failed to invoke init method {} in {}, {}: {}", method.getName(), clazz.getName(), exception.getCause(), exception.getMessage());
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
final ProtocolHandler.PayloadReceiver payloadReceiver = method.getAnnotation(ProtocolHandler.PayloadReceiver.class);
|
||||
if (payloadReceiver != null) {
|
||||
PAYLOAD_RECEIVERS.put(payloadReceiver.payload(), new PayloadReceiverInvokerHolder(protocol, method, payloadReceiver));
|
||||
continue;
|
||||
}
|
||||
|
||||
final ProtocolHandler.BytebufReceiver bytebufReceiver = method.getAnnotation(ProtocolHandler.BytebufReceiver.class);
|
||||
if (bytebufReceiver != null) {
|
||||
String key = bytebufReceiver.key();
|
||||
BytebufReceiverInvokerHolder holder = new BytebufReceiverInvokerHolder(protocol, method, bytebufReceiver);
|
||||
if (bytebufReceiver.onlyNamespace()) {
|
||||
NAMESPACED_BYTEBUF_RECEIVERS.put(key.isEmpty() ? register.namespace() : key, holder);
|
||||
} else {
|
||||
if (key.isEmpty()) {
|
||||
GENERIC_BYTEBUF_RECEIVERS.add(holder);
|
||||
} else {
|
||||
if (key.contains(":")) {
|
||||
STRICT_BYTEBUF_RECEIVERS.put(key, holder);
|
||||
} else {
|
||||
STRICT_BYTEBUF_RECEIVERS.put(register.namespace() + ":" + key, holder);
|
||||
}
|
||||
}
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
final ProtocolHandler.Ticker ticker = method.getAnnotation(ProtocolHandler.Ticker.class);
|
||||
if (ticker != null) {
|
||||
TICKERS.add(new EmptyInvokerHolder<>(protocol, method, ticker));
|
||||
continue;
|
||||
}
|
||||
|
||||
final ProtocolHandler.PlayerJoin playerJoin = method.getAnnotation(ProtocolHandler.PlayerJoin.class);
|
||||
if (playerJoin != null) {
|
||||
PLAYER_JOIN.add(new PlayerInvokerHolder<>(protocol, method, playerJoin));
|
||||
continue;
|
||||
}
|
||||
|
||||
final ProtocolHandler.PlayerLeave playerLeave = method.getAnnotation(ProtocolHandler.PlayerLeave.class);
|
||||
if (playerLeave != null) {
|
||||
PLAYER_LEAVE.add(new PlayerInvokerHolder<>(protocol, method, playerLeave));
|
||||
continue;
|
||||
}
|
||||
|
||||
final ProtocolHandler.ReloadServer reloadServer = method.getAnnotation(ProtocolHandler.ReloadServer.class);
|
||||
if (reloadServer != null) {
|
||||
RELOAD_SERVER.add(new EmptyInvokerHolder<>(protocol, method, reloadServer));
|
||||
continue;
|
||||
}
|
||||
|
||||
final ProtocolHandler.ReloadDataPack reloadDataPack = method.getAnnotation(ProtocolHandler.ReloadDataPack.class);
|
||||
if (reloadDataPack != null) {
|
||||
RELOAD_DATAPACK.add(new EmptyInvokerHolder<>(protocol, method, reloadDataPack));
|
||||
continue;
|
||||
}
|
||||
|
||||
final ProtocolHandler.MinecraftRegister minecraftRegister = method.getAnnotation(ProtocolHandler.MinecraftRegister.class);
|
||||
if (minecraftRegister != null) {
|
||||
String key = minecraftRegister.key();
|
||||
MinecraftRegisterInvokerHolder holder = new MinecraftRegisterInvokerHolder(protocol, method, minecraftRegister);
|
||||
if (minecraftRegister.onlyNamespace()) {
|
||||
NAMESPACED_MINECRAFT_REGISTER.put(key.isEmpty() ? register.namespace() : key, holder);
|
||||
} else {
|
||||
if (key.isEmpty()) {
|
||||
WILD_MINECRAFT_REGISTER.add(holder);
|
||||
} else {
|
||||
if (key.contains(":")) {
|
||||
STRICT_MINECRAFT_REGISTER.put(key, holder);
|
||||
} else {
|
||||
STRICT_MINECRAFT_REGISTER.put(register.namespace() + ":" + key, holder);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
for (var idInfo : IDS.entrySet()) {
|
||||
var codec = CODECS.get(idInfo.getKey());
|
||||
if (codec == null) {
|
||||
throw new IllegalArgumentException("Payload " + idInfo.getKey() + " is not configured correctly");
|
||||
}
|
||||
ID2CODEC.put(idInfo.getValue(), codec);
|
||||
}
|
||||
}
|
||||
|
||||
public static LeavesCustomPayload decode(ResourceLocation location, FriendlyByteBuf buf) {
|
||||
var codec = ID2CODEC.get(location);
|
||||
if (codec == null) {
|
||||
return null;
|
||||
}
|
||||
return codec.decode(ProtocolUtils.decorate(buf));
|
||||
}
|
||||
|
||||
public static void encode(FriendlyByteBuf buf, LeavesCustomPayload payload) {
|
||||
var location = IDS.get(payload.getClass());
|
||||
var codec = CODECS.get(payload.getClass());
|
||||
if (location == null || codec == null) {
|
||||
throw new IllegalArgumentException("Payload " + payload.getClass() + " is not configured correctly " + location + " " + codec);
|
||||
}
|
||||
buf.writeResourceLocation(location);
|
||||
codec.encode(ProtocolUtils.decorate(buf), payload);
|
||||
}
|
||||
|
||||
public static void handlePayload(IdentifierSelector selector, LeavesCustomPayload payload) {
|
||||
PayloadReceiverInvokerHolder holder;
|
||||
if ((holder = PAYLOAD_RECEIVERS.get(payload.getClass())) != null) {
|
||||
holder.invoke(selector, payload);
|
||||
}
|
||||
}
|
||||
|
||||
public static boolean handleBytebuf(IdentifierSelector selector, ResourceLocation location, ByteBuf buf) {
|
||||
RegistryFriendlyByteBuf buf1 = ProtocolUtils.decorate(buf);
|
||||
BytebufReceiverInvokerHolder holder;
|
||||
if ((holder = STRICT_BYTEBUF_RECEIVERS.get(location.toString())) != null) {
|
||||
holder.invoke(selector, buf1);
|
||||
return true;
|
||||
}
|
||||
if ((holder = NAMESPACED_BYTEBUF_RECEIVERS.get(location.getNamespace())) != null) {
|
||||
if (holder.invoke(selector, buf1)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
for (var holder1 : GENERIC_BYTEBUF_RECEIVERS) {
|
||||
if (holder1.invoke(selector, buf1)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public static void handleTick(long tickCount) {
|
||||
for (var tickerInfo : TICKERS) {
|
||||
if (tickCount % tickerInfo.owner().tickerInterval(tickerInfo.handler().tickerId()) == 0) {
|
||||
tickerInfo.invoke();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static void handlePlayerJoin(ServerPlayer player) {
|
||||
sendKnownId(player);
|
||||
for (var join : PLAYER_JOIN) {
|
||||
join.invoke(player);
|
||||
}
|
||||
}
|
||||
|
||||
public static void handlePlayerLeave(ServerPlayer player) {
|
||||
for (var leave : PLAYER_LEAVE) {
|
||||
leave.invoke(player);
|
||||
}
|
||||
}
|
||||
|
||||
public static void handleServerReload() {
|
||||
for (var reload : RELOAD_SERVER) {
|
||||
reload.invoke();
|
||||
}
|
||||
}
|
||||
|
||||
public static void handleDataPackReload() {
|
||||
for (var reload : RELOAD_DATAPACK) {
|
||||
reload.invoke();
|
||||
}
|
||||
}
|
||||
|
||||
public static void handleMinecraftRegister(String channelId, IdentifierSelector selector) {
|
||||
ResourceLocation location = ResourceLocation.tryParse(channelId);
|
||||
if (location == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (var wildHolder : WILD_MINECRAFT_REGISTER) {
|
||||
wildHolder.invoke(selector, location);
|
||||
}
|
||||
|
||||
MinecraftRegisterInvokerHolder holder;
|
||||
if ((holder = STRICT_MINECRAFT_REGISTER.get(location.toString())) != null) {
|
||||
holder.invoke(selector, location);
|
||||
}
|
||||
if ((holder = NAMESPACED_MINECRAFT_REGISTER.get(location.getNamespace())) != null) {
|
||||
holder.invoke(selector, location);
|
||||
}
|
||||
}
|
||||
|
||||
private static void sendKnownId(ServerPlayer player) {
|
||||
Set<String> set = new HashSet<>();
|
||||
PAYLOAD_RECEIVERS.forEach((clazz, holder) -> {
|
||||
if (holder.owner().isActive()) {
|
||||
set.add(IDS.get(clazz).toString());
|
||||
}
|
||||
});
|
||||
STRICT_BYTEBUF_RECEIVERS.forEach((key, holder) -> {
|
||||
if (holder.owner().isActive()) {
|
||||
set.add(key);
|
||||
}
|
||||
});
|
||||
ProtocolUtils.sendBytebufPacket(player, ResourceLocation.fromNamespaceAndPath("minecraft", "register"), buf -> {
|
||||
for (String channel : set) {
|
||||
buf.writeBytes(channel.getBytes(StandardCharsets.US_ASCII));
|
||||
buf.writeByte(0);
|
||||
}
|
||||
buf.writerIndex(Math.max(buf.writerIndex() - 1, 0));
|
||||
});
|
||||
}
|
||||
|
||||
public static Set<Class<?>> getClasses(String pack) {
|
||||
Set<Class<?>> classes = new LinkedHashSet<>();
|
||||
String packageDirName = pack.replace('.', '/');
|
||||
Enumeration<URL> dirs;
|
||||
try {
|
||||
dirs = Thread.currentThread().getContextClassLoader().getResources(packageDirName);
|
||||
while (dirs.hasMoreElements()) {
|
||||
URL url = dirs.nextElement();
|
||||
String protocol = url.getProtocol();
|
||||
if ("file".equals(protocol)) {
|
||||
String filePath = URLDecoder.decode(url.getFile(), StandardCharsets.UTF_8);
|
||||
findClassesInPackageByFile(pack, filePath, classes);
|
||||
} else if ("jar".equals(protocol)) {
|
||||
JarFile jar;
|
||||
try {
|
||||
jar = ((JarURLConnection) url.openConnection()).getJarFile();
|
||||
Enumeration<JarEntry> entries = jar.entries();
|
||||
findClassesInPackageByJar(pack, entries, packageDirName, classes);
|
||||
} catch (IOException exception) {
|
||||
LOGGER.warn("Failed to load jar file, {}: {}", exception.getCause(), exception.getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (IOException exception) {
|
||||
LOGGER.warn("Failed to load classes, {}: {}", exception.getCause(), exception.getMessage());
|
||||
}
|
||||
return classes;
|
||||
}
|
||||
|
||||
private static void findClassesInPackageByFile(String packageName, String packagePath, Set<Class<?>> classes) {
|
||||
File dir = new File(packagePath);
|
||||
if (!dir.exists() || !dir.isDirectory()) {
|
||||
return;
|
||||
}
|
||||
File[] dirfiles = dir.listFiles((file) -> file.isDirectory() || file.getName().endsWith(".class"));
|
||||
if (dirfiles != null) {
|
||||
for (File file : dirfiles) {
|
||||
if (file.isDirectory()) {
|
||||
findClassesInPackageByFile(packageName + "." + file.getName(), file.getAbsolutePath(), classes);
|
||||
} else {
|
||||
String className = file.getName().substring(0, file.getName().length() - 6);
|
||||
try {
|
||||
classes.add(Class.forName(packageName + '.' + className));
|
||||
} catch (ClassNotFoundException exception) {
|
||||
LOGGER.warn("Failed to load class {}, {}: {}", className, exception.getCause(), exception.getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void findClassesInPackageByJar(String packageName, Enumeration<JarEntry> entries, String packageDirName, Set<Class<?>> classes) {
|
||||
while (entries.hasMoreElements()) {
|
||||
JarEntry entry = entries.nextElement();
|
||||
String name = entry.getName();
|
||||
if (name.charAt(0) == '/') {
|
||||
name = name.substring(1);
|
||||
}
|
||||
if (name.startsWith(packageDirName)) {
|
||||
int idx = name.lastIndexOf('/');
|
||||
if (idx != -1) {
|
||||
packageName = name.substring(0, idx).replace('/', '.');
|
||||
}
|
||||
if (name.endsWith(".class") && !entry.isDirectory()) {
|
||||
String className = name.substring(packageName.length() + 1, name.length() - 6);
|
||||
try {
|
||||
classes.add(Class.forName(packageName + '.' + className));
|
||||
} catch (ClassNotFoundException exception) {
|
||||
LOGGER.warn("Failed to load class {}, {}: {}", className, exception.getCause(), exception.getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,103 @@
|
||||
/*
|
||||
* This file is part of Leaves (https://github.com/LeavesMC/Leaves)
|
||||
*
|
||||
* Leaves is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Leaves is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with Leaves. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
|
||||
package org.leavesmc.leaves.protocol.core;
|
||||
|
||||
import net.minecraft.server.level.ServerPlayer;
|
||||
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
public class ProtocolHandler {
|
||||
|
||||
@Target(ElementType.METHOD)
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
public @interface Init {
|
||||
}
|
||||
|
||||
@Target(ElementType.METHOD)
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
public @interface PayloadReceiver {
|
||||
Class<? extends LeavesCustomPayload> payload();
|
||||
|
||||
Stage stage() default Stage.GAME;
|
||||
}
|
||||
|
||||
@Target(ElementType.METHOD)
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
public @interface BytebufReceiver {
|
||||
String key() default "";
|
||||
|
||||
boolean onlyNamespace() default false;
|
||||
|
||||
Stage stage() default Stage.GAME;
|
||||
}
|
||||
|
||||
@Target(ElementType.METHOD)
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
public @interface Ticker {
|
||||
String tickerId() default "";
|
||||
}
|
||||
|
||||
@Target(ElementType.METHOD)
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
public @interface PlayerJoin {
|
||||
}
|
||||
|
||||
@Target(ElementType.METHOD)
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
public @interface PlayerLeave {
|
||||
}
|
||||
|
||||
@Target(ElementType.METHOD)
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
public @interface ReloadServer {
|
||||
}
|
||||
|
||||
@Target(ElementType.METHOD)
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
public @interface MinecraftRegister {
|
||||
String key() default "";
|
||||
|
||||
boolean onlyNamespace() default false;
|
||||
|
||||
Stage stage() default Stage.CONFIGURATION;
|
||||
}
|
||||
|
||||
@Target(ElementType.METHOD)
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
public @interface ReloadDataPack {
|
||||
}
|
||||
|
||||
public enum Stage {
|
||||
CONFIGURATION(Context.class),
|
||||
GAME(ServerPlayer.class);
|
||||
|
||||
private final Class<?> identifier;
|
||||
|
||||
Stage(Class<?> identifier) {
|
||||
this.identifier = identifier;
|
||||
}
|
||||
|
||||
public Class<?> identifier() {
|
||||
return identifier;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,89 @@
|
||||
/*
|
||||
* This file is part of Leaves (https://github.com/LeavesMC/Leaves)
|
||||
*
|
||||
* Leaves is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Leaves is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with Leaves. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
|
||||
package org.leavesmc.leaves.protocol.core;
|
||||
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import io.netty.buffer.ByteBufUtil;
|
||||
import io.netty.buffer.Unpooled;
|
||||
import io.papermc.paper.ServerBuildInfo;
|
||||
import net.minecraft.network.RegistryFriendlyByteBuf;
|
||||
import net.minecraft.network.protocol.common.ClientboundCustomPayloadPacket;
|
||||
import net.minecraft.network.protocol.common.custom.CustomPacketPayload;
|
||||
import net.minecraft.network.protocol.common.custom.DiscardedPayload;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import net.minecraft.server.MinecraftServer;
|
||||
import net.minecraft.server.level.ServerPlayer;
|
||||
import net.minecraft.server.network.ServerCommonPacketListenerImpl;
|
||||
import net.minecraft.server.network.ServerGamePacketListenerImpl;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.function.Consumer;
|
||||
import java.util.function.Function;
|
||||
|
||||
public class ProtocolUtils {
|
||||
|
||||
private static final Function<ByteBuf, RegistryFriendlyByteBuf> bufDecorator = buf -> buf instanceof RegistryFriendlyByteBuf registry ? registry : new RegistryFriendlyByteBuf(buf, MinecraftServer.getServer().registryAccess());
|
||||
private static final byte[] EMPTY = new byte[0];
|
||||
|
||||
public static String buildProtocolVersion(String protocol) {
|
||||
return protocol + "-lophine-" + ServerBuildInfo.buildInfo().asString(ServerBuildInfo.StringRepresentation.VERSION_SIMPLE);
|
||||
}
|
||||
|
||||
public static void sendEmptyPacket(ServerPlayer player, ResourceLocation id) {
|
||||
player.connection.send(new ClientboundCustomPayloadPacket(new DiscardedPayload(id, EMPTY)));
|
||||
}
|
||||
|
||||
public static void sendBytebufPacket(@NotNull ServerPlayer player, ResourceLocation id, Consumer<? super RegistryFriendlyByteBuf> consumer) {
|
||||
RegistryFriendlyByteBuf buf = decorate(Unpooled.buffer());
|
||||
consumer.accept(buf);
|
||||
player.connection.send(new ClientboundCustomPayloadPacket(new DiscardedPayload(id, ByteBufUtil.getBytes(buf))));
|
||||
}
|
||||
|
||||
public static void sendPayloadPacket(ServerPlayer player, CustomPacketPayload payload) {
|
||||
player.connection.send(new ClientboundCustomPayloadPacket(payload));
|
||||
}
|
||||
|
||||
public static void sendEmptyPacket(Context context, ResourceLocation id) {
|
||||
context.connection().send(new ClientboundCustomPayloadPacket(new DiscardedPayload(id, EMPTY)));
|
||||
}
|
||||
|
||||
public static void sendBytebufPacket(@NotNull Context context, ResourceLocation id, Consumer<? super RegistryFriendlyByteBuf> consumer) {
|
||||
RegistryFriendlyByteBuf buf = decorate(Unpooled.buffer());
|
||||
consumer.accept(buf);
|
||||
context.connection().send(new ClientboundCustomPayloadPacket(new DiscardedPayload(id, ByteBufUtil.getBytes(buf))));
|
||||
}
|
||||
|
||||
public static void sendPayloadPacket(Context context, CustomPacketPayload payload) {
|
||||
context.connection().send(new ClientboundCustomPayloadPacket(payload));
|
||||
}
|
||||
|
||||
public static RegistryFriendlyByteBuf decorate(ByteBuf buf) {
|
||||
return bufDecorator.apply(buf);
|
||||
}
|
||||
|
||||
public static IdentifierSelector createSelector(ServerCommonPacketListenerImpl common) {
|
||||
ServerPlayer player = common instanceof ServerGamePacketListenerImpl game ? game.getPlayer() : null;
|
||||
return new IdentifierSelector(new Context(common.profile, common.connection), player);
|
||||
}
|
||||
|
||||
public static ByteBuf wrapNullable(byte @Nullable [] data) {
|
||||
return data == null ? Unpooled.wrappedBuffer(EMPTY) : Unpooled.wrappedBuffer(data);
|
||||
}
|
||||
}
|
||||
+92
@@ -0,0 +1,92 @@
|
||||
/*
|
||||
* This file is part of Leaves (https://github.com/LeavesMC/Leaves)
|
||||
*
|
||||
* Leaves is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Leaves is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with Leaves. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package org.leavesmc.leaves.protocol.core.invoker;
|
||||
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.leavesmc.leaves.protocol.core.LeavesProtocol;
|
||||
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.lang.reflect.Method;
|
||||
import java.lang.reflect.Modifier;
|
||||
|
||||
public abstract class AbstractInvokerHolder<T> {
|
||||
|
||||
protected final LeavesProtocol owner;
|
||||
protected final Method invoker;
|
||||
protected final T handler;
|
||||
protected final Class<?> returnType;
|
||||
protected final Class<?>[] parameterTypes;
|
||||
protected final boolean isStatic;
|
||||
|
||||
protected AbstractInvokerHolder(LeavesProtocol owner, Method invoker, T handler, @Nullable Class<?> returnType, @NotNull Class<?>... parameterTypes) {
|
||||
this.owner = owner;
|
||||
this.invoker = invoker;
|
||||
this.handler = handler;
|
||||
this.returnType = returnType;
|
||||
this.parameterTypes = parameterTypes;
|
||||
this.isStatic = Modifier.isStatic(invoker.getModifiers());
|
||||
|
||||
validateMethodSignature();
|
||||
}
|
||||
|
||||
protected void validateMethodSignature() {
|
||||
if (returnType != null && !returnType.isAssignableFrom(invoker.getReturnType())) {
|
||||
throw new IllegalArgumentException("Return type mismatch in " + owner.getClass().getName() + "#" + invoker.getName() +
|
||||
": expected " + returnType.getName() + " but found " + invoker.getReturnType().getName());
|
||||
}
|
||||
|
||||
Class<?>[] methodParamTypes = invoker.getParameterTypes();
|
||||
if (methodParamTypes.length != parameterTypes.length) {
|
||||
throw new IllegalArgumentException("Parameter count mismatch in " + owner.getClass().getName() + "#" + invoker.getName() +
|
||||
": expected " + parameterTypes.length + " but found " + methodParamTypes.length);
|
||||
}
|
||||
|
||||
for (int i = 0; i < parameterTypes.length; i++) {
|
||||
if (!parameterTypes[i].isAssignableFrom(methodParamTypes[i])) {
|
||||
throw new IllegalArgumentException("Parameter type mismatch in " + owner.getClass().getName() + "#" + invoker.getName() +
|
||||
" at index " + i + ": expected " + parameterTypes[i].getName() + " but found " + methodParamTypes[i].getName());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public LeavesProtocol owner() {
|
||||
return owner;
|
||||
}
|
||||
|
||||
public T handler() {
|
||||
return handler;
|
||||
}
|
||||
|
||||
protected Object invoke0(boolean force, Object... args) {
|
||||
if (!force && !owner.isActive()) {
|
||||
return null;
|
||||
}
|
||||
try {
|
||||
if (isStatic) {
|
||||
return invoker.invoke(null, args);
|
||||
} else {
|
||||
return invoker.invoke(owner, args);
|
||||
}
|
||||
} catch (InvocationTargetException e) {
|
||||
throw new RuntimeException(e.getCause());
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
+35
@@ -0,0 +1,35 @@
|
||||
/*
|
||||
* This file is part of Leaves (https://github.com/LeavesMC/Leaves)
|
||||
*
|
||||
* Leaves is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Leaves is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with Leaves. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package org.leavesmc.leaves.protocol.core.invoker;
|
||||
|
||||
import net.minecraft.network.FriendlyByteBuf;
|
||||
import org.leavesmc.leaves.protocol.core.IdentifierSelector;
|
||||
import org.leavesmc.leaves.protocol.core.LeavesProtocol;
|
||||
import org.leavesmc.leaves.protocol.core.ProtocolHandler;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
|
||||
public class BytebufReceiverInvokerHolder extends AbstractInvokerHolder<ProtocolHandler.BytebufReceiver> {
|
||||
public BytebufReceiverInvokerHolder(LeavesProtocol owner, Method invoker, ProtocolHandler.BytebufReceiver handler) {
|
||||
super(owner, invoker, handler, null, handler.stage().identifier(), FriendlyByteBuf.class);
|
||||
}
|
||||
|
||||
public boolean invoke(IdentifierSelector selector, FriendlyByteBuf buf) {
|
||||
return invoke0(false, selector.select(handler.stage()), buf) instanceof Boolean b && b;
|
||||
}
|
||||
}
|
||||
+32
@@ -0,0 +1,32 @@
|
||||
/*
|
||||
* This file is part of Leaves (https://github.com/LeavesMC/Leaves)
|
||||
*
|
||||
* Leaves is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Leaves is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with Leaves. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package org.leavesmc.leaves.protocol.core.invoker;
|
||||
|
||||
import org.leavesmc.leaves.protocol.core.LeavesProtocol;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
|
||||
public class EmptyInvokerHolder<T> extends AbstractInvokerHolder<T> {
|
||||
public EmptyInvokerHolder(LeavesProtocol owner, Method invoker, T handler) {
|
||||
super(owner, invoker, handler, null);
|
||||
}
|
||||
|
||||
public void invoke() {
|
||||
invoke0(false);
|
||||
}
|
||||
}
|
||||
+33
@@ -0,0 +1,33 @@
|
||||
/*
|
||||
* This file is part of Leaves (https://github.com/LeavesMC/Leaves)
|
||||
*
|
||||
* Leaves is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Leaves is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with Leaves. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package org.leavesmc.leaves.protocol.core.invoker;
|
||||
|
||||
import org.leavesmc.leaves.protocol.core.LeavesProtocol;
|
||||
import org.leavesmc.leaves.protocol.core.ProtocolHandler;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
|
||||
public class InitInvokerHolder extends AbstractInvokerHolder<ProtocolHandler.Init> {
|
||||
public InitInvokerHolder(LeavesProtocol owner, Method invoker, ProtocolHandler.Init handler) {
|
||||
super(owner, invoker, handler, null);
|
||||
}
|
||||
|
||||
public void invoke() {
|
||||
invoke0(true);
|
||||
}
|
||||
}
|
||||
+35
@@ -0,0 +1,35 @@
|
||||
/*
|
||||
* This file is part of Leaves (https://github.com/LeavesMC/Leaves)
|
||||
*
|
||||
* Leaves is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Leaves is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with Leaves. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package org.leavesmc.leaves.protocol.core.invoker;
|
||||
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import org.leavesmc.leaves.protocol.core.IdentifierSelector;
|
||||
import org.leavesmc.leaves.protocol.core.LeavesProtocol;
|
||||
import org.leavesmc.leaves.protocol.core.ProtocolHandler;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
|
||||
public class MinecraftRegisterInvokerHolder extends AbstractInvokerHolder<ProtocolHandler.MinecraftRegister> {
|
||||
public MinecraftRegisterInvokerHolder(LeavesProtocol owner, Method invoker, ProtocolHandler.MinecraftRegister handler) {
|
||||
super(owner, invoker, handler, null, handler.stage().identifier(), ResourceLocation.class);
|
||||
}
|
||||
|
||||
public void invoke(IdentifierSelector selector, ResourceLocation id) {
|
||||
invoke0(false, selector.select(handler.stage()), id);
|
||||
}
|
||||
}
|
||||
+35
@@ -0,0 +1,35 @@
|
||||
/*
|
||||
* This file is part of Leaves (https://github.com/LeavesMC/Leaves)
|
||||
*
|
||||
* Leaves is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Leaves is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with Leaves. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package org.leavesmc.leaves.protocol.core.invoker;
|
||||
|
||||
import org.leavesmc.leaves.protocol.core.IdentifierSelector;
|
||||
import org.leavesmc.leaves.protocol.core.LeavesCustomPayload;
|
||||
import org.leavesmc.leaves.protocol.core.LeavesProtocol;
|
||||
import org.leavesmc.leaves.protocol.core.ProtocolHandler;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
|
||||
public class PayloadReceiverInvokerHolder extends AbstractInvokerHolder<ProtocolHandler.PayloadReceiver> {
|
||||
public PayloadReceiverInvokerHolder(LeavesProtocol owner, Method invoker, ProtocolHandler.PayloadReceiver handler) {
|
||||
super(owner, invoker, handler, null, handler.stage().identifier(), handler.payload());
|
||||
}
|
||||
|
||||
public void invoke(IdentifierSelector selector, LeavesCustomPayload payload) {
|
||||
invoke0(false, selector.select(handler.stage()), payload);
|
||||
}
|
||||
}
|
||||
+33
@@ -0,0 +1,33 @@
|
||||
/*
|
||||
* This file is part of Leaves (https://github.com/LeavesMC/Leaves)
|
||||
*
|
||||
* Leaves is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Leaves is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with Leaves. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package org.leavesmc.leaves.protocol.core.invoker;
|
||||
|
||||
import net.minecraft.server.level.ServerPlayer;
|
||||
import org.leavesmc.leaves.protocol.core.LeavesProtocol;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
|
||||
public class PlayerInvokerHolder<T> extends AbstractInvokerHolder<T> {
|
||||
public PlayerInvokerHolder(LeavesProtocol owner, Method invoker, T handler) {
|
||||
super(owner, invoker, handler, null, ServerPlayer.class);
|
||||
}
|
||||
|
||||
public void invoke(ServerPlayer player) {
|
||||
invoke0(false, player);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,134 @@
|
||||
/*
|
||||
* This file is part of Leaves (https://github.com/LeavesMC/Leaves)
|
||||
*
|
||||
* Leaves is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Leaves is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with Leaves. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package org.leavesmc.leaves.protocol.servux;
|
||||
|
||||
import io.netty.buffer.Unpooled;
|
||||
import net.minecraft.network.FriendlyByteBuf;
|
||||
import net.minecraft.server.level.ServerPlayer;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
// Powered by Servux(https://github.com/sakura-ryoko/servux)
|
||||
|
||||
/**
|
||||
* Network packet splitter code from QuickCarpet by skyrising
|
||||
*
|
||||
* @author skyrising
|
||||
* <p>
|
||||
* Updated by Sakura to work with newer versions by changing the Reading Session keys,
|
||||
* and using the HANDLER interface to send packets via the Payload system
|
||||
* <p>
|
||||
* Move to Leaves by violetc
|
||||
*/
|
||||
public class PacketSplitter {
|
||||
public static final int MAX_TOTAL_PER_PACKET_S2C = 1048576;
|
||||
public static final int MAX_PAYLOAD_PER_PACKET_S2C = MAX_TOTAL_PER_PACKET_S2C - 5;
|
||||
public static final int MAX_TOTAL_PER_PACKET_C2S = 32767;
|
||||
public static final int MAX_PAYLOAD_PER_PACKET_C2S = MAX_TOTAL_PER_PACKET_C2S - 5;
|
||||
public static final int DEFAULT_MAX_RECEIVE_SIZE_C2S = 1048576;
|
||||
public static final int DEFAULT_MAX_RECEIVE_SIZE_S2C = 67108864;
|
||||
|
||||
private static final Map<Long, ReadingSession> READING_SESSIONS = new HashMap<>();
|
||||
|
||||
public static boolean send(IPacketSplitterHandler handler, FriendlyByteBuf packet, ServerPlayer player) {
|
||||
return send(handler, packet, player, MAX_PAYLOAD_PER_PACKET_S2C);
|
||||
}
|
||||
|
||||
private static boolean send(IPacketSplitterHandler handler, FriendlyByteBuf packet, ServerPlayer player, int payloadLimit) {
|
||||
int len = packet.writerIndex();
|
||||
|
||||
packet.resetReaderIndex();
|
||||
|
||||
for (int offset = 0; offset < len; offset += payloadLimit) {
|
||||
int thisLen = Math.min(len - offset, payloadLimit);
|
||||
FriendlyByteBuf buf = new FriendlyByteBuf(Unpooled.buffer(thisLen));
|
||||
|
||||
buf.resetWriterIndex();
|
||||
|
||||
if (offset == 0) {
|
||||
buf.writeVarInt(len);
|
||||
}
|
||||
|
||||
buf.writeBytes(packet, thisLen);
|
||||
handler.encode(player, buf);
|
||||
}
|
||||
|
||||
packet.release();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public static FriendlyByteBuf receive(long key, FriendlyByteBuf buf) {
|
||||
return receive(key, buf, DEFAULT_MAX_RECEIVE_SIZE_S2C);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private static FriendlyByteBuf receive(long key, FriendlyByteBuf buf, int maxLength) {
|
||||
return READING_SESSIONS.computeIfAbsent(key, ReadingSession::new).receive(buf, maxLength);
|
||||
}
|
||||
|
||||
public interface IPacketSplitterHandler {
|
||||
void encode(ServerPlayer player, FriendlyByteBuf buf);
|
||||
}
|
||||
|
||||
/**
|
||||
* I had to fix the `Pair.of` key mappings, because they were removed from MC;
|
||||
* So I made it into a pre-shared random session 'key' between client and server.
|
||||
* Generated using 'long key = Random.create(Util.getMeasuringTimeMs()).nextLong();'
|
||||
* -
|
||||
* It can be shared to the receiving end via a separate packet; or it can just be
|
||||
* generated randomly on the receiving end per an expected Reading Session.
|
||||
* It needs to be stored and changed for every unique session.
|
||||
*/
|
||||
private static class ReadingSession {
|
||||
private final long key;
|
||||
private int expectedSize = -1;
|
||||
private FriendlyByteBuf received;
|
||||
|
||||
private ReadingSession(long key) {
|
||||
this.key = key;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private FriendlyByteBuf receive(FriendlyByteBuf data, int maxLength) {
|
||||
data.readerIndex(0);
|
||||
// data = PacketUtils.slice(data);
|
||||
|
||||
if (this.expectedSize < 0) {
|
||||
this.expectedSize = data.readVarInt();
|
||||
|
||||
if (this.expectedSize > maxLength) {
|
||||
throw new IllegalArgumentException("Payload too large");
|
||||
}
|
||||
|
||||
this.received = new FriendlyByteBuf(Unpooled.buffer(this.expectedSize));
|
||||
}
|
||||
|
||||
this.received.writeBytes(data.readBytes(data.readableBytes()));
|
||||
|
||||
if (this.received.writerIndex() >= this.expectedSize) {
|
||||
READING_SESSIONS.remove(this.key);
|
||||
return this.received;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
+286
@@ -0,0 +1,286 @@
|
||||
/*
|
||||
* This file is part of Leaves (https://github.com/LeavesMC/Leaves)
|
||||
*
|
||||
* Leaves is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Leaves is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with Leaves. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package org.leavesmc.leaves.protocol.servux;
|
||||
|
||||
import com.mojang.logging.LogUtils;
|
||||
import io.netty.buffer.Unpooled;
|
||||
import net.minecraft.Util;
|
||||
import net.minecraft.core.BlockPos;
|
||||
import net.minecraft.nbt.CompoundTag;
|
||||
import net.minecraft.network.FriendlyByteBuf;
|
||||
import net.minecraft.network.codec.StreamCodec;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import net.minecraft.server.level.ServerPlayer;
|
||||
import net.minecraft.util.RandomSource;
|
||||
import net.minecraft.world.entity.Entity;
|
||||
import net.minecraft.world.level.block.entity.BlockEntity;
|
||||
import org.leavesmc.leaves.protocol.core.LeavesCustomPayload;
|
||||
import org.leavesmc.leaves.protocol.core.LeavesProtocol;
|
||||
import org.leavesmc.leaves.protocol.core.ProtocolHandler;
|
||||
import org.leavesmc.leaves.protocol.core.ProtocolUtils;
|
||||
import org.leavesmc.leaves.util.TagUtil;
|
||||
import org.slf4j.Logger;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.UUID;
|
||||
|
||||
// Powered by Servux(https://github.com/sakura-ryoko/servux)
|
||||
|
||||
@LeavesProtocol.Register(namespace = "servux")
|
||||
public class ServuxEntityDataProtocol implements LeavesProtocol {
|
||||
private static final Logger LOGGER = LogUtils.getClassLogger();
|
||||
|
||||
public static final int PROTOCOL_VERSION = 1;
|
||||
|
||||
private static final Map<UUID, Long> readingSessionKeys = new HashMap<>();
|
||||
|
||||
@ProtocolHandler.PlayerJoin
|
||||
public static void onPlayerJoin(ServerPlayer player) {
|
||||
sendMetadata(player);
|
||||
}
|
||||
|
||||
@ProtocolHandler.PlayerLeave
|
||||
public static void onPlayerLeave(ServerPlayer player) {
|
||||
readingSessionKeys.remove(player.getUUID());
|
||||
}
|
||||
|
||||
@ProtocolHandler.PayloadReceiver(payload = EntityDataPayload.class)
|
||||
public static void onPacketReceive(ServerPlayer player, EntityDataPayload payload) {
|
||||
switch (payload.packetType) {
|
||||
case PACKET_C2S_METADATA_REQUEST -> sendMetadata(player);
|
||||
case PACKET_C2S_BLOCK_ENTITY_REQUEST -> onBlockEntityRequest(player, payload.pos);
|
||||
case PACKET_C2S_ENTITY_REQUEST -> onEntityRequest(player, payload.entityId);
|
||||
case PACKET_C2S_NBT_RESPONSE_DATA -> {
|
||||
UUID uuid = player.getUUID();
|
||||
long readingSessionKey;
|
||||
|
||||
if (!readingSessionKeys.containsKey(uuid)) {
|
||||
readingSessionKey = RandomSource.create(Util.getMillis()).nextLong();
|
||||
readingSessionKeys.put(uuid, readingSessionKey);
|
||||
} else {
|
||||
readingSessionKey = readingSessionKeys.get(uuid);
|
||||
}
|
||||
|
||||
FriendlyByteBuf fullPacket = PacketSplitter.receive(readingSessionKey, payload.buffer);
|
||||
|
||||
if (fullPacket != null) {
|
||||
readingSessionKeys.remove(uuid);
|
||||
LOGGER.warn("ServuxEntityDataProtocol,PACKET_C2S_NBT_RESPONSE_DATA NOT Implemented!");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static void sendMetadata(ServerPlayer player) {
|
||||
CompoundTag metadata = new CompoundTag();
|
||||
metadata.putString("name", "entity_data");
|
||||
metadata.putString("id", EntityDataPayload.CHANNEL.toString());
|
||||
metadata.putInt("version", PROTOCOL_VERSION);
|
||||
metadata.putString("servux", ServuxProtocol.SERVUX_STRING);
|
||||
|
||||
EntityDataPayload payload = new EntityDataPayload(EntityDataPayloadType.PACKET_S2C_METADATA);
|
||||
payload.nbt.merge(metadata);
|
||||
sendPacket(player, payload);
|
||||
}
|
||||
|
||||
public static void onBlockEntityRequest(ServerPlayer player, BlockPos pos) {
|
||||
io.papermc.paper.threadedregions.RegionizedServer.getInstance().taskQueue.queueTickTaskQueue(
|
||||
player.level(),
|
||||
ca.spottedleaf.moonrise.common.util.CoordinateUtils.getChunkX(player.position()),
|
||||
ca.spottedleaf.moonrise.common.util.CoordinateUtils.getChunkZ(player.position()),
|
||||
() -> {
|
||||
BlockEntity be = player.level().getBlockEntity(pos);
|
||||
CompoundTag nbt = be != null ? be.saveWithFullMetadata(player.registryAccess()) : new CompoundTag();
|
||||
|
||||
EntityDataPayload payload = new EntityDataPayload(EntityDataPayloadType.PACKET_S2C_BLOCK_NBT_RESPONSE_SIMPLE);
|
||||
payload.pos = pos.immutable();
|
||||
payload.nbt.merge(nbt);
|
||||
sendPacket(player, payload);
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
public static void onEntityRequest(ServerPlayer player, int entityId) {
|
||||
io.papermc.paper.threadedregions.RegionizedServer.getInstance().taskQueue.queueTickTaskQueue(
|
||||
player.level(),
|
||||
ca.spottedleaf.moonrise.common.util.CoordinateUtils.getChunkX(player.position()),
|
||||
ca.spottedleaf.moonrise.common.util.CoordinateUtils.getChunkZ(player.position()),
|
||||
() -> {
|
||||
Entity entity = player.level().getEntity(entityId);
|
||||
CompoundTag nbt = TagUtil.saveEntityWithoutId(entity);
|
||||
|
||||
EntityDataPayload payload = new EntityDataPayload(EntityDataPayloadType.PACKET_S2C_ENTITY_NBT_RESPONSE_SIMPLE);
|
||||
payload.entityId = entityId;
|
||||
payload.nbt.merge(nbt);
|
||||
sendPacket(player, payload);
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
public static void sendPacket(ServerPlayer player, EntityDataPayload payload) {
|
||||
if (payload.packetType == EntityDataPayloadType.PACKET_S2C_NBT_RESPONSE_START) {
|
||||
FriendlyByteBuf buffer = new FriendlyByteBuf(Unpooled.buffer());
|
||||
buffer.writeNbt(payload.nbt);
|
||||
PacketSplitter.send(ServuxEntityDataProtocol::sendWithSplitter, buffer, player);
|
||||
} else {
|
||||
ProtocolUtils.sendPayloadPacket(player, payload);
|
||||
}
|
||||
}
|
||||
|
||||
private static void sendWithSplitter(ServerPlayer player, FriendlyByteBuf buf) {
|
||||
EntityDataPayload payload = new EntityDataPayload(EntityDataPayloadType.PACKET_S2C_NBT_RESPONSE_DATA);
|
||||
payload.buffer = buf;
|
||||
payload.nbt = new CompoundTag();
|
||||
sendPacket(player, payload);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isActive() {
|
||||
return fun.bm.lophine.config.modules.misc.SurvuxProtocolConfig.entityProtocol;
|
||||
}
|
||||
|
||||
public enum EntityDataPayloadType {
|
||||
PACKET_S2C_METADATA(1),
|
||||
PACKET_C2S_METADATA_REQUEST(2),
|
||||
PACKET_C2S_BLOCK_ENTITY_REQUEST(3),
|
||||
PACKET_C2S_ENTITY_REQUEST(4),
|
||||
PACKET_S2C_BLOCK_NBT_RESPONSE_SIMPLE(5),
|
||||
PACKET_S2C_ENTITY_NBT_RESPONSE_SIMPLE(6),
|
||||
// For Packet Splitter (Oversize Packets, S2C)
|
||||
PACKET_S2C_NBT_RESPONSE_START(10),
|
||||
PACKET_S2C_NBT_RESPONSE_DATA(11),
|
||||
// For Packet Splitter (Oversize Packets, C2S)
|
||||
PACKET_C2S_NBT_RESPONSE_START(12),
|
||||
PACKET_C2S_NBT_RESPONSE_DATA(13);
|
||||
|
||||
public final int type;
|
||||
|
||||
EntityDataPayloadType(int type) {
|
||||
this.type = type;
|
||||
Helper.ID_TO_TYPE.put(type, this);
|
||||
}
|
||||
|
||||
public static EntityDataPayloadType fromId(int id) {
|
||||
return Helper.ID_TO_TYPE.get(id);
|
||||
}
|
||||
|
||||
private static final class Helper {
|
||||
static Map<Integer, EntityDataPayloadType> ID_TO_TYPE = new HashMap<>();
|
||||
}
|
||||
}
|
||||
|
||||
public static class EntityDataPayload implements LeavesCustomPayload {
|
||||
|
||||
@ID
|
||||
public static final ResourceLocation CHANNEL = ServuxProtocol.id("entity_data");
|
||||
|
||||
@Codec
|
||||
public static final StreamCodec<FriendlyByteBuf, EntityDataPayload> CODEC = StreamCodec.of(
|
||||
(buf, payload) -> {
|
||||
buf.writeVarInt(payload.packetType.type);
|
||||
switch (payload.packetType) {
|
||||
case PACKET_C2S_BLOCK_ENTITY_REQUEST -> {
|
||||
buf.writeVarInt(payload.transactionId);
|
||||
buf.writeBlockPos(payload.pos);
|
||||
}
|
||||
case PACKET_C2S_ENTITY_REQUEST -> {
|
||||
buf.writeVarInt(payload.transactionId);
|
||||
buf.writeVarInt(payload.entityId);
|
||||
}
|
||||
case PACKET_S2C_BLOCK_NBT_RESPONSE_SIMPLE -> {
|
||||
buf.writeBlockPos(payload.pos);
|
||||
buf.writeNbt(payload.nbt);
|
||||
}
|
||||
case PACKET_S2C_ENTITY_NBT_RESPONSE_SIMPLE -> {
|
||||
buf.writeVarInt(payload.entityId);
|
||||
buf.writeNbt(payload.nbt);
|
||||
}
|
||||
case PACKET_S2C_NBT_RESPONSE_DATA, PACKET_C2S_NBT_RESPONSE_DATA ->
|
||||
buf.writeBytes(payload.buffer.copy());
|
||||
case PACKET_C2S_METADATA_REQUEST, PACKET_S2C_METADATA -> buf.writeNbt(payload.nbt);
|
||||
}
|
||||
},
|
||||
buf -> {
|
||||
EntityDataPayloadType type = EntityDataPayloadType.fromId(buf.readVarInt());
|
||||
if (type == null) {
|
||||
throw new IllegalStateException("invalid packet type received");
|
||||
}
|
||||
EntityDataPayload payload = new EntityDataPayload(type);
|
||||
switch (type) {
|
||||
case PACKET_C2S_BLOCK_ENTITY_REQUEST -> {
|
||||
buf.readVarInt();
|
||||
payload.pos = buf.readBlockPos().immutable();
|
||||
}
|
||||
case PACKET_C2S_ENTITY_REQUEST -> {
|
||||
buf.readVarInt();
|
||||
payload.entityId = buf.readVarInt();
|
||||
}
|
||||
case PACKET_S2C_BLOCK_NBT_RESPONSE_SIMPLE -> {
|
||||
payload.pos = buf.readBlockPos().immutable();
|
||||
CompoundTag nbt = buf.readNbt();
|
||||
if (nbt != null) {
|
||||
payload.nbt.merge(nbt);
|
||||
}
|
||||
}
|
||||
case PACKET_S2C_ENTITY_NBT_RESPONSE_SIMPLE -> {
|
||||
payload.entityId = buf.readVarInt();
|
||||
CompoundTag nbt = buf.readNbt();
|
||||
if (nbt != null) {
|
||||
payload.nbt.merge(nbt);
|
||||
}
|
||||
}
|
||||
case PACKET_S2C_NBT_RESPONSE_DATA, PACKET_C2S_NBT_RESPONSE_DATA -> {
|
||||
payload.buffer = new FriendlyByteBuf(buf.readBytes(buf.readableBytes()));
|
||||
payload.nbt = new CompoundTag();
|
||||
}
|
||||
case PACKET_C2S_METADATA_REQUEST, PACKET_S2C_METADATA -> {
|
||||
CompoundTag nbt = buf.readNbt();
|
||||
if (nbt != null) {
|
||||
payload.nbt.merge(nbt);
|
||||
}
|
||||
}
|
||||
}
|
||||
return payload;
|
||||
}
|
||||
);
|
||||
|
||||
private final EntityDataPayloadType packetType;
|
||||
private final int transactionId;
|
||||
private int entityId;
|
||||
private BlockPos pos;
|
||||
private CompoundTag nbt;
|
||||
private FriendlyByteBuf buffer;
|
||||
|
||||
private EntityDataPayload(EntityDataPayloadType type) {
|
||||
this.packetType = type;
|
||||
this.transactionId = -1;
|
||||
this.entityId = -1;
|
||||
this.pos = BlockPos.ZERO;
|
||||
this.nbt = new CompoundTag();
|
||||
this.clearPacket();
|
||||
}
|
||||
|
||||
private void clearPacket() {
|
||||
if (this.buffer != null) {
|
||||
this.buffer.clear();
|
||||
this.buffer = new FriendlyByteBuf(Unpooled.buffer());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+375
@@ -0,0 +1,375 @@
|
||||
/*
|
||||
* This file is part of Leaves (https://github.com/LeavesMC/Leaves)
|
||||
*
|
||||
* Leaves is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Leaves is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with Leaves. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package org.leavesmc.leaves.protocol.servux;
|
||||
|
||||
import com.google.common.collect.HashBasedTable;
|
||||
import com.google.common.collect.Table;
|
||||
import com.mojang.serialization.DataResult;
|
||||
import io.netty.buffer.Unpooled;
|
||||
import net.minecraft.core.BlockPos;
|
||||
import net.minecraft.nbt.CompoundTag;
|
||||
import net.minecraft.nbt.ListTag;
|
||||
import net.minecraft.nbt.NbtOps;
|
||||
import net.minecraft.nbt.Tag;
|
||||
import net.minecraft.network.FriendlyByteBuf;
|
||||
import net.minecraft.network.chat.Component;
|
||||
import net.minecraft.network.codec.StreamCodec;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import net.minecraft.server.MinecraftServer;
|
||||
import net.minecraft.server.level.ServerLevel;
|
||||
import net.minecraft.server.level.ServerPlayer;
|
||||
import net.minecraft.world.item.crafting.Recipe;
|
||||
import net.minecraft.world.item.crafting.RecipeHolder;
|
||||
import net.minecraft.world.level.GameRules;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.leavesmc.leaves.protocol.core.LeavesCustomPayload;
|
||||
import org.leavesmc.leaves.protocol.core.LeavesProtocol;
|
||||
import org.leavesmc.leaves.protocol.core.ProtocolHandler;
|
||||
import org.leavesmc.leaves.protocol.core.ProtocolUtils;
|
||||
import org.leavesmc.leaves.protocol.servux.logger.DataLogger;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
import java.util.*;
|
||||
|
||||
@LeavesProtocol.Register(namespace = "servux")
|
||||
public class ServuxHudDataProtocol implements LeavesProtocol {
|
||||
|
||||
public static final int PROTOCOL_VERSION = 2;
|
||||
|
||||
private static final List<ServerPlayer> players = new ArrayList<>();
|
||||
private static final int updateInterval = 80;
|
||||
|
||||
private static final HashMap<ServerPlayer, List<DataLogger.Type>> loggerPlayers = new HashMap<>();
|
||||
private static final HashMap<DataLogger.Type, DataLogger<?>> LOGGERS = new HashMap<>();
|
||||
private static final Table<DataLogger.Type, ServerPlayer, Tag> DATA = HashBasedTable.create();
|
||||
|
||||
public static boolean refreshSpawnMetadata = false;
|
||||
|
||||
@ProtocolHandler.Init
|
||||
private static void initializeLoggers() {
|
||||
if (!LOGGERS.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
for (DataLogger.Type type : DataLogger.Type.VALUES) {
|
||||
DataLogger<?> entry = type.init();
|
||||
if (entry != null) {
|
||||
LOGGERS.put(type, entry);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ProtocolHandler.PlayerJoin
|
||||
private static void onPlayerJoin(ServerPlayer player) {
|
||||
sendHudMetadata(player);
|
||||
}
|
||||
|
||||
@ProtocolHandler.PlayerLeave
|
||||
private static void onPlayerLeave(ServerPlayer player) {
|
||||
players.remove(player);
|
||||
loggerPlayers.remove(player);
|
||||
}
|
||||
|
||||
@ProtocolHandler.PayloadReceiver(payload = HudDataPayload.class)
|
||||
public static void onPacketReceive(ServerPlayer player, HudDataPayload payload) {
|
||||
switch (payload.packetType) {
|
||||
case PACKET_C2S_METADATA_REQUEST -> {
|
||||
players.add(player);
|
||||
sendHudMetadata(player);
|
||||
}
|
||||
case PACKET_C2S_SPAWN_DATA_REQUEST -> refreshSpawnMetadata(player);
|
||||
case PACKET_C2S_RECIPE_MANAGER_REQUEST -> refreshRecipeManager(player);
|
||||
case PACKET_C2S_DATA_LOGGER_REQUEST -> refreshLoggers(player, payload.nbt);
|
||||
}
|
||||
}
|
||||
|
||||
public static void sendHudMetadata(ServerPlayer player) {
|
||||
CompoundTag metadata = new CompoundTag();
|
||||
metadata.putString("name", "hud_metadata");
|
||||
metadata.putString("id", HudDataPayload.CHANNEL.toString());
|
||||
metadata.putInt("version", PROTOCOL_VERSION);
|
||||
metadata.putString("servux", ServuxProtocol.SERVUX_STRING);
|
||||
if (fun.bm.lophine.config.modules.misc.SurvuxProtocolConfig.hudLoggerProtocol) {
|
||||
CompoundTag nbt = new CompoundTag();
|
||||
for (DataLogger.Type type : DataLogger.Type.VALUES) {
|
||||
nbt.putBoolean(type.getSerializedName(), isLoggerTypeEnabled(type));
|
||||
}
|
||||
metadata.put("Loggers", nbt);
|
||||
}
|
||||
putWorldData(metadata);
|
||||
|
||||
sendPacket(player, new HudDataPayload(HudDataPayloadType.PACKET_S2C_METADATA, metadata));
|
||||
}
|
||||
|
||||
public static void refreshSpawnMetadata(ServerPlayer player) {
|
||||
CompoundTag metadata = new CompoundTag();
|
||||
metadata.putString("id", HudDataPayload.CHANNEL.toString());
|
||||
metadata.putString("servux", ServuxProtocol.SERVUX_STRING);
|
||||
putWorldData(metadata);
|
||||
|
||||
sendPacket(player, new HudDataPayload(HudDataPayloadType.PACKET_S2C_SPAWN_DATA, metadata));
|
||||
}
|
||||
|
||||
public static void refreshRecipeManager(ServerPlayer player) {
|
||||
Collection<RecipeHolder<?>> recipes = MinecraftServer.getServer().getRecipeManager().getRecipes();
|
||||
CompoundTag nbt = new CompoundTag();
|
||||
ListTag list = new ListTag();
|
||||
|
||||
recipes.forEach((recipeEntry -> {
|
||||
DataResult<Tag> dr = Recipe.CODEC.encodeStart(NbtOps.INSTANCE, recipeEntry.value());
|
||||
|
||||
if (dr.result().isPresent()) {
|
||||
CompoundTag entry = new CompoundTag();
|
||||
entry.putString("id_reg", recipeEntry.id().registry().toString());
|
||||
entry.putString("id_value", recipeEntry.id().location().toString());
|
||||
entry.put("recipe", dr.result().get());
|
||||
list.add(entry);
|
||||
}
|
||||
}));
|
||||
|
||||
nbt.put("RecipeManager", list);
|
||||
sendPacket(player, new HudDataPayload(HudDataPayloadType.PACKET_S2C_NBT_RESPONSE_START, nbt));
|
||||
}
|
||||
|
||||
public static void refreshWeatherData(ServerPlayer player) {
|
||||
ServerLevel level = MinecraftServer.getServer().overworld();
|
||||
if (!level.getGameRules().getBoolean(GameRules.RULE_WEATHER_CYCLE)) {
|
||||
return;
|
||||
}
|
||||
|
||||
CompoundTag nbt = new CompoundTag();
|
||||
nbt.putString("id", HudDataPayload.CHANNEL.toString());
|
||||
nbt.putString("servux", ServuxProtocol.SERVUX_STRING);
|
||||
|
||||
if (level.serverLevelData.isRaining() && level.serverLevelData.getRainTime() > -1) {
|
||||
nbt.putInt("SetRaining", level.serverLevelData.getRainTime());
|
||||
nbt.putBoolean("isRaining", true);
|
||||
} else {
|
||||
nbt.putBoolean("isRaining", false);
|
||||
}
|
||||
|
||||
if (level.serverLevelData.isThundering() && level.serverLevelData.getThunderTime() > -1) {
|
||||
nbt.putInt("SetThundering", level.serverLevelData.getThunderTime());
|
||||
nbt.putBoolean("isThundering", true);
|
||||
} else {
|
||||
nbt.putBoolean("isThundering", false);
|
||||
}
|
||||
|
||||
if (level.serverLevelData.getClearWeatherTime() > -1) {
|
||||
nbt.putInt("SetClear", level.serverLevelData.getClearWeatherTime());
|
||||
}
|
||||
|
||||
sendPacket(player, new HudDataPayload(HudDataPayloadType.PACKET_S2C_WEATHER_TICK, nbt));
|
||||
}
|
||||
|
||||
private static void putWorldData(@NotNull CompoundTag metadata) {
|
||||
ServerLevel level = MinecraftServer.getServer().overworld();
|
||||
BlockPos spawnPos = level.levelData.getSpawnPos();
|
||||
metadata.putInt("spawnPosX", spawnPos.getX());
|
||||
metadata.putInt("spawnPosY", spawnPos.getY());
|
||||
metadata.putInt("spawnPosZ", spawnPos.getZ());
|
||||
metadata.putInt("spawnChunkRadius", level.getGameRules().getInt(GameRules.RULE_SPAWN_CHUNK_RADIUS));
|
||||
|
||||
if (fun.bm.lophine.config.modules.misc.SurvuxProtocolConfig.hudMetadataShareSeed) {
|
||||
metadata.putLong("worldSeed", level.getSeed());
|
||||
}
|
||||
}
|
||||
|
||||
public static void refreshLoggers(ServerPlayer player, @Nonnull CompoundTag nbt) {
|
||||
if (!player.getBukkitEntity().hasPermission("servux.provider.hud_data.logger")) {
|
||||
player.sendSystemMessage(Component.translatable("servux.hud_data.error.insufficient_for_loggers", "any"));
|
||||
return;
|
||||
}
|
||||
|
||||
if (nbt.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
List<DataLogger.Type> list = new ArrayList<>();
|
||||
|
||||
for (String key : nbt.keySet()) {
|
||||
DataLogger.Type type = DataLogger.Type.fromStringStatic(key);
|
||||
|
||||
if (type != null && nbt.getBooleanOr(key, false)) {
|
||||
list.add(type);
|
||||
}
|
||||
}
|
||||
if (!list.isEmpty()) {
|
||||
loggerPlayers.put(player, list);
|
||||
} else {
|
||||
loggerPlayers.remove(player);
|
||||
}
|
||||
}
|
||||
|
||||
private static boolean isLoggerTypeEnabled(DataLogger.Type type) {
|
||||
return fun.bm.lophine.config.modules.misc.SurvuxProtocolConfig.hudEnabledLoggers.contains(type.getSerializedName());
|
||||
}
|
||||
|
||||
@ProtocolHandler.Ticker
|
||||
public void protocolTick() {
|
||||
for (ServerPlayer player : players) {
|
||||
if (refreshSpawnMetadata) {
|
||||
refreshSpawnMetadata(player);
|
||||
}
|
||||
refreshWeatherData(player);
|
||||
}
|
||||
refreshSpawnMetadata = false;
|
||||
}
|
||||
|
||||
@ProtocolHandler.Ticker(tickerId = "logger")
|
||||
public void loggerTick() {
|
||||
if (!fun.bm.lophine.config.modules.misc.SurvuxProtocolConfig.hudLoggerProtocol) {
|
||||
return;
|
||||
}
|
||||
|
||||
MinecraftServer server = MinecraftServer.getServer();
|
||||
|
||||
if (server.getTickCount() % fun.bm.lophine.config.modules.misc.SurvuxProtocolConfig.hudUpdateInterval == 0) {
|
||||
LOGGERS.forEach((type, logger) -> {
|
||||
if (!isLoggerTypeEnabled(type)) {
|
||||
return;
|
||||
}
|
||||
for (ServerPlayer player : players) {
|
||||
Tag ret = logger.getResult(server, this, player);
|
||||
if (ret != null) {
|
||||
DATA.put(type, player, ret);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
for (ServerPlayer player : loggerPlayers.keySet()) {
|
||||
List<DataLogger.Type> list = loggerPlayers.get(player);
|
||||
if (list.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
CompoundTag nbt = new CompoundTag();
|
||||
for (DataLogger.Type type : list) {
|
||||
Tag data = DATA.get(type, player);
|
||||
if (data != null) {
|
||||
nbt.put(type.getSerializedName(), data);
|
||||
DATA.remove(type, player);
|
||||
}
|
||||
}
|
||||
sendPacket(player, new HudDataPayload(HudDataPayloadType.PACKET_S2C_DATA_LOGGER_TICK, nbt));
|
||||
}
|
||||
}
|
||||
|
||||
public void applyData(DataLogger.Type type, ServerPlayer player, Tag tag) {
|
||||
DATA.put(type, player, tag);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isActive() {
|
||||
return fun.bm.lophine.config.modules.misc.SurvuxProtocolConfig.hudMetadataProtocol;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int tickerInterval(String tickerID) {
|
||||
return tickerID.equals("logger") ? 1 : updateInterval;
|
||||
}
|
||||
|
||||
public static void sendPacket(ServerPlayer player, HudDataPayload payload) {
|
||||
if (payload.packetType == HudDataPayloadType.PACKET_S2C_NBT_RESPONSE_START) {
|
||||
FriendlyByteBuf buffer = new FriendlyByteBuf(Unpooled.buffer());
|
||||
buffer.writeNbt(payload.nbt);
|
||||
PacketSplitter.send(ServuxHudDataProtocol::sendWithSplitter, buffer, player);
|
||||
} else {
|
||||
ProtocolUtils.sendPayloadPacket(player, payload);
|
||||
}
|
||||
}
|
||||
|
||||
private static void sendWithSplitter(ServerPlayer player, FriendlyByteBuf buf) {
|
||||
sendPacket(player, new HudDataPayload(HudDataPayloadType.PACKET_S2C_NBT_RESPONSE_DATA, buf));
|
||||
}
|
||||
|
||||
public enum HudDataPayloadType {
|
||||
PACKET_S2C_METADATA(1),
|
||||
PACKET_C2S_METADATA_REQUEST(2),
|
||||
PACKET_S2C_SPAWN_DATA(3),
|
||||
PACKET_C2S_SPAWN_DATA_REQUEST(4),
|
||||
PACKET_S2C_WEATHER_TICK(5),
|
||||
PACKET_C2S_RECIPE_MANAGER_REQUEST(6),
|
||||
PACKET_S2C_DATA_LOGGER_TICK(7),
|
||||
PACKET_C2S_DATA_LOGGER_REQUEST(8),
|
||||
// For Packet Splitter (Oversize Packets, S2C)
|
||||
PACKET_S2C_NBT_RESPONSE_START(10),
|
||||
PACKET_S2C_NBT_RESPONSE_DATA(11);
|
||||
|
||||
public final int type;
|
||||
|
||||
HudDataPayloadType(int type) {
|
||||
this.type = type;
|
||||
Helper.ID_TO_TYPE.put(type, this);
|
||||
}
|
||||
|
||||
public static HudDataPayloadType fromId(int id) {
|
||||
return Helper.ID_TO_TYPE.get(id);
|
||||
}
|
||||
|
||||
private static final class Helper {
|
||||
static Map<Integer, HudDataPayloadType> ID_TO_TYPE = new HashMap<>();
|
||||
}
|
||||
}
|
||||
|
||||
public record HudDataPayload(HudDataPayloadType packetType, CompoundTag nbt,
|
||||
FriendlyByteBuf buffer) implements LeavesCustomPayload {
|
||||
|
||||
@ID
|
||||
public static final ResourceLocation CHANNEL = ServuxProtocol.id("hud_metadata");
|
||||
|
||||
@Codec
|
||||
public static final StreamCodec<FriendlyByteBuf, HudDataPayload> CODEC = StreamCodec.of(
|
||||
(buf, payload) -> {
|
||||
buf.writeVarInt(payload.packetType().type);
|
||||
switch (payload.packetType()) {
|
||||
case PACKET_S2C_NBT_RESPONSE_DATA -> buf.writeBytes(payload.buffer().copy());
|
||||
case PACKET_C2S_METADATA_REQUEST, PACKET_S2C_METADATA, PACKET_C2S_SPAWN_DATA_REQUEST,
|
||||
PACKET_S2C_SPAWN_DATA, PACKET_S2C_WEATHER_TICK, PACKET_C2S_RECIPE_MANAGER_REQUEST,
|
||||
PACKET_C2S_DATA_LOGGER_REQUEST, PACKET_S2C_DATA_LOGGER_TICK -> buf.writeNbt(payload.nbt());
|
||||
}
|
||||
},
|
||||
buf -> {
|
||||
HudDataPayloadType type = HudDataPayloadType.fromId(buf.readVarInt());
|
||||
if (type == null) {
|
||||
throw new IllegalStateException("invalid packet type received");
|
||||
}
|
||||
switch (type) {
|
||||
case PACKET_S2C_NBT_RESPONSE_DATA -> {
|
||||
return new HudDataPayload(type, new FriendlyByteBuf(buf.readBytes(buf.readableBytes())));
|
||||
}
|
||||
case PACKET_C2S_METADATA_REQUEST, PACKET_S2C_METADATA, PACKET_C2S_SPAWN_DATA_REQUEST,
|
||||
PACKET_S2C_SPAWN_DATA, PACKET_S2C_WEATHER_TICK,
|
||||
PACKET_C2S_RECIPE_MANAGER_REQUEST, PACKET_C2S_DATA_LOGGER_REQUEST,
|
||||
PACKET_S2C_DATA_LOGGER_TICK -> {
|
||||
return new HudDataPayload(type, buf.readNbt());
|
||||
}
|
||||
}
|
||||
throw new IllegalStateException("invalid packet type received");
|
||||
}
|
||||
);
|
||||
|
||||
public HudDataPayload(HudDataPayloadType type, CompoundTag nbt) {
|
||||
this(type, nbt, null);
|
||||
}
|
||||
|
||||
public HudDataPayload(HudDataPayloadType type, FriendlyByteBuf buffer) {
|
||||
this(type, new CompoundTag(), buffer);
|
||||
}
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user