Compare commits

...

50 Commits

Author SHA1 Message Date
Helvetica Volubi 79dbac2a8e Update Luminol 2025-09-04 17:30:46 +08:00
Helvetica Volubi 4d3e3ac832 feat: add function command unsafe support 2025-09-04 02:19:09 +08:00
Helvetica Volubi d985a9ca33 fix: fixes #55 2025-09-01 22:16:41 +08:00
Helvetica Volubi 192ef93833 upload license 2025-08-31 00:04:58 +08:00
Bacteriawa b61d6cf1ac Add Fakeplayer 2025-08-30 23:44:05 +08:00
Helvetica Volubi 1c4dea41c8 fix: fix servux's MobCapData build & send
rebuild in folia environment
2025-08-30 03:50:31 +08:00
Helvetica Volubi 4740744adb Update upstream(Luminol) 2025-08-30 01:23:09 +08:00
Bacteriawa 434694f6a1 [ci skip]update readme 2025-08-29 21:13:17 +08:00
Bacteriawa e584866cf5 [ci skip]update readme 2025-08-29 21:11:05 +08:00
Helvetica Volubi 1cff183f26 [ci skip]fix up 2025-08-27 23:45:26 +08:00
Helvetica Volubi a3af47db18 Update upstream to fix a config bug 2025-08-27 22:36:54 +08:00
Helvetica Volubi b830c66be7 Update upstream(Luminol) & rename config names 2025-08-27 00:22:52 +08:00
Helvetica Volubi fc50c2ac44 Update gradle.properties 2025-08-26 14:08:25 +08:00
Helvetica Volubi f81cee2454 feat: add a config to revert MC-159283 2025-08-26 03:04:39 +08:00
Helvetica Volubi 049716a64c Update upstream(Luminol) 2025-08-25 22:31:49 +08:00
dependabot[bot] c7c37c5a95 build(deps): bump actions/setup-java from 4 to 5 (#52)
Bumps [actions/setup-java](https://github.com/actions/setup-java) from 4 to 5.
- [Release notes](https://github.com/actions/setup-java/releases)
- [Commits](https://github.com/actions/setup-java/compare/v4...v5)

---
updated-dependencies:
- dependency-name: actions/setup-java
  dependency-version: '5'
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-08-25 21:32:02 +08:00
Helvetica Volubi 4dea005c0d Update gradle.properties 2025-08-24 22:07:20 +08:00
Helvetica Volubi 074c5c9553 Update upstream(Luminol-lithium-sleeping-block-entity) 2025-08-24 03:55:44 +08:00
Helvetica Volubi f79cc86332 Update upstream(Luminol-lithium-sleeping-block-entity) 2025-08-22 20:55:59 +08:00
Helvetica Volubi e880fdecfa Update upstream(Luminol-lithium-sleeping-block-entity) 2025-08-22 02:18:26 +08:00
xiaoxijun dda45b510a update maven repo 2025-08-20 03:51:09 +08:00
Bacteriawa 6fec3a73ac update maven repo 2025-08-20 02:59:17 +08:00
Helvetica Volubi d22d5fdf5a Update upstream(Luminol-lithium-sleeping-block-entity) 2025-08-19 07:11:56 +08:00
Helvetica Volubi fcb90d468e Update upstream(Luminol-lithium-sleeping-block-entity) 2025-08-18 23:18:53 +08:00
dependabot[bot] 1094716d9d [ci skip]build(deps): bump actions/checkout from 4 to 5 (#49)
Bumps [actions/checkout](https://github.com/actions/checkout) from 4 to 5.
- [Release notes](https://github.com/actions/checkout/releases)
- [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md)
- [Commits](https://github.com/actions/checkout/compare/v4...v5)

---
updated-dependencies:
- dependency-name: actions/checkout
  dependency-version: '5'
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-08-18 19:22:24 +08:00
Helvetica Volubi 3d57a67913 Update upstream 2025-08-17 22:25:14 +08:00
Helvetica Volubi ecc514b340 Update upstream(Luminol-lithium-sleeping-block-entity) 2025-08-16 02:51:48 +08:00
Helvetica Volubi 75baf3dfaf Update some patches from Leaves 2025-08-15 03:21:36 +08:00
Helvetica Volubi 4ed8600511 Update upstream(Luminol-lithium-sleeping-block-entity) 2025-08-13 22:27:33 +08:00
Helvetica Volubi 0160aac4c6 Update upstream (Luminol) 2025-08-11 13:35:13 +08:00
Helvetica Volubi cbed24020a Update upstream (Luminol) 2025-08-09 02:37:40 +08:00
Helvetica Volubi 682029a451 fix: fix some bugs
fix crash in contain view (#44)

fix tps and mspt send in servux
2025-08-08 00:59:22 +08:00
Helvetica Volubi f3334ae6ec Update Upstream(Luminol) 2025-08-04 20:00:05 +08:00
Helvetica Volubi df0e0eb82b [ci skip]add missing license 2025-08-04 18:01:47 +08:00
Helvetica Volubi d09469e84b servux protocol & unapply old replaceable by mushrooms [(#38)(#40)](#43)
* feat: add a config to allow to disable some check for operator

* feat: leaves servux protocol

* fix: upload missing files

* Update Upstream(Luminol)

* fix: upload missing files - 2

* fix: reuse tickCount

* fix: fix

* fix: adjust folia

* add leaves license

* add infinite support
2025-08-04 15:19:09 +08:00
Helvetica Volubi a5e56ff2ca feat: add a config to allow to disable some check for operator 2025-08-03 16:55:38 +08:00
Helvetica Volubi e38108c99b fix: temporarily fix endermite spawn position 2025-08-02 12:44:30 +08:00
Helvetica Volubi ab31ebc674 Update Upstream(Luminol) 2025-08-01 13:40:00 +08:00
Helvetica Volubi 87ba8b9746 Update Upstream(Luminol) 2025-07-30 21:55:01 +08:00
Helvetica Volubi e71b36e331 [ci skip]docs: refactor format 2025-07-30 16:18:31 +08:00
Helvetica Volubi cfb1ad88f4 Update Upstream(Luminol) 2025-07-28 23:03:43 +08:00
Helvetica Volubi b40276f037 feat: use new limit of 524288 instead of 16384 2025-07-28 17:25:16 +08:00
Helvetica Volubi e1b3a14ece feat: Leaves Vanilla hopper 2025-07-28 01:11:40 +08:00
Helvetica Volubi c257ebd19c fix: fix redstone shear wrench
origin: https://github.com/LeavesMC/Leaves/pull/621
2025-07-27 18:37:33 +08:00
Helvetica Volubi 52b6803d68 feat: leaves disable packet limit 2025-07-27 18:19:20 +08:00
Helvetica Volubi 71f83be588 fix: fix infinite trade 2025-07-27 00:38:31 +08:00
Helvetica Volubi 5733cc4e35 fix: fix a config name error 2025-07-26 18:59:50 +08:00
Helvetica Volubi a8147bb171 Fix quick craft 2025-07-26 13:41:28 +08:00
Helvetica Volubi 17f0269ee6 Update Upstream(Luminol) 2025-07-26 13:31:38 +08:00
Helvetica Volubi f574f277eb feat: use leaves-ver Item overstack util instead of Stackable ShulkerBoxes 2025-07-26 01:20:32 +08:00
237 changed files with 21805 additions and 11116 deletions
+2
View File
@@ -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 中。
+13 -11
View File
@@ -16,10 +16,10 @@ 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: 21
@@ -39,14 +39,6 @@ jobs:
- name: CreateMojmapPaperclipJar
run: ./gradlew --refresh-dependencies createMojmapPaperclipJar
- name: Publish to repo
if: github.event_name != 'pull_request' && 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: Upload Artifact
uses: actions/upload-artifact@v4
with:
@@ -57,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 }}
@@ -66,6 +66,8 @@ jobs:
body: |
📦Version: `${{ env.mcversion }}` | Commit ${{ env.commit_id }} [![download](https://img.shields.io/github/downloads/LuminolMC/${{ env.project_id }}/${{ env.tag }}/total?color=red&style=flat-square)](https://github.com/LuminolMC/${{ env.project_id }}/releases/download/${{ env.tag }}/${{ env.jar }})
This release is automatically compiled by GitHub Actions
### Branch Info
${{ github.ref_name }}
### Commit Message
${{ env.commit_msg }}
artifacts: ${{ env.jar_dir }}
+1 -1
View File
@@ -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.
+101 -37
View File
@@ -1,54 +1,78 @@
<img src="public/image/lophine/lophine3.png" alt="Logo" align="right" width="250">
<div align="center">
<img src="./public/image/lophine/lophine3.png" alt="Lophine Logo" width="300">
# Lophine
*Lophine 是一个基于Luminol的分支,具有许多有用的优化和可配置的原版特性,目标是在Folia上实现更多生电的内容(请注意,完整生电请使用Fabric)*
![Created At](https://img.shields.io/github/created-at/LuminolMC/Lophine?style=for-the-badge&color=blue)
[![License](https://img.shields.io/github/license/LuminolMC/Lophine?style=for-the-badge&color=green)](LICENSE.md)
[![Issues](https://img.shields.io/github/issues/LuminolMC/Lophine?style=for-the-badge&color=orange)](https://github.com/LuminolMC/Lophine/issues)
![Commit Activity](https://img.shields.io/github/commit-activity/w/LuminolMC/Lophine?style=for-the-badge&color=purple)
![CodeFactor Grade](https://img.shields.io/codefactor/grade/github/LuminolMC/Lophine?style=for-the-badge&color=yellow)
![GitHub all releases](https://img.shields.io/github/downloads/LuminolMC/Lophine/total?style=for-the-badge&color=red)
![Repo contributors](https://img.shields.io/github/contributors/LuminolMC/Lophine?style=for-the-badge&color=brightgreen)
[English](./README_EN.md) | **中文**
</div>
# Lophine
---
<h4>Lophine 是一个基于Luminol的分支,具有许多有用的优化和可配置的原版特性,目标是在Folia上实现更多生电的实现</h4>
## ✨ 核心特性
[![License](https://img.shields.io/github/license/LuminolMC/Lophine?style=flat-square)](LICENSE.md)
[![Issues](https://img.shields.io/github/issues/LuminolMC/Lophine?style=flat-square)](https://github.com/LuminolMC/Lophine/issues)
![Commit Activity](https://img.shields.io/github/commit-activity/w/LuminolMC/Lophine?style=flat-square)
![CodeFactor Grade](https://img.shields.io/codefactor/grade/github/LuminolMC/Lophine?style=flat-square)
![GitHub all releases](https://img.shields.io/github/downloads/LuminolMC/Lophine/total?style=flat-square)
- 🔧 **可配置的原版特性** - 灵活调整游戏机制以适应不同服务器需求
- 📊 **Tpsbar 支持** - 实时显示服务器 TPS 状态
- 🐛 **Folia Bug 修复** - 针对 Folia 已知问题的专项修复
- 💾 **多存档格式支持** - 支持 linear 和 b_linearlinear 重新实现)存档格式
- 🔬 **生电功能增强** - 在 Folia 上实现更多生电内容(完整生电请使用 Fabric)
- 🛠️ **更多实用功能** - 持续添加有用的服务器功能
[English](./README_EN.md) | **中文**
## 📥 下载
## 特性
- 支持部分Bukkit插件
- 可配置的原版特性
- Tpsbar 支持
- 对单线程区域性能的优化
- 更多有用的功能
### 稳定版本
所有发布版本都可以在 [Releases](https://github.com/LuminolMC/Lophine/releases) 页面找到。
## 下载
任何版本都可以在 [Release](https://github.com/LuminolMC/Lophine/releases), 中找到,你也可以通过[以下步骤](./README.md#构建)自己构建。
### 开发版本
如果您想体验最新功能,可以通过以下步骤自行构建。
## 构建
要构建一个paperclip jar,你需要运行以下命令。你可以在lophine-server/build/libs中找到jar(注意:需要`JDK21`
```shell
### 构建步骤
```bash
# 克隆项目
git clone https://github.com/LuminolMC/Lophine.git
cd Lophine
# 应用补丁并构建 Paperclip JAR
./gradlew applyAllPatches && ./gradlew createMojmapPaperclipJar
```
## 使用API
使用 Gradle:
构建完成后,您可以在 `lophine-server/build/libs` 目录中找到生成的 JAR 文件。
## 🔌 API 使用
### Gradle 配置
```kotlin
repositories {
maven {
url = "https://repo.menthamc.com/repository/maven-public/"
url = "https://repo.menthamc.org/repository/maven-public/"
}
}
dependencies {
compileOnly("me.earthme.luminol:luminol-api:$VERSION")
}
```
```
使用 Maven
### Maven 配置
```xml
<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>
@@ -61,29 +85,65 @@ dependencies {
</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)
### 加入我们的社区
- **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)
- 📋 [提交 Issue](https://github.com/LuminolMC/Lophine/issues)
- 💬 [GitHub Discussions](https://github.com/LuminolMC/Lophine/discussions)
- 📖 [项目文档](./docs/)
## 🐛 问题反馈
当您遇到任何问题时,请向我们提问,我们将尽力解决。请记得:
- 📝 **清楚描述问题** - 详细说明问题的具体表现
- 📋 **提供完整日志** - 包含错误日志和相关配置信息
- 🔍 **环境信息** - 说明服务器版本、插件列表等环境详情
- 🔄 **复现步骤** - 如果可能,请提供问题复现的具体步骤
## 🤝 贡献代码
我们欢迎社区贡献!详细的贡献指南请查看:
- 📖 [贡献指南 (中文)](./docs/CONTRIBUTING.md)
- 📖 [Contributing Guide (English)](./docs/CONTRIBUTING_EN.md)
## 📊 项目统计
### BStats 数据
## BStats
![bStats](https://bstats.org/signatures/server-implementation/Lophine.svg "bStats")
## 特别感谢
<b>感谢[LegacyLands](https://github.com/LegacyLands)对本项目的赞助,如果你想开发一些跨folia/非folia平台的插件,[legacy-lands-library](https://github.com/LegacyLands/legacy-lands-library/)将会是个不错的lib</b>
## 🎉 特别感谢
![legacy-lands-logo](public/image/legacy-lands-logo.png)
### 项目赞助商
<div align="center">
<b>感谢 <a href="https://github.com/LegacyLands">LegacyLands</a> 对本项目的赞助</b>
<br>
<i>如果你想开发跨 Folia/非 Folia 平台的插件,<a href="https://github.com/LegacyLands/legacy-lands-library/">legacy-lands-library</a> 将会是个不错的选择</i>
<br><br>
<img src="public/image/legacy-lands-logo.png" alt="LegacyLands Logo" width="200">
</div>
---
## ⭐ 请给我们一个 Star
## 请给我们一个 ⭐Star
> 你的每一个免费的 ⭐Star 就是我们每一个前进的动力。
### Star 历史
<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" />
@@ -91,3 +151,7 @@ dependencies {
<img alt="Star历史表" src="https://api.star-history.com/svg?repos=LuminolMC/Luminol%2CLuminolMC/LightingLuminol%2CLuminolMC/Lophine&type=Date" />
</picture>
</a>
<div align="center">
<b>如果这个项目对您有帮助,请不要忘记给我们一个 ⭐Star!</b>
</div>
+112 -44
View File
@@ -1,86 +1,150 @@
<img src="public/image/lophine/lophine3.png" alt="Logo" align="right" width="250">
<div align="center">
<img src="./public/image/lophine/lophine3.png" alt="Lophine Logo" width="300">
# Lophine
*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)*
![Created At](https://img.shields.io/github/created-at/LuminolMC/Lophine?style=for-the-badge&color=blue)
[![License](https://img.shields.io/github/license/LuminolMC/Lophine?style=for-the-badge&color=green)](LICENSE.md)
[![Issues](https://img.shields.io/github/issues/LuminolMC/Lophine?style=for-the-badge&color=orange)](https://github.com/LuminolMC/Lophine/issues)
![Commit Activity](https://img.shields.io/github/commit-activity/w/LuminolMC/Lophine?style=for-the-badge&color=purple)
![CodeFactor Grade](https://img.shields.io/codefactor/grade/github/LuminolMC/Lophine?style=for-the-badge&color=yellow)
![GitHub all releases](https://img.shields.io/github/downloads/LuminolMC/Lophine/total?style=for-the-badge&color=red)
![Repo contributors](https://img.shields.io/github/contributors/LuminolMC/Lophine?style=for-the-badge&color=brightgreen)
**English** | [中文](./README.md)
</div>
# 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>
## ✨ Core Features
[![License](https://img.shields.io/github/license/LuminolMC/Lophine?style=flat-square)](LICENSE.md)
[![Issues](https://img.shields.io/github/issues/LuminolMC/Lophine?style=flat-square)](https://github.com/LuminolMC/Lophine/issues)
![Commit Activity](https://img.shields.io/github/commit-activity/w/LuminolMC/Lophine?style=flat-square)
![CodeFactor Grade](https://img.shields.io/codefactor/grade/github/LuminolMC/Lophine?style=flat-square)
![GitHub all releases](https://img.shields.io/github/downloads/LuminolMC/Lophine/total?style=flat-square)
- 🔧 **Configurable Vanilla Features** - Flexibly adjust game mechanics to suit different server needs
- 📊 **Tpsbar Support** - Real-time TPS status display
- 🐛 **Folia Bug Fixes** - Targeted fixes for known Folia issues
- 💾 **Multiple World Format Support** - Support for linear and b_linear (linear reimplementation) world formats
- 🔬 **Redstone Enhancement** - More redstone functionality on Folia (use Fabric for complete redstone features)
- 🛠️ **More Useful Functions** - Continuously adding useful server features
**English** | [中文](./README.md)
## 📥 Download
## Features
- Supported some Bukkit plugin
- Configurable vanilla features
- Tpsbar support
- Useful optimizations to improve the performance of single threaded region
- More functions
### Stable Releases
All release versions can be found on the [Releases](https://github.com/LuminolMC/Lophine/releases) page.
## 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).
### Development Builds
If you want to experience the latest features, you can build it yourself following the steps below.
## 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
### Build Steps
```bash
# Clone the project
git clone https://github.com/LuminolMC/Lophine.git
cd Lophine
# Apply patches and build Paperclip JAR
./gradlew applyAllPatches && ./gradlew createMojmapPaperclipJar
```
## Using API
For gradle:
After building, you can find the generated JAR file in the `lophine-server/build/libs` directory.
## 🔌 API Usage
### Gradle Configuration
```kotlin
repositories {
maven {
url = "https://repo.menthamc.com/repository/maven-public/"
url = "https://repo.menthamc.org/repository/maven-public/"
}
}
dependencies {
compileOnly("me.earthme.luminol:luminol-api:$VERSION")
}
```
```
For maven
### Maven Configuration
```xml
<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>
<dependencies>
<dependency>
<groupId>me.earthme.luminol</groupId>
<artifactId>luminol-api</artifactId>
<version>$VERSION</version>
</dependency>
<dependency>
<groupId>me.earthme.luminol</groupId>
<artifactId>luminol-api</artifactId>
<version>$VERSION</version>
<scope>provided</scope>
</dependency>
</dependencies>
```
## Contact
> If you are interested in this project or have any issue, feel free to ask us.
## 💬 Community & Support
**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)
> If you're interested in this project or have any questions, feel free to ask us.
## 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>
### Join Our Community
## Pull Requests
See [Contributing](./docs/CONTRIBUTING_EN.md)
- **QQ Group**: [1015048616](http://qm.qq.com/cgi-bin/qm/qr?_wv=1027&k=QML5kIVsniPi1PlZvnjHQT_02EHsZ5Jc&authKey=%2FTCJsZC7JFQ9sxAroPCKuYnlV57Z5fyqp36ewXZk3Sn4iJ9p4MB1JKdc%2FFcX3HOM&noverify=0&group_code=1015048616)
- **QQ Channel**: [Join Here](https://pd.qq.com/s/eq9krf9j)
- **Telegram**: [Join Here](https://t.me/LuminolMinecraft)
- **Discord**: [Join Here](https://discord.gg/Qd7m3V6eDx)
### Get Help
- 📋 [Submit Issues](https://github.com/LuminolMC/Lophine/issues)
- 💬 [GitHub Discussions](https://github.com/LuminolMC/Lophine/discussions)
- 📖 [Project Documentation](./docs/)
## 🐛 Bug Reports
When you encounter any issues, please ask us and we'll do our best to resolve them. Please remember to:
- 📝 **Describe the problem clearly** - Provide detailed information about the specific issue
- 📋 **Provide complete logs** - Include error logs and relevant configuration information
- 🔍 **Environment details** - Specify server version, plugin list, and other environment details
- 🔄 **Reproduction steps** - If possible, provide specific steps to reproduce the issue
## 🤝 Contributing
We welcome community contributions! For detailed contribution guidelines, please see:
- 📖 [Contributing Guide (English)](./docs/CONTRIBUTING_EN.md)
- 📖 [贡献指南 (中文)](./docs/CONTRIBUTING.md)
## 📊 Project Statistics
### BStats Data
## BStats
![bStats](https://bstats.org/signatures/server-implementation/Lophine.svg "bStats")
## 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
## 🎉 Special Thanks
![legacy-lands-logo](public/image/legacy-lands-logo.png)
### Project Sponsors
<div align="center">
<b>Thanks to <a href="https://github.com/LegacyLands">LegacyLands</a> for sponsoring this project</b>
<br>
<i>If you want to develop cross-Folia/non-Folia platform plugins, <a href="https://github.com/LegacyLands/legacy-lands-library/">legacy-lands-library</a> would be a great choice</i>
<br><br>
<img src="public/image/legacy-lands-logo.png" alt="LegacyLands Logo" width="200">
</div>
---
## ⭐ Give Us a Star!
> Every free ⭐Star you give is the motivation for our every step forward.
### Star History
## 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" />
@@ -88,3 +152,7 @@ Thanks [LegacyLands](https://github.com/LegacyLands) for sponsoring this project
<img alt="Star History Chart" src="https://api.star-history.com/svg?repos=LuminolMC/Luminol%2CLuminolMC/LightingLuminol%2CLuminolMC/Lophine&type=Date" />
</picture>
</a>
<div align="center">
<b>If this project helps you, please don't forget to give us a ⭐Star!</b>
</div>
+2 -2
View File
@@ -41,7 +41,7 @@ paperweight {
}
val paperMavenPublicUrl = "https://repo.papermc.io/repository/maven-public/"
val menthaMavenPublicUrl = "https://repo.menthamc.com/repository/maven-public/";
val menthaMavenPublicUrl = "https://repo.menthamc.org/repository/maven-public/";
subprojects {
apply(plugin = "java-library")
@@ -88,7 +88,7 @@ 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("PRIVATE_MAVEN_REPO_USERNAME")
+13 -10
View File
@@ -1,5 +1,5 @@
为Lophine贡献代码
===========
===============
[English](./CONTRIBUTING_EN.md) | **中文**
@@ -45,17 +45,20 @@ Lophine 使用和 Folia 一样的补丁系统,并为了针对不同部分的
4. 进入 仓库根目录下的 `*-api``*-server` 文件夹进行修改。
以下为对上述各个文件夹的简单描述,详细描述可以参考[这里](https://github.com/Toffikk/paperweight-examples/blob/18241979c88068d5b061d95ad69c98ecb201c246/README.md)
1. API部分
- `lophine-api` :对新增API的修改
- `luminol-api` :对luminol-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原版服务器的修改和新增文件应当在此文件夹下进行
- `luminol-server` :对luminol-server的修改应当在此文件夹下进行
- `folia-server` :对folia-Server的修改应当在此文件夹下进行
- `paper-server` :对于paper对服务器逻辑的修改应当在此文件夹下进行
- `lophine-server` :对Minecraft原版服务器的修改和新增文件应当在此文件夹下进行
- `luminol-server` :对luminol-server的修改应当在此文件夹下进行
- `folia-server` :对folia-Server的修改应当在此文件夹下进行
- `paper-server` :对于paper对服务器逻辑的修改应当在此文件夹下进行
顺便一提,仓库根目录下的 `*-api``*-server` 并不是正常的 git 仓库:
@@ -82,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` 来修改已被修改的补丁;
+11 -9
View File
@@ -1,5 +1,5 @@
Contributing to Lophine
===========
=======================
**English** | [中文](./CONTRIBUTING.md)
@@ -50,16 +50,18 @@ If you have forked the main repository, then you should follow the steps below:
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` : 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
- `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` : 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
- `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.
+1 -1
View File
@@ -4,7 +4,7 @@ mcVersion=1.21.8
release=2
# 0 for skip release, 1 for pre-release, 2 for release
luminolRef=5d95e9675c4cc0d74d791c09f22c0b990ad55bcf
luminolRef=e7176d188b9e4d3b1f258d56cda91e2d008282f4
org.gradle.configuration-cache=true
org.gradle.caching=true
+71 -70
View File
@@ -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
View File
@@ -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
@@ -0,0 +1,44 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Bacteriawa <A3167717663@hotmail.com>
Date: Sat, 30 Aug 2025 16:51:26 +0800
Subject: [PATCH] Add fakeplayer api
diff --git a/src/main/java/org/bukkit/Bukkit.java b/src/main/java/org/bukkit/Bukkit.java
index 85b778980e762835ac278528087e91b2e3effb43..89f00f4b6ff64305fa44ebb71f22a113c1296d26 100644
--- a/src/main/java/org/bukkit/Bukkit.java
+++ b/src/main/java/org/bukkit/Bukkit.java
@@ -3052,4 +3052,15 @@ public final class Bukkit {
public static void restart() {
server.restart();
}
+
+ // Leaves start - Bot API
+ /**
+ * Returns a bot manager.
+ *
+ * @return Bot Manager
+ */
+ public static @NotNull org.leavesmc.leaves.entity.bot.BotManager getBotManager() {
+ return server.getBotManager();
+ }
+ // Leaves end - Bot API
}
diff --git a/src/main/java/org/bukkit/Server.java b/src/main/java/org/bukkit/Server.java
index a0540d7de5c2536aed5231f9e67b73345817921d..c7ae463fb166750bbd0638a2ccf81753b293a381 100644
--- a/src/main/java/org/bukkit/Server.java
+++ b/src/main/java/org/bukkit/Server.java
@@ -2753,4 +2753,13 @@ public interface Server extends PluginMessageRecipient, net.kyori.adventure.audi
*/
double @Nullable [] getRegionTPS(@NotNull World world, int chunkX, int chunkZ);
// Folia end - region TPS API
+
+ // Leaves start - Bot API
+ /**
+ * Returns a bot manager.
+ *
+ * @return Bot Manager
+ */
+ @NotNull org.leavesmc.leaves.entity.bot.BotManager getBotManager();
+ // Leaves end - Bot API
}
@@ -0,0 +1,94 @@
/*
* 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.entity.bot;
import org.bukkit.entity.Player;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.leavesmc.leaves.entity.bot.action.BotAction;
import java.util.UUID;
/**
* Represents a fakeplayer
*/
public interface Bot extends Player {
/**
* Gets the fakeplayer skin
*
* @return fakeplayer skin name
*/
@Nullable String getSkinName();
/**
* Gets the fakeplayer name without prefix and suffix
*
* @return fakeplayer real name
*/
@NotNull String getRealName();
/**
* Gets the creator's UUID of the fakeplayer
*
* @return creator's UUID
*/
@Nullable UUID getCreatePlayerUUID();
/**
* Add an action to the fakeplayer
*
* @param action bot action
*/
<T extends BotAction<T>> void addAction(@NotNull T action);
/**
* Get the copy action in giving index
*
* @param index index of actions
* @return Action of that index
*/
BotAction<?> getAction(int index);
/**
* Get action size
*
* @return size
*/
int getActionSize();
/**
* Stop the action in giving index
*
* @param index index of actions
*/
void stopAction(int index);
/**
* Stop all the actions of the fakeplayer
*/
void stopAllActions();
/**
* Remove the fakeplayer
*
* @param save should save
* @return success
*/
boolean remove(boolean save);
}
@@ -0,0 +1,69 @@
/*
* 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.entity.bot;
import org.bukkit.Bukkit;
import org.bukkit.Location;
import org.bukkit.command.CommandSender;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.function.Consumer;
public interface BotCreator {
static BotCreator of(String realName, Location location) {
return Bukkit.getBotManager().botCreator(realName, location);
}
BotCreator name(String name);
BotCreator skinName(String skinName);
BotCreator skin(String[] skin);
/**
* Sets the skin of the bot using the Mojang API based on the provided skin name.
* <p>
* Need Async.
*
* @return BotCreator
*/
BotCreator mojangAPISkin();
BotCreator location(@NotNull Location location);
BotCreator creator(@Nullable CommandSender creator);
/**
* Create a bot directly
*
* @return a bot, null spawn fail
*/
@Nullable Bot spawn();
/**
* Create a bot and apply skin of player names `skinName` from MojangAPI
* just like `mojangAPISkin().spawn()`, but async
* <p>
* you can not get the bot instance instantly because get skin in on async thread
*
* @param consumer Consumer
*/
void spawnWithSkin(Consumer<Bot> consumer);
}
@@ -0,0 +1,65 @@
/*
* 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.entity.bot;
import org.bukkit.Location;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.leavesmc.leaves.entity.bot.action.BotAction;
import java.util.Collection;
import java.util.UUID;
/**
* Simple fakeplayer manager
*/
public interface BotManager {
/**
* Gets a fakeplayer object by the given uuid.
*
* @param uuid the uuid to look up
* @return a fakeplayer if one was found, null otherwise
*/
@Nullable Bot getBot(@NotNull UUID uuid);
/**
* Gets a fakeplayer object by the given name.
*
* @param name the name to look up
* @return a fakeplayer if one was found, null otherwise
*/
@Nullable Bot getBot(@NotNull String name);
/**
* Gets a view of all currently logged in fakeplayers. This view is a reused object, making some operations like Collection.size() zero-allocation.
*
* @return a view of fakeplayers.
*/
Collection<Bot> getBots();
/**
* Create a bot action by class.
*
* @param type action class
* @return a bot action instance if one was found, null otherwise
*/
<T extends BotAction<T>> T newAction(@NotNull Class<T> type);
BotCreator botCreator(@NotNull String realName, @NotNull Location location);
}
@@ -0,0 +1,29 @@
/*
* 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.entity.bot.action;
import org.bukkit.Bukkit;
/**
* Represents an action for a bot to attack entities.
*/
public interface AttackAction extends TimerBotAction<AttackAction> {
static AttackAction create() {
return Bukkit.getBotManager().newAction(AttackAction.class);
}
}
@@ -0,0 +1,102 @@
/*
* 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.entity.bot.action;
import java.util.UUID;
import java.util.function.Consumer;
/**
* Represents an action that can be performed by a Bot.
* <p>
* This interface defines the basic properties of an action (such as name and UUID)
* and lifecycle event callbacks (success, failure, stop).
*
* @param <T> the type of the callback parameter, usually a context object related to the action
*/
public interface BotAction<T> {
/**
* Gets the name of this action.
*
* @return the action name
*/
String getName();
/**
* Gets the UUID of this action.
*
* @return the UUID of the action
*/
UUID getUUID();
/**
* Sets whether this action is cancelled.
*
* @param cancel true to cancel the action, false otherwise
*/
void setCancelled(boolean cancel);
/**
* Checks whether this action has been cancelled.
*
* @return true if cancelled, false otherwise
*/
boolean isCancelled();
/**
* Sets the callback to be executed when the action fails.
*
* @param onFail the callback to execute on failure, with a parameter of type T
*/
void setOnFail(Consumer<T> onFail);
/**
* Gets the callback to be executed when the action fails.
*
* @return the failure callback
*/
Consumer<T> getOnFail();
/**
* Sets the callback to be executed when the action succeeds.
*
* @param onSuccess the callback to execute on success, with a parameter of type T
*/
void setOnSuccess(Consumer<T> onSuccess);
/**
* Gets the callback to be executed when the action succeeds.
*
* @return the success callback
*/
Consumer<T> getOnSuccess();
/**
* Sets the callback to be executed when the action is stopped.
*
* @param onStop the callback to execute on stop, with a parameter of type T
*/
void setOnStop(Consumer<T> onStop);
/**
* Gets the callback to be executed when the action is stopped.
*
* @return the stop callback
*/
Consumer<T> getOnStop();
}
@@ -0,0 +1,29 @@
/*
* 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.entity.bot.action;
import org.bukkit.Bukkit;
/**
* Represents an action for a bot to break a block.
*/
public interface BreakBlockAction extends TimerBotAction<BreakBlockAction> {
static BreakBlockAction create() {
return Bukkit.getBotManager().newAction(BreakBlockAction.class);
}
}
@@ -0,0 +1,29 @@
/*
* 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.entity.bot.action;
import org.bukkit.Bukkit;
/**
* Represents an action for a bot to drop all items in its inventory.
*/
public interface DropAction extends TimerBotAction<DropAction> {
static DropAction create() {
return Bukkit.getBotManager().newAction(DropAction.class);
}
}
@@ -0,0 +1,29 @@
/*
* 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.entity.bot.action;
import org.bukkit.Bukkit;
/**
* Represents an action for a bot to perform auto fishing.
*/
public interface FishAction extends TimerBotAction<FishAction> {
static FishAction create() {
return Bukkit.getBotManager().newAction(FishAction.class);
}
}
@@ -0,0 +1,29 @@
/*
* 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.entity.bot.action;
import org.bukkit.Bukkit;
/**
* Represents an action for a bot to perform a jump.
*/
public interface JumpAction extends TimerBotAction<JumpAction> {
static JumpAction create() {
return Bukkit.getBotManager().newAction(JumpAction.class);
}
}
@@ -0,0 +1,71 @@
/*
* 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.entity.bot.action;
import org.bukkit.Bukkit;
import org.bukkit.entity.Player;
import org.bukkit.util.Vector;
/**
* Represents an action for a bot to look at a specific position or player.
* <p>
* If both a position and a player are set, the player target takes precedence.
*/
public interface LookAction extends BotAction<LookAction> {
/**
* Sets the position in the world for the bot to look at.
* <p>
* If a player target is set via {@link #setTarget(Player)}, the bot will look at the player instead of this position.
*
* @param pos the {@link Vector} representing the position to look at
* @return this {@code LookAction} instance for method chaining
*/
LookAction setPos(Vector pos);
/**
* Gets the position in the world that the bot is set to look at.
* <p>
* If a player target is set, this value may be ignored.
*
* @return the {@link Vector} position to look at, or {@code null} if not set
*/
Vector getPos();
/**
* Sets the player for the bot to look at.
* <p>
* When a player is set as the target, the bot will continuously look at the player's current position,
* overriding any position set by {@link #setPos(Vector)}.
*
* @param player the {@link Player} to look at, or {@code null} to clear the target
* @return this {@code LookAction} instance for method chaining
*/
LookAction setTarget(Player player);
/**
* Gets the player that the bot is set to look at.
*
* @return the {@link Player} target, or {@code null} if not set
*/
Player getTarget();
static LookAction create() {
return Bukkit.getBotManager().newAction(LookAction.class);
}
}
@@ -0,0 +1,29 @@
/*
* 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.entity.bot.action;
import org.bukkit.Bukkit;
/**
* Represents an action for a bot to mount to nearby vehicles.
*/
public interface MountAction extends BotAction<MountAction> {
static MountAction create() {
return Bukkit.getBotManager().newAction(MountAction.class);
}
}
@@ -0,0 +1,61 @@
/*
* 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.entity.bot.action;
import org.bukkit.Bukkit;
/**
* Represents an action for a bot to move to a specific direction.
*/
public interface MoveAction extends StateBotAction<MoveAction> {
/**
* Gets the direction of the move action.
*
* @return the direction of the move action
*/
MoveDirection getDirection();
/**
* Sets the direction of the move action.
*
* @param direction the direction to set
* @return this action instance
*/
MoveAction setDirection(MoveDirection direction);
/**
* Represents possible movement directions for the bot.
*/
enum MoveDirection {
FORWARD("forward"),
BACKWARD("backward"),
LEFT("left"),
RIGHT("right");
public final String name;
MoveDirection(String name) {
this.name = name;
}
}
static MoveAction create() {
return Bukkit.getBotManager().newAction(MoveAction.class);
}
}
@@ -0,0 +1,60 @@
/*
* 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.entity.bot.action;
import org.bukkit.Bukkit;
/**
* Represents an action for a bot to rotate to a specific yaw and pitch.
*/
public interface RotationAction extends BotAction<RotationAction> {
/**
* Sets the yaw of the rotation.
*
* @param yaw the yaw to set
* @return this action instance
*/
RotationAction setYaw(float yaw);
/**
* Sets the pitch of the rotation.
*
* @param pitch the pitch to set
* @return this action instance
*/
RotationAction setPitch(float pitch);
/**
* Gets the yaw of the rotation.
*
* @return the yaw
*/
float getYaw();
/**
* Gets the pitch of the rotation.
*
* @return the pitch
*/
float getPitch();
static RotationAction create() {
return Bukkit.getBotManager().newAction(RotationAction.class);
}
}
@@ -0,0 +1,29 @@
/*
* 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.entity.bot.action;
import org.bukkit.Bukkit;
/**
* Represents an action for a bot to sneak.
*/
public interface SneakAction extends StateBotAction<SneakAction> {
static SneakAction create() {
return Bukkit.getBotManager().newAction(SneakAction.class);
}
}
@@ -0,0 +1,27 @@
/*
* 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.entity.bot.action;
/**
* Represents a type of bot action that places the bot in a specific state while the action is active.
* When the action is stopped, the associated state is removed from the bot.
*
* @param <E> The type of entity that this action operates on.
*/
public interface StateBotAction<E> extends BotAction<E> {
}
@@ -0,0 +1,29 @@
/*
* 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.entity.bot.action;
import org.bukkit.Bukkit;
/**
* Represents an action for a bot to swap items between the main hand and off-hand.
*/
public interface SwapAction extends BotAction<SwapAction> {
static SwapAction create() {
return Bukkit.getBotManager().newAction(SwapAction.class);
}
}
@@ -0,0 +1,29 @@
/*
* 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.entity.bot.action;
import org.bukkit.Bukkit;
/**
* Represents an action for a bot to float up in water.
*/
public interface SwimAction extends StateBotAction<SwimAction> {
static SwimAction create() {
return Bukkit.getBotManager().newAction(SwimAction.class);
}
}
@@ -0,0 +1,85 @@
/*
* 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.entity.bot.action;
/**
* Represents a scheduled bot task that runs periodically.
* <p>
* TimerBotAction allows configuration of start delay, execution interval, and the number of executions.
* It is intended for bot actions that need to be triggered at regular intervals.
*
* @param <E> the type of entity this action operates on
*/
public interface TimerBotAction<E> extends BotAction<E> {
/**
* Sets the delay in ticks before the task starts for the first time.
*
* @param delayTick the number of ticks to delay before the first execution
*/
void setStartDelayTick(int delayTick);
/**
* Gets the delay in ticks before the task starts for the first time.
*
* @return the number of ticks to delay before the first execution
*/
int getStartDelayTick();
/**
* Sets the interval in ticks between each execution of the task.
*
* @param intervalTick the number of ticks between executions
*/
void setDoIntervalTick(int intervalTick);
/**
* Gets the interval in ticks between each execution of the task.
*
* @return the number of ticks between executions
*/
int getDoIntervalTick();
/**
* Sets the total number of times the task should be executed.
*
* @param doNumber the total number of executions
*/
void setDoNumber(int doNumber);
/**
* Gets the total number of times the task should be executed.
*
* @return the total number of executions
*/
int getDoNumber();
/**
* Gets the number of ticks remaining until the next execution.
*
* @return the number of ticks until the next execution
*/
int getTickToNext();
/**
* Gets the number of executions remaining for this task.
*
* @return the number of executions remaining
*/
int getDoNumberRemaining();
}
@@ -0,0 +1,46 @@
/*
* 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.entity.bot.action;
import org.bukkit.Bukkit;
/**
* Represents an action where a bot only uses an item, without using it on blocks or entities.
*/
public interface UseItemAction extends TimerBotAction<UseItemAction> {
/**
* Gets the equivalent right-click hold duration timeout in ticks.
* Default is -1, which means no timeout.
*
* @return the equivalent right-click hold duration timeout
*/
int getUseTickTimeout();
/**
* Sets the equivalent right-click hold duration timeout in ticks.
*
* @param timeout the equivalent right-click hold duration timeout
* @return this action instance
*/
UseItemAction setUseTickTimeout(int timeout);
static UseItemAction create() {
return Bukkit.getBotManager().newAction(UseItemAction.class);
}
}
@@ -0,0 +1,46 @@
/*
* 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.entity.bot.action;
import org.bukkit.Bukkit;
/**
* Represents an action where a bot uses an item, fully simulating the effect of a player right-clicking.
*/
public interface UseItemAutoAction extends TimerBotAction<UseItemAutoAction> {
/**
* Gets the equivalent right-click hold duration timeout in ticks.
* Default is -1, which means no timeout.
*
* @return the equivalent right-click hold duration timeout
*/
int getUseTickTimeout();
/**
* Sets the equivalent right-click hold duration timeout in ticks.
*
* @param timeout the equivalent right-click hold duration timeout
* @return this action instance
*/
UseItemAutoAction setUseTickTimeout(int timeout);
static UseItemAutoAction create() {
return Bukkit.getBotManager().newAction(UseItemAutoAction.class);
}
}
@@ -0,0 +1,46 @@
/*
* 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.entity.bot.action;
import org.bukkit.Bukkit;
/**
* Represents an action where a bot only uses the item in its offhand, without using it on blocks or entities.
*/
public interface UseItemOffhandAction extends TimerBotAction<UseItemOffhandAction> {
/**
* Gets the equivalent right-click hold duration timeout in ticks.
* Default is -1, which means no timeout.
*
* @return the equivalent right-click hold duration timeout
*/
int getUseTickTimeout();
/**
* Sets the equivalent right-click hold duration timeout in ticks.
*
* @param timeout the equivalent right-click hold duration timeout
* @return this action instance
*/
UseItemOffhandAction setUseTickTimeout(int timeout);
static UseItemOffhandAction create() {
return Bukkit.getBotManager().newAction(UseItemOffhandAction.class);
}
}
@@ -0,0 +1,46 @@
/*
* 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.entity.bot.action;
import org.bukkit.Bukkit;
/**
* Represents an action where a bot uses an item on a block.
*/
public interface UseItemOnAction extends TimerBotAction<UseItemOnAction> {
/**
* Gets the equivalent right-click hold duration timeout in ticks.
* Default is -1, which means no timeout.
*
* @return the equivalent right-click hold duration timeout
*/
int getUseTickTimeout();
/**
* Sets the equivalent right-click hold duration timeout in ticks.
*
* @param timeout the equivalent right-click hold duration timeout
* @return this action instance
*/
UseItemOnAction setUseTickTimeout(int timeout);
static UseItemOnAction create() {
return Bukkit.getBotManager().newAction(UseItemOnAction.class);
}
}
@@ -0,0 +1,46 @@
/*
* 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.entity.bot.action;
import org.bukkit.Bukkit;
/**
* Represents an action where a bot uses an item in its offhand on a block.
*/
public interface UseItemOnOffhandAction extends TimerBotAction<UseItemOnOffhandAction> {
/**
* Gets the equivalent right-click hold duration timeout in ticks.
* Default is -1, which means no timeout.
*
* @return the equivalent right-click hold duration timeout
*/
int getUseTickTimeout();
/**
* Sets the equivalent right-click hold duration timeout in ticks.
*
* @param timeout the equivalent right-click hold duration timeout
* @return this action instance
*/
UseItemOnOffhandAction setUseTickTimeout(int timeout);
static UseItemOnOffhandAction create() {
return Bukkit.getBotManager().newAction(UseItemOnOffhandAction.class);
}
}
@@ -0,0 +1,46 @@
/*
* 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.entity.bot.action;
import org.bukkit.Bukkit;
/**
* Represents an action where a bot uses an item to an entity.
*/
public interface UseItemToAction extends TimerBotAction<UseItemToAction> {
/**
* Gets the equivalent right-click hold duration timeout in ticks.
* Default is -1, which means no timeout.
*
* @return the equivalent right-click hold duration timeout
*/
int getUseTickTimeout();
/**
* Sets the equivalent right-click hold duration timeout in ticks.
*
* @param timeout the equivalent right-click hold duration timeout
* @return this action instance
*/
UseItemToAction setUseTickTimeout(int timeout);
static UseItemToAction create() {
return Bukkit.getBotManager().newAction(UseItemToAction.class);
}
}
@@ -0,0 +1,46 @@
/*
* 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.entity.bot.action;
import org.bukkit.Bukkit;
/**
* Represents an action where a bot uses an item in its offhand to an entity.
*/
public interface UseItemToOffhandAction extends TimerBotAction<UseItemToOffhandAction> {
/**
* Gets the equivalent right-click hold duration timeout in ticks.
* Default is -1, which means no timeout.
*
* @return the equivalent right-click hold duration timeout
*/
int getUseTickTimeout();
/**
* Sets the equivalent right-click hold duration timeout in ticks.
*
* @param timeout the equivalent right-click hold duration timeout
* @return this action instance
*/
UseItemToOffhandAction setUseTickTimeout(int timeout);
static UseItemToOffhandAction create() {
return Bukkit.getBotManager().newAction(UseItemToOffhandAction.class);
}
}
@@ -0,0 +1,44 @@
/*
* 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.event.bot;
import org.jetbrains.annotations.NotNull;
import org.leavesmc.leaves.entity.bot.Bot;
import java.util.UUID;
public abstract class BotActionEvent extends BotEvent {
private final String actionName;
private final UUID actionUUID;
public BotActionEvent(@NotNull Bot who, String actionName, UUID actionUUID) {
super(who);
this.actionName = actionName;
this.actionUUID = actionUUID;
}
@NotNull
public String getActionName() {
return actionName;
}
public UUID getActionUUID() {
return actionUUID;
}
}
@@ -0,0 +1,68 @@
/*
* 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.event.bot;
import org.bukkit.event.Cancellable;
import org.bukkit.event.HandlerList;
import org.jetbrains.annotations.NotNull;
import org.leavesmc.leaves.entity.bot.Bot;
import java.util.UUID;
public class BotActionExecuteEvent extends BotActionEvent implements Cancellable {
private static final HandlerList handlers = new HandlerList();
private Result result = Result.PASS;
public BotActionExecuteEvent(@NotNull Bot who, String actionName, UUID actionUUID) {
super(who, actionName, actionUUID);
}
public static HandlerList getHandlerList() {
return handlers;
}
@Override
public boolean isCancelled() {
return result != Result.PASS;
}
@Override
public void setCancelled(boolean cancel) {
this.result = cancel ? Result.SOFT_CANCEL : Result.PASS;
}
public void hardCancel() {
this.result = Result.HARD_CANCEL;
}
public Result getResult() {
return this.result;
}
@Override
public @NotNull HandlerList getHandlers() {
return handlers;
}
public enum Result {
PASS, SOFT_CANCEL, HARD_CANCEL
}
}
@@ -0,0 +1,64 @@
/*
* 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.event.bot;
import org.bukkit.command.CommandSender;
import org.bukkit.event.Cancellable;
import org.bukkit.event.HandlerList;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.leavesmc.leaves.entity.bot.Bot;
import java.util.UUID;
public class BotActionScheduleEvent extends BotActionEvent implements Cancellable {
private static final HandlerList handlers = new HandlerList();
private final CommandSender sender;
private boolean cancel = false;
public BotActionScheduleEvent(@NotNull Bot who, String actionName, UUID actionUUID, CommandSender sender) {
super(who, actionName, actionUUID);
this.sender = sender;
}
public static HandlerList getHandlerList() {
return handlers;
}
@Override
public boolean isCancelled() {
return cancel;
}
@Override
public void setCancelled(boolean cancel) {
this.cancel = cancel;
}
@Nullable
public CommandSender getSender() {
return sender;
}
@Override
@NotNull
public HandlerList getHandlers() {
return handlers;
}
}
@@ -0,0 +1,72 @@
/*
* 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.event.bot;
import org.bukkit.command.CommandSender;
import org.bukkit.event.Cancellable;
import org.bukkit.event.HandlerList;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.leavesmc.leaves.entity.bot.Bot;
import java.util.UUID;
public class BotActionStopEvent extends BotActionEvent implements Cancellable {
private static final HandlerList handlers = new HandlerList();
private final CommandSender sender;
private final Reason reason;
private boolean cancel = false;
public BotActionStopEvent(@NotNull Bot who, String actionName, UUID actionUUID, Reason stopReason, CommandSender sender) {
super(who, actionName, actionUUID);
this.reason = stopReason;
this.sender = sender;
}
public static HandlerList getHandlerList() {
return handlers;
}
public Reason getReason() {
return reason;
}
@Override
public @NotNull HandlerList getHandlers() {
return handlers;
}
@Override
public boolean isCancelled() {
return cancel;
}
@Override
public void setCancelled(boolean cancel) {
this.cancel = cancel;
}
@Nullable
public CommandSender getSender() {
return sender;
}
public enum Reason {
DONE, COMMAND, PLUGIN, INTERNAL
}
}
@@ -0,0 +1,70 @@
/*
* 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.event.bot;
import org.bukkit.command.CommandSender;
import org.bukkit.event.Cancellable;
import org.bukkit.event.HandlerList;
import org.jetbrains.annotations.NotNull;
import org.leavesmc.leaves.entity.bot.Bot;
public class BotConfigModifyEvent extends BotEvent implements Cancellable {
private static final HandlerList handlers = new HandlerList();
private final String configName;
private final String[] configValue;
private final CommandSender sender;
private boolean cancel;
public BotConfigModifyEvent(@NotNull Bot who, String configName, String[] configValue, CommandSender sender) {
super(who);
this.configName = configName;
this.configValue = configValue;
this.sender = sender;
}
public static HandlerList getHandlerList() {
return handlers;
}
@NotNull
public String getConfigName() {
return configName;
}
@NotNull
public String[] getConfigValue() {
return configValue;
}
@Override
public boolean isCancelled() {
return cancel;
}
@Override
public void setCancelled(boolean cancel) {
this.cancel = cancel;
}
@Override
public @NotNull HandlerList getHandlers() {
return handlers;
}
}
@@ -0,0 +1,135 @@
/*
* 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.event.bot;
import org.bukkit.Location;
import org.bukkit.command.CommandSender;
import org.bukkit.event.Cancellable;
import org.bukkit.event.Event;
import org.bukkit.event.HandlerList;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
/**
* Call when a fakeplayer created
*/
public class BotCreateEvent extends Event implements Cancellable {
private static final HandlerList handlers = new HandlerList();
private final String bot;
private final String skin;
private final CreateReason reason;
private final CommandSender creator;
private Location createLocation;
private boolean cancel = false;
public BotCreateEvent(@NotNull final String who, @NotNull final String skin, @NotNull final Location createLocation, @NotNull CreateReason reason, @Nullable CommandSender creator) {
this.bot = who;
this.skin = skin;
this.createLocation = createLocation;
this.reason = reason;
this.creator = creator;
}
@NotNull
public static HandlerList getHandlerList() {
return handlers;
}
/**
* Gets the fakeplayer name
*
* @return fakeplayer name
*/
public String getBot() {
return bot;
}
/**
* Gets the location to create the fakeplayer
*
* @return Location to create the fakeplayer
*/
@NotNull
public Location getCreateLocation() {
return createLocation;
}
/**
* Sets the location to create the fakeplayer
*
* @param createLocation location to create the fakeplayer
*/
public void setCreateLocation(@NotNull Location createLocation) {
this.createLocation = createLocation;
}
/**
* Gets the fakeplayer skin
*
* @return fakeplayer skin name
*/
@Nullable
public String getSkin() {
return skin;
}
/**
* Gets the create reason of the bot
*
* @return create reason
*/
@NotNull
public CreateReason getReason() {
return reason;
}
/**
* Gets the creator of the bot
* if the create reason is not COMMAND, the creator might be null
*
* @return An optional of creator
*/
@Nullable
public CommandSender getCreator() {
return creator;
}
@Override
public boolean isCancelled() {
return cancel;
}
@Override
public void setCancelled(boolean cancel) {
this.cancel = cancel;
}
@Override
@NotNull
public HandlerList getHandlers() {
return handlers;
}
public enum CreateReason {
COMMAND,
PLUGIN,
INTERNAL,
UNKNOWN,
}
}
@@ -0,0 +1,86 @@
/*
* 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.event.bot;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer;
import org.bukkit.event.Cancellable;
import org.bukkit.event.HandlerList;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.leavesmc.leaves.entity.bot.Bot;
public class BotDeathEvent extends BotEvent implements Cancellable {
private static final HandlerList handlers = new HandlerList();
private boolean cancel = false;
private boolean sendDeathMessage;
private Component deathMessage;
public BotDeathEvent(@NotNull Bot who, @Nullable Component deathMessage, boolean sendDeathMessage) {
super(who);
this.deathMessage = deathMessage;
this.sendDeathMessage = sendDeathMessage;
}
@NotNull
public static HandlerList getHandlerList() {
return handlers;
}
@Override
public @NotNull HandlerList getHandlers() {
return handlers;
}
public Component deathMessage() {
return deathMessage;
}
public void deathMessage(Component deathMessage) {
this.deathMessage = deathMessage;
}
@Nullable
public String getDeathMessage() {
return this.deathMessage == null ? null : LegacyComponentSerializer.legacySection().serialize(this.deathMessage);
}
public void setDeathMessage(@Nullable String deathMessage) {
this.deathMessage = deathMessage != null ? LegacyComponentSerializer.legacySection().deserialize(deathMessage) : null;
}
public boolean isSendDeathMessage() {
return sendDeathMessage;
}
public void setSendDeathMessage(boolean sendDeathMessage) {
this.sendDeathMessage = sendDeathMessage;
}
@Override
public boolean isCancelled() {
return cancel;
}
@Override
public void setCancelled(boolean cancel) {
this.cancel = cancel;
}
}
@@ -0,0 +1,49 @@
/*
* 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.event.bot;
import org.bukkit.event.Event;
import org.jetbrains.annotations.NotNull;
import org.leavesmc.leaves.entity.bot.Bot;
/**
* Represents a fakeplayer related event
*/
public abstract class BotEvent extends Event {
protected Bot bot;
public BotEvent(@NotNull final Bot who) {
bot = who;
}
public BotEvent(@NotNull final Bot who, boolean async) {
super(async);
bot = who;
}
/**
* Returns the fakeplayer involved in this event
*
* @return Fakeplayer who is involved in this event
*/
@NotNull
public final Bot getBot() {
return bot;
}
}
@@ -0,0 +1,63 @@
/*
* 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.event.bot;
import org.bukkit.entity.Player;
import org.bukkit.event.Cancellable;
import org.bukkit.event.HandlerList;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.leavesmc.leaves.entity.bot.Bot;
public class BotInventoryOpenEvent extends BotEvent implements Cancellable {
private static final HandlerList handlers = new HandlerList();
private final Player player;
private boolean cancel = false;
public BotInventoryOpenEvent(@NotNull Bot who, @Nullable Player player) {
super(who);
this.player = player;
}
@NotNull
public static HandlerList getHandlerList() {
return handlers;
}
@Override
public boolean isCancelled() {
return cancel;
}
@Override
public void setCancelled(boolean cancel) {
this.cancel = cancel;
}
@Nullable
public Player getOpenPlayer() {
return player;
}
@Override
public @NotNull HandlerList getHandlers() {
return handlers;
}
}
@@ -0,0 +1,84 @@
/*
* 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.event.bot;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer;
import org.bukkit.event.HandlerList;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.leavesmc.leaves.entity.bot.Bot;
/**
* Called when a fakeplayer joins a server
*/
public class BotJoinEvent extends BotEvent {
private static final HandlerList handlers = new HandlerList();
private Component joinMessage;
public BotJoinEvent(@NotNull final Bot who, @Nullable final Component joinMessage) {
super(who);
this.joinMessage = joinMessage;
}
public BotJoinEvent(@NotNull final Bot who, @Nullable final String joinMessage) {
super(who);
this.joinMessage = joinMessage != null ? LegacyComponentSerializer.legacySection().deserialize(joinMessage) : null;
}
@NotNull
public static HandlerList getHandlerList() {
return handlers;
}
public void joinMessage(@Nullable final Component joinMessage) {
this.joinMessage = joinMessage;
}
@Nullable
public Component joinMessage() {
return joinMessage;
}
/**
* Gets the join message to send to all online players
*
* @return string join message. Can be null
*/
@Nullable
public String getJoinMessage() {
return this.joinMessage == null ? null : LegacyComponentSerializer.legacySection().serialize(this.joinMessage);
}
/**
* Sets the join message to send to all online players
*
* @param joinMessage join message. If null, no message will be sent
*/
public void setJoinMessage(@Nullable String joinMessage) {
this.joinMessage = joinMessage != null ? LegacyComponentSerializer.legacySection().deserialize(joinMessage) : null;
}
@Override
@NotNull
public HandlerList getHandlers() {
return handlers;
}
}
@@ -0,0 +1,76 @@
/*
* 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.event.bot;
import org.bukkit.event.Cancellable;
import org.bukkit.event.Event;
import org.bukkit.event.HandlerList;
import org.jetbrains.annotations.NotNull;
import java.util.UUID;
/**
* Call when a fakeplayer loaded
*/
public class BotLoadEvent extends Event implements Cancellable {
private static final HandlerList handlers = new HandlerList();
private final String bot;
private final UUID botUUID;
private boolean cancel = false;
public BotLoadEvent(@NotNull final String who, @NotNull final UUID uuid) {
this.bot = who;
this.botUUID = uuid;
}
@NotNull
public static HandlerList getHandlerList() {
return handlers;
}
/**
* Gets the fakeplayer name
*
* @return fakeplayer name
*/
public String getBot() {
return bot;
}
public UUID getBotUUID() {
return botUUID;
}
@Override
public boolean isCancelled() {
return cancel;
}
@Override
public void setCancelled(boolean cancel) {
this.cancel = cancel;
}
@Override
@NotNull
public HandlerList getHandlers() {
return handlers;
}
}
@@ -0,0 +1,122 @@
/*
* 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.event.bot;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer;
import org.bukkit.command.CommandSender;
import org.bukkit.event.Cancellable;
import org.bukkit.event.HandlerList;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.leavesmc.leaves.entity.bot.Bot;
/**
* Call when a fakeplayer removed
*/
public class BotRemoveEvent extends BotEvent implements Cancellable {
private static final HandlerList handlers = new HandlerList();
private final RemoveReason reason;
private final CommandSender remover;
private Component removeMessage;
private boolean save;
private boolean cancel = false;
public BotRemoveEvent(@NotNull final Bot who, @NotNull RemoveReason reason, @Nullable CommandSender remover, @Nullable Component removeMessage, boolean save) {
super(who);
this.reason = reason;
this.remover = remover;
this.removeMessage = removeMessage;
this.save = save;
}
@NotNull
public static HandlerList getHandlerList() {
return handlers;
}
/**
* Gets the remove reason of the bot
*
* @return remove reason
*/
@NotNull
public RemoveReason getReason() {
return reason;
}
/**
* Gets the remover of the bot
* if the remove reason is not COMMAND, the creator might be null
*
* @return An optional of remover
*/
@Nullable
public CommandSender getRemover() {
return remover;
}
public Component removeMessage() {
return removeMessage;
}
public void removeMessage(Component removeMessage) {
this.removeMessage = removeMessage;
}
@Nullable
public String getRemoveMessage() {
return this.removeMessage == null ? null : LegacyComponentSerializer.legacySection().serialize(this.removeMessage);
}
public void setRemoveMessage(@Nullable String removeMessage) {
this.removeMessage = removeMessage != null ? LegacyComponentSerializer.legacySection().deserialize(removeMessage) : null;
}
@Override
public boolean isCancelled() {
return cancel;
}
@Override
public void setCancelled(boolean cancel) {
this.cancel = cancel;
}
public boolean shouldSave() {
return save;
}
public void setSave(boolean save) {
this.save = save;
}
@Override
@NotNull
public HandlerList getHandlers() {
return handlers;
}
public enum RemoveReason {
COMMAND,
PLUGIN,
DEATH,
INTERNAL
}
}
@@ -0,0 +1,55 @@
/*
* 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.event.bot;
import org.bukkit.Location;
import org.bukkit.event.HandlerList;
import org.jetbrains.annotations.NotNull;
import org.leavesmc.leaves.entity.bot.Bot;
public class BotSpawnLocationEvent extends BotEvent {
private static final HandlerList handlers = new HandlerList();
private Location spawnLocation;
public BotSpawnLocationEvent(@NotNull final Bot who, @NotNull Location spawnLocation) {
super(who);
this.spawnLocation = spawnLocation;
}
@NotNull
public static HandlerList getHandlerList() {
return handlers;
}
@NotNull
public Location getSpawnLocation() {
return spawnLocation;
}
public void setSpawnLocation(@NotNull Location location) {
this.spawnLocation = location;
}
@NotNull
@Override
public HandlerList getHandlers() {
return handlers;
}
}
@@ -0,0 +1,73 @@
/*
* 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.event.player;
import org.bukkit.block.Block;
import org.bukkit.entity.Player;
import org.bukkit.event.HandlerList;
import org.bukkit.event.player.PlayerEvent;
import org.jetbrains.annotations.NotNull;
/**
* Called when a player operation is limited
*/
public class PlayerOperationLimitEvent extends PlayerEvent {
private static final HandlerList handlers = new HandlerList();
private final Block block;
private final Operation operation;
public PlayerOperationLimitEvent(@NotNull Player who, Operation operation, Block block) {
super(who);
this.block = block;
this.operation = operation;
}
@NotNull
public static HandlerList getHandlerList() {
return handlers;
}
/**
* Gets the operated block
*
* @return block
*/
public Block getBlock() {
return block;
}
/**
* Gets the type of operation
*
* @return operation type
*/
public Operation getOperation() {
return operation;
}
@NotNull
@Override
public HandlerList getHandlers() {
return handlers;
}
public enum Operation {
MINE, PLACE
}
}
@@ -0,0 +1,63 @@
/*
* 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.event.player;
import org.bukkit.Location;
import org.bukkit.Material;
import org.bukkit.entity.Player;
import org.bukkit.event.Event;
import org.bukkit.event.HandlerList;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
public class UpdateSuppressionEvent extends Event {
private static final HandlerList handlers = new HandlerList();
private final @Nullable Player player;
private final @Nullable Location position;
private final @Nullable Material material;
private final @NotNull Throwable throwable;
public UpdateSuppressionEvent(@Nullable Player player, @Nullable Location position, @Nullable Material material, @NotNull Throwable throwable) {
this.player = player;
this.position = position;
this.material = material;
this.throwable = throwable;
}
@Override
public @NotNull HandlerList getHandlers() {
return handlers;
}
public @Nullable Player getPlayer() {
return player;
}
public @NotNull Throwable getThrowable() {
return throwable;
}
public @Nullable Location getPosition() {
return position;
}
public @Nullable Material getMaterial() {
return material;
}
}
+2 -2
View File
@@ -51,7 +51,7 @@
// 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")
@@ -277,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",
@@ -70,7 +70,7 @@
"Build-Number" to (build ?: ""),
"Build-Time" to buildTime.toString(),
"Git-Branch" to gitBranch,
@@ -448,7 +_,7 @@
@@ -447,7 +_,7 @@
}
fill {
@@ -5,35 +5,23 @@ 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 26a812cd526acfd1edf31a5c9799fa6deeae927a..9b01cf6f066b2998a2b178321782be1c96f73b0c 100644
index 4be82092886728b7862b6e8951cf1c8ab917ca55..1b822e9576573c360b997e5cb4fa02278c88b74f 100644
--- a/src/main/java/me/earthme/luminol/config/ConfigManager.java
+++ b/src/main/java/me/earthme/luminol/config/ConfigManager.java
@@ -19,6 +19,7 @@ public class ConfigManager {
@@ -21,6 +21,7 @@ public class ConfigManager {
public static void initConfigs() throws IOException {
configfiles.put("luminol", new ConfigsInstance(new File("luminol_config"), "luminol", "me.earthme.luminol.config.modules"));
+ configfiles.put("lophine", new ConfigsInstance(new File("lophine_config"), "lophine", "fun.bm.lophine.config.modules")); // add lophine config to global config
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/experiment/CommandBlockExecutionConfig.java b/src/main/java/me/earthme/luminol/config/modules/experiment/CommandBlockExecutionConfig.java
index 531214d7ab28ece4bf84e82b39d1c9f983f0fb0c..b68fa0427bb975e91e76f73ab4cc089a424937a3 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
@@ -5,6 +5,7 @@ import me.earthme.luminol.config.IConfigModule;
import me.earthme.luminol.config.flags.ConfigInfo;
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/ServerModNameConfig.java b/src/main/java/me/earthme/luminol/config/modules/misc/ServerModNameConfig.java
index ded1f0667327a70a923ebda55c41b1c9094d3e37..57ddc0832b919b115b85c81ac30f136e32f1e77e 100644
index f164e41975adee8b68dbcb400007681c0b66a4f8..4e76fb3b1274f2fde734089b20458f6561c39700 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
@@ -6,7 +6,7 @@ import me.earthme.luminol.config.flags.ConfigInfo;
@@ -8,7 +8,7 @@ import me.earthme.luminol.enums.EnumConfigCategory;
@ConfigClassInfo(configAttribution = EnumConfigCategory.MISC, mainName = "server_mod_name")
public class ServerModNameConfig implements IConfigModule {
@ConfigInfo(baseName = "name")
- public static String serverModName = "Luminol";
@@ -0,0 +1,30 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Helvetica Volubi <suisuroru@blue-millennium.fun>
Date: Tue, 26 Aug 2025 22:08:54 +0800
Subject: [PATCH] Transformed Configs
diff --git a/src/main/java/me/earthme/luminol/config/modules/experiment/CommandConfig.java b/src/main/java/me/earthme/luminol/config/modules/experiment/CommandConfig.java
index 4e906e8e0ebd57b9f7000e4194280e3e3636e52c..14eefd162c9facd54a53718a7a0b1b0266e1ada8 100644
--- a/src/main/java/me/earthme/luminol/config/modules/experiment/CommandConfig.java
+++ b/src/main/java/me/earthme/luminol/config/modules/experiment/CommandConfig.java
@@ -11,6 +11,7 @@ public class CommandConfig implements IConfigModule {
@TransformedConfig(name = "enable", category = {"experiment", "force_the_data_command_to_be_enabled"})
@ConfigInfo(baseName = "enable_data_command")
public static boolean data = false;
+ @TransformedConfig(name = "command_block_enabled", category = {"experiment", "command"}, originInstance = "lophine")
@TransformedConfig(name = "enabled", category = {"experiment", "force_enable_command_block_command_execution"})
@ConfigInfo(baseName = "enable_command_block")
public static boolean commandBlock = false;
diff --git a/src/main/java/me/earthme/luminol/config/modules/optimizations/PaperPacketLimiterConfig.java b/src/main/java/me/earthme/luminol/config/modules/optimizations/PaperPacketLimiterConfig.java
index 192a67360db810934d02a15c955af3579e6f6b20..fbafc5a8aa2581301e62e12adb22169ab7fd8e19 100644
--- a/src/main/java/me/earthme/luminol/config/modules/optimizations/PaperPacketLimiterConfig.java
+++ b/src/main/java/me/earthme/luminol/config/modules/optimizations/PaperPacketLimiterConfig.java
@@ -8,6 +8,7 @@ import me.earthme.luminol.enums.EnumConfigCategory;
@ConfigClassInfo(configAttribution = EnumConfigCategory.OPTIMIZATIONS, mainName = "force_disable_packet_limiter_of_paper")
public class PaperPacketLimiterConfig implements IConfigModule {
+ @TransformedConfig(name = "unlimit-packet", category = {"misc", "network"}, originInstance = "lophine")
@TransformedConfig(name = "force_disable", category = {"misc", "force_disable_packet_limiter_of_paper"})
@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..0f8c9d4932c8bbd619f21d4aa28b890e14439b46 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.function.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
@@ -4,8 +4,29 @@ 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..050e89c3e95c7c3ef7ad98f8ef03ab206241a7e2 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.function.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..a876335e26daec9a9a3a2f00ee5cb23cfd913aa6 100644
index 6e2fa442ddcd4ef3c4fe789b9ebac9dd409c9de7..0c7318a0ed214204f5493576c8a7b02fbe51c00b 100644
--- a/net/minecraft/world/item/trading/Merchant.java
+++ b/net/minecraft/world/item/trading/Merchant.java
@@ -42,7 +42,14 @@ public interface Merchant {
@@ -15,9 +36,9 @@ index 6e2fa442ddcd4ef3c4fe789b9ebac9dd409c9de7..a876335e26daec9a9a3a2f00ee5cb23c
- MerchantOffers offers = this.getOffers();
+ // Lophine start - infinite trade
+ MerchantOffers offers = this.getOffers().copy();
+ if (fun.bm.lophine.config.modules.misc.VillagerConfig.villagerInfiniteTrade) {
+ if (fun.bm.lophine.config.modules.function.VillagerConfig.villagerInfiniteTrade) {
+ for (MerchantOffer merchantOffer : offers) {
+ merchantOffer.maxUses = 16384;
+ merchantOffer.maxUses = 5242884;
+ }
+ }
+ // Lophine end - infinite trade
@@ -25,7 +46,7 @@ index 6e2fa442ddcd4ef3c4fe789b9ebac9dd409c9de7..a876335e26daec9a9a3a2f00ee5cb23c
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
index 64c99df8ff305fa28c75dc03fc5ef8c61634ad84..770f37d7158c7b4cec62783025f623a68c1f2457 100644
--- a/net/minecraft/world/item/trading/MerchantOffer.java
+++ b/net/minecraft/world/item/trading/MerchantOffer.java
@@ -196,7 +196,7 @@ public class MerchantOffer {
@@ -33,7 +54,7 @@ index 64c99df8ff305fa28c75dc03fc5ef8c61634ad84..21ec77d5167cfdf05043ea474165148f
public boolean isOutOfStock() {
- return this.uses >= this.maxUses;
+ return (this.uses >= this.maxUses && !fun.bm.lophine.config.modules.misc.VillagerConfig.villagerInfiniteTrade); // Lophine - infinite trade
+ return (this.uses >= this.maxUses && !fun.bm.lophine.config.modules.function.VillagerConfig.villagerInfiniteTrade); // Lophine - infinite trade
}
public void setToOutOfStock() {
@@ -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..4fb9244172b74c3c5fed984d5d3b968f76dd12be 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 (!fun.bm.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();
@@ -0,0 +1,19 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Helvetica Volubi <suisuroru@blue-millennium.fun>
Date: Tue, 26 Aug 2025 02:55:23 +0800
Subject: [PATCH] Add config to allow generation of end void rings
diff --git a/net/minecraft/world/level/levelgen/DensityFunctions.java b/net/minecraft/world/level/levelgen/DensityFunctions.java
index 04527a5c65ad630f794fed9071d485aedd02257a..57e4c6c60f7d012a9de411b07d000f3504c27e6b 100644
--- a/net/minecraft/world/level/levelgen/DensityFunctions.java
+++ b/net/minecraft/world/level/levelgen/DensityFunctions.java
@@ -528,7 +528,7 @@ public final class DensityFunctions {
int i1 = z / 2;
int i2 = x % 2;
int i3 = z % 2;
- float f = 100.0F - Mth.sqrt((long)x * (long)x + (long)z * (long)z) * 8.0F; // Paper - cast ints to long to avoid integer overflow
+ float f = fun.bm.lophine.config.modules.fixes.EndVoidRingsConfig.enabled ? 100.0F - Mth.sqrt(x * x + z * z) * 8.0F : 100.0F - Mth.sqrt((long)x * (long)x + (long)z * (long)z) * 8.0F; // Paper - cast ints to long to avoid integer overflow // Lophine - customize end void rings generation
f = Mth.clamp(f, -100.0F, 80.0F);
NoiseCache cache = noiseCache.get().computeIfAbsent(noise, noiseKey -> new NoiseCache()); // Paper - Perf: Optimize end generation
@@ -1,14 +1,14 @@
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
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 06e41bca6b964ca71ea7bca547b03cdb3610ba31..b7bc75648634aefc549a69631ab6e401eb902e7d 100644
index 11e6197dec541b28733715f0d7eaa4c7b834d632..de1263404395c28209234a556347284fa557f81c 100644
--- a/net/minecraft/server/level/ServerPlayer.java
+++ b/net/minecraft/server/level/ServerPlayer.java
@@ -1341,6 +1341,13 @@ public class ServerPlayer extends Player implements ca.spottedleaf.moonrise.patc
@@ -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);
@@ -22,7 +22,7 @@ index 06e41bca6b964ca71ea7bca547b03cdb3610ba31..b7bc75648634aefc549a69631ab6e401
}
this.level().broadcastEntityEvent(this, (byte)3);
@@ -1364,6 +1371,24 @@ public class ServerPlayer extends Player implements ca.spottedleaf.moonrise.patc
@@ -1368,6 +1375,24 @@ public class ServerPlayer extends Player implements ca.spottedleaf.moonrise.patc
}
// Leaves end - exp fix
@@ -40,7 +40,7 @@ index 06e41bca6b964ca71ea7bca547b03cdb3610ba31..b7bc75648634aefc549a69631ab6e401
+ } catch (Throwable ex) {
+ LOGGER.error(ex.getMessage(), ex);
+ }
+ }, null, 1L );
+ }, null, 1L);
+ }
+ // Luminol End - Cross Region Damage trace
+
@@ -48,40 +48,10 @@ index 06e41bca6b964ca71ea7bca547b03cdb3610ba31..b7bc75648634aefc549a69631ab6e401
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 d55a6989fd68f0bcf7bd05e9420fda1115afa6c0..5369a6597e48ed9f0d8f7f0b9685ce4950df4fe5 100644
index 567d63ae589705e13403814b36e35d00c241a69e..3f4b1c54f9207ba1854a8c8da965cbaca5ea517d 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
}
}
+ // 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 (fun.bm.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);
@@ -1948,6 +1971,13 @@ public abstract class LivingEntity extends Entity implements Attackable, Waypoin
@@ -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);
@@ -95,7 +65,7 @@ index d55a6989fd68f0bcf7bd05e9420fda1115afa6c0..5369a6597e48ed9f0d8f7f0b9685ce49
}
}); // Paper end
this.postDeathDropItems(deathEvent); // Paper
@@ -1958,6 +1988,18 @@ public abstract class LivingEntity extends Entity implements Attackable, Waypoin
@@ -1958,6 +1965,18 @@ public abstract class LivingEntity extends Entity implements Attackable, Waypoin
return deathEvent; // Paper
}
@@ -114,7 +84,7 @@ index d55a6989fd68f0bcf7bd05e9420fda1115afa6c0..5369a6597e48ed9f0d8f7f0b9685ce49
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 +2598,18 @@ public abstract class LivingEntity extends Entity implements Attackable, Waypoin
@@ -2556,6 +2575,18 @@ public abstract class LivingEntity extends Entity implements Attackable, Waypoin
}
}
@@ -133,22 +103,3 @@ index d55a6989fd68f0bcf7bd05e9420fda1115afa6c0..5369a6597e48ed9f0d8f7f0b9685ce49
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 510514e363f4816a6e286fb9b5488153e03c9e55..4638af10539a316acae64fb26881c8e699c496e0 100644
--- a/net/minecraft/world/entity/raid/Raider.java
+++ b/net/minecraft/world/entity/raid/Raider.java
@@ -157,7 +157,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, fun.bm.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
+ // Luminol start - Raid changes adapt DamageSource trace
+ if (fun.bm.lophine.config.modules.experiment.EntityDamageSourceTraceConfig.enabled) {
+ 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);
}
@@ -1,7 +1,7 @@
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
@@ -18,7 +18,7 @@ index 6868b915bf3deb85783a638d4441a15fea6da2dc..70740381c6501c1a518c52b24381edd1
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 894897a120123cb54f9dba898748724173c01f57..312c385780019bf483e79022bba3fe6e12ca3be7 100644
index 8ec753fe9c84ffe1188f7e7ebb6895cf4bba9e39..ee4a459700d75676eae3422d4a0e10be73c45871 100644
--- a/net/minecraft/world/entity/Entity.java
+++ b/net/minecraft/world/entity/Entity.java
@@ -147,7 +147,7 @@ import net.minecraft.world.waypoints.WaypointTransmitter;
@@ -30,7 +30,7 @@ index 894897a120123cb54f9dba898748724173c01f57..312c385780019bf483e79022bba3fe6e
// CraftBukkit start
private static final int CURRENT_LEVEL = 2;
public boolean preserveMotion = true; // Paper - Fix Entity Teleportation and cancel velocity if teleported; keep initial motion on first snapTo
@@ -6365,4 +6365,46 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess
@@ -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
@@ -1,207 +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
Co-authored by: huanli233 <392352840@qq.com>
Some of changes is a part of : Leaves (https://github.com/LeavesMC/Leaves/blob/b5793e809b6346e16b7ea218496806a312562e27/leaves-server/minecraft-patches/features/0106-Revert-raid-changes.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..985f27d249810bf133a520d937b7006e57f553ec 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.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 1910bff3f08147bfd63703097e30b9f88c84e337..26f4d7359f638a11ea2d768d18091e261d6b1791 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 (!fun.bm.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 (!fun.bm.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 > (fun.bm.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 (fun.bm.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 3e30989c99cd11c8c94d6fd194d2969e3a7efda6..510514e363f4816a6e286fb9b5488153e03c9e55 100644
--- a/net/minecraft/world/entity/raid/Raider.java
+++ b/net/minecraft/world/entity/raid/Raider.java
@@ -128,6 +128,41 @@ public abstract class Raider extends PatrollingMonster {
currentRaid.removeFromRaid(serverLevel, this, false);
}
+
+ // Leaves start - Revert raid changes
+ if (fun.bm.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, fun.bm.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);
@@ -156,7 +191,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 !fun.bm.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..2fca459f2c888079322f6b957bbee3ac0e33c4bc 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, fun.bm.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, fun.bm.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());
}
}
@@ -5,11 +5,11 @@ Subject: [PATCH] Add config to enable datapack command
diff --git a/net/minecraft/commands/Commands.java b/net/minecraft/commands/Commands.java
index 0d5ed9602492c8393088954105e9a19032067b8b..e2fc15d84fafde6847ecead23c55169449275e1b 100644
index e05f75b6bbd48d6fb7379926d68d5f8ed6014be9..af895859057ca303177ec3c8a07cf7534e62cca6 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) {
if(me.earthme.luminol.config.modules.experiment.CommandConfig.data) {
DataCommands.register(this.dispatcher); // Folia - region threading - TODO
}
- //DataPackCommand.register(this.dispatcher, context); // Folia - region threading - TODO
@@ -20,10 +20,10 @@ index 0d5ed9602492c8393088954105e9a19032067b8b..e2fc15d84fafde6847ecead23c551694
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 6675b83c9f15d0341dcf169ec3ac20f401c5d10b..e8c9e739de35313c0212bab97a202fa362fd0887 100644
index 75027e73859c955a54034640f905cb5d91a6a2f4..b4b3ff86f7c4afb94fc4da5133746391a078a7fb 100644
--- a/net/minecraft/server/MinecraftServer.java
+++ b/net/minecraft/server/MinecraftServer.java
@@ -2349,6 +2349,16 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
@@ -2353,6 +2353,16 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
return this.reloadResources(selectedIds, io.papermc.paper.event.server.ServerResourcesReloadedEvent.Cause.PLUGIN);
}
@@ -0,0 +1,21 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Helvetica Volubi <suisuroru@blue-millennium.fun>
Date: Thu, 4 Sep 2025 02:14:16 +0800
Subject: [PATCH] Add config to enable function command
diff --git a/net/minecraft/commands/Commands.java b/net/minecraft/commands/Commands.java
index af895859057ca303177ec3c8a07cf7534e62cca6..e0e9fcf3224f55f2ef31c8afca3154a5992f761b 100644
--- a/net/minecraft/commands/Commands.java
+++ b/net/minecraft/commands/Commands.java
@@ -206,7 +206,9 @@ public class Commands {
FillCommand.register(this.dispatcher, context);
FillBiomeCommand.register(this.dispatcher, context);
ForceLoadCommand.register(this.dispatcher);
- //FunctionCommand.register(this.dispatcher); // Folia - region threading - TODO
+ if (fun.bm.lophine.config.modules.experiment.CommandConfig.function) {
+ FunctionCommand.register(this.dispatcher); // Folia - region threading - TODO
+ }
GameModeCommand.register(this.dispatcher);
GameRuleCommand.register(this.dispatcher, context);
GiveCommand.register(this.dispatcher, context);
@@ -44,7 +44,7 @@ index e1842ab8f20271a8b20ab8462b09a4745a612a9a..8405157ec35a443e238fa6866772c839
world.setDayTime(world.levelData.getDayTime() + (long)tickCount);
}
diff --git a/io/papermc/paper/threadedregions/TickRegionScheduler.java b/io/papermc/paper/threadedregions/TickRegionScheduler.java
index 0357792de0ed8ec9058d1847c8b45c33ff365af6..4945c11f573a53df2f5be4beb7af6a3f4d32f7d4 100644
index c485d5b3ba95f2a969bdf0dab9654ac284976f8d..b957585cf7cf7f845e20f59c7b355722880f3b4e 100644
--- a/io/papermc/paper/threadedregions/TickRegionScheduler.java
+++ b/io/papermc/paper/threadedregions/TickRegionScheduler.java
@@ -31,8 +31,8 @@ public final class TickRegionScheduler {
@@ -97,10 +97,10 @@ index 0357792de0ed8ec9058d1847c8b45c33ff365af6..4945c11f573a53df2f5be4beb7af6a3f
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 8a90aa5c145f53afbb3f2371f97cb09974f30bbb..0d5ed9602492c8393088954105e9a19032067b8b 100644
index e0e9fcf3224f55f2ef31c8afca3154a5992f761b..a5b59eb79c014ef7a48ff305ed554fc33a184d87 100644
--- a/net/minecraft/commands/Commands.java
+++ b/net/minecraft/commands/Commands.java
@@ -243,7 +243,11 @@ public class Commands {
@@ -247,7 +247,11 @@ public class Commands {
TeleportCommand.register(this.dispatcher);
TellRawCommand.register(this.dispatcher, context);
//TestCommand.register(this.dispatcher, context); // Folia - region threading
@@ -114,7 +114,7 @@ index 8a90aa5c145f53afbb3f2371f97cb09974f30bbb..0d5ed9602492c8393088954105e9a190
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 2ed2f4cca3a81117c0beaf7a11bbbe8156f6a9ce..6675b83c9f15d0341dcf169ec3ac20f401c5d10b 100644
index d08a5b553f4e5932d960fbcdeb6a43419730e32a..a0f1db50adf5163a671d3d9e9df7adb7de4250c6 100644
--- a/net/minecraft/server/MinecraftServer.java
+++ b/net/minecraft/server/MinecraftServer.java
@@ -267,7 +267,7 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
@@ -1,20 +1,20 @@
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
Subject: [PATCH] Add config to enable waypoint command & bar
diff --git a/net/minecraft/commands/Commands.java b/net/minecraft/commands/Commands.java
index e2fc15d84fafde6847ecead23c55169449275e1b..b0a06cdfaa49237f372ecd131d47289160476b07 100644
index a5b59eb79c014ef7a48ff305ed554fc33a184d87..e8c7cc10f10fc013100fa66f3489ac98b3a29227 100644
--- a/net/minecraft/commands/Commands.java
+++ b/net/minecraft/commands/Commands.java
@@ -253,7 +253,11 @@ public class Commands {
@@ -255,7 +255,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) {
+ if (fun.bm.lophine.config.modules.experiment.CommandConfig.waypoint) {
+ WaypointCommand.register(this.dispatcher, context); // Folia - region threading - TODO later
+ }
+ // Lophine end - unsafe waypoint bar
@@ -22,7 +22,7 @@ index e2fc15d84fafde6847ecead23c55169449275e1b..b0a06cdfaa49237f372ecd131d472891
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
index 0f8cacbb8fe55a60e2f0c98bf36c005b29f41a4b..52fd730998535ea071bfc99b7cc2c254b9b656d7 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
@@ -31,7 +31,7 @@ index 0f8cacbb8fe55a60e2f0c98bf36c005b29f41a4b..36e9b027ce5213f8914728293633e78e
public void trackWaypoint(WaypointTransmitter waypoint) {
- // Folia - region threading
+ // Lophine start - unsafe waypoint bar
+ if (!fun.bm.lophine.config.modules.experiment.WaypointConfig.forceEnableWaypointUnsafe) return;
+ if (!fun.bm.lophine.config.modules.experiment.CommandConfig.waypoint) return;
+ this.waypoints.add(waypoint);
+
+ for (ServerPlayer serverPlayer : this.players) {
@@ -46,7 +46,7 @@ index 0f8cacbb8fe55a60e2f0c98bf36c005b29f41a4b..36e9b027ce5213f8914728293633e78e
public void addPlayer(ServerPlayer player) {
// Folia - region threading
+ // Lophine start - unsafe waypoint bar
+ if (!fun.bm.lophine.config.modules.experiment.WaypointConfig.forceEnableWaypointUnsafe) return;
+ if (!fun.bm.lophine.config.modules.experiment.CommandConfig.waypoint) return;
+ this.players.add(player);
+
+ for (WaypointTransmitter waypointTransmitter : this.waypoints) {
@@ -62,7 +62,7 @@ index 0f8cacbb8fe55a60e2f0c98bf36c005b29f41a4b..36e9b027ce5213f8914728293633e78e
public void updatePlayer(ServerPlayer player) {
// Folia - region threading
+ // Lophine start - unsafe waypoint bar
+ if (!fun.bm.lophine.config.modules.experiment.WaypointConfig.forceEnableWaypointUnsafe) return;
+ if (!fun.bm.lophine.config.modules.experiment.CommandConfig.waypoint) return;
+ Map<WaypointTransmitter, WaypointTransmitter.Connection> map = this.connections.row(player);
+ SetView<WaypointTransmitter> set = Sets.difference(this.waypoints, map.keySet());
+
@@ -79,7 +79,7 @@ index 0f8cacbb8fe55a60e2f0c98bf36c005b29f41a4b..36e9b027ce5213f8914728293633e78e
public void removePlayer(ServerPlayer player) {
// Folia - region threading
+ // Lophine start - unsafe waypoint bar
+ if (!fun.bm.lophine.config.modules.experiment.WaypointConfig.forceEnableWaypointUnsafe) return;
+ if (!fun.bm.lophine.config.modules.experiment.CommandConfig.waypoint) return;
+ this.connections.row(player).values().removeIf(connection -> {
+ connection.disconnect();
+ return true;
@@ -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 b156b5c635f931a3d4abc0584591f90476d011a6..0b7c014114f1f8648824fe7c9b75fde3ea79162a 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) {
@@ -2227,9 +2228,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() {
@@ -1,30 +1,34 @@
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..3b4d97c76db9c742072c92dd7c8b39553c62a1f1 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 = fun.bm.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 5a1c21b05545a03fbb00cf734605049870d3eecb..2ade5ce23cdab08b1746e2cbe99bfe1d3da42f02 100644
index 7ef109cb9921c25987905149616eddbd0b2f9b9c..70fc9d6c5b226211ff5964464c37e11695d5f745 100644
--- a/net/minecraft/server/commands/GiveCommand.java
+++ b/net/minecraft/server/commands/GiveCommand.java
@@ -55,7 +55,7 @@ public class GiveCommand {
@@ -32,76 +36,76 @@ index 5a1c21b05545a03fbb00cf734605049870d3eecb..2ade5ce23cdab08b1746e2cbe99bfe1d
ItemStack itemStack = item.createItemStack(1, false);
final Component displayName = itemStack.getDisplayName(); // Paper - get display name early
- int maxStackSize = itemStack.getMaxStackSize();
+ int maxStackSize = fun.bm.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 68f0023c4234585a8385a830ed410883c8bd1549..1aed2578bb5f4e605fe68d1cfe4dce6a4cf5a1f7 100644
index 74ec003486f9362d2bafbad8392b9bbdf0c15882..f06b9d8ad9512d35f3f460eb77c869690321c67f 100644
--- a/net/minecraft/server/network/ServerGamePacketListenerImpl.java
+++ b/net/minecraft/server/network/ServerGamePacketListenerImpl.java
@@ -3076,7 +3076,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, fun.bm.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;
@@ -3112,7 +3112,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() <= fun.bm.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;
}
@@ -3323,6 +3323,7 @@ public class ServerGamePacketListenerImpl
@@ -3326,6 +3326,7 @@ public class ServerGamePacketListenerImpl
this.player.containerMenu.broadcastFullState();
} else {
this.player.containerMenu.broadcastChanges();
+ if (fun.bm.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.
}
@@ -3433,7 +3434,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() <= fun.bm.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();
@@ -3475,6 +3476,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 (fun.bm.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..5d8bd05f966cf9befe8b8f3359b2fa4ce2428e98 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(), fun.bm.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 d907e24d563e27acab2f2bf9711b1755ea9afd19..6189cb7c1745a242b610e89c859cc849f16cc9e3 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 d907e24d563e27acab2f2bf9711b1755ea9afd19..6189cb7c1745a242b610e89c859cc849
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 d907e24d563e27acab2f2bf9711b1755ea9afd19..6189cb7c1745a242b610e89c859cc849
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 b745eb5d9c68547335247910ff2ae8d5fb36349c..6d8675b918f92dc3ce85825edf2b33469950ef9d 100644
index fd4f1e63f980b6959fc80084651991f8f8bc6c34..e5e8c752129a6ebb0dd1315c9b8ec459be11ef07 100644
--- a/net/minecraft/world/entity/item/ItemEntity.java
+++ b/net/minecraft/world/entity/item/ItemEntity.java
@@ -279,10 +279,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() < fun.bm.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 (!fun.bm.lophine.utils.ShulkerBoxesUtil.checkShulkerBox(sf)) return false;
+ if (sf.getItem().equals(ot.getItem())
+ && fun.bm.lophine.utils.ShulkerBoxesUtil.emptyShulkerBoxCheck(sf)
+ && fun.bm.lophine.utils.ShulkerBoxesUtil.emptyShulkerBoxCheck(ot)
+ && Objects.equals(sf.getComponents(), ot.getComponents()) // empty block entity tags are cleaned up when spawning
+ && sf.getCount() != fun.bm.lophine.utils.ShulkerBoxesUtil.getShulkerBoxesMaxCountUnsafe()) {
+ int count = Math.min(ot.getCount(), fun.bm.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 (fun.bm.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 a6bb436dc80daf6901dc027a6011ead4b3ed27e2..53210a7d32241c47c2b1cd698b515726f0a1fd0d 100644
index a6bb436dc80daf6901dc027a6011ead4b3ed27e2..71df58ae26325a2d28b6679733c474c0c59f7654 100644
--- a/net/minecraft/world/entity/player/Inventory.java
+++ b/net/minecraft/world/entity/player/Inventory.java
@@ -166,8 +166,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)
+ && fun.bm.lophine.utils.ShulkerBoxesUtil.isStackable(destination) // Lophine - Stackable ShulkerBoxes
+ && destination.getCount() < fun.bm.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
}
@@ -181,7 +181,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 -= (fun.bm.lophine.utils.ShulkerBoxesUtil.getItemMaxCount(itemInSlot) < this.getMaxStackSize() ? fun.bm.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();
@@ -190,7 +190,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 -= (fun.bm.lophine.utils.ShulkerBoxesUtil.getItemMaxCount(itemInOffhand) < this.getMaxStackSize() ? fun.bm.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();
@@ -314,7 +314,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;
@@ -420,7 +420,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 = fun.bm.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..3206b599481eeab29c58bc1ca9cac6935da9963a 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..3206b599481eeab29c58bc1ca9cac693
public void accountStack(ItemStack stack) {
- this.accountStack(stack, stack.getMaxStackSize());
+ this.accountStack(stack, fun.bm.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 02d2efef2dc0f0e12eac0c71fa290af706f7694d..d042b3729a615bdabeb08f559ac895a731856545 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 02d2efef2dc0f0e12eac0c71fa290af706f7694d..d042b3729a615bdabeb08f559ac895a7
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 9b6cd35e4dfa894069238a1365db16050aa5542b..a928c2e50e43245542a8bc8e307acf27259bbf74 100644
index 2d6acceffb2cb7e55dd504018a70b05afccd9c11..bdc0ca1f625dc264bef6e5722ec66121ba48625a 100644
--- a/net/minecraft/world/inventory/AbstractContainerMenu.java
+++ b/net/minecraft/world/inventory/AbstractContainerMenu.java
@@ -258,6 +258,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();
@@ -451,7 +459,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(fun.bm.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));
@@ -563,7 +571,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(), fun.bm.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);
@@ -625,7 +633,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(fun.bm.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);
@@ -656,15 +664,15 @@ public abstract class AbstractContainerMenu {
@@ -660,15 +667,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() < fun.bm.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 9b6cd35e4dfa894069238a1365db16050aa5542b..a928c2e50e43245542a8bc8e307acf27
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() != fun.bm.lophine.utils.ShulkerBoxesUtil.getItemMaxCount(item1)) { // Lophine - stackable shulker boxes
+ ItemStack itemStack1 = slot3.safeTake(item1.getCount(), fun.bm.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());
}
}
@@ -766,7 +774,7 @@ public abstract class AbstractContainerMenu {
@@ -770,7 +777,7 @@ public abstract class AbstractContainerMenu {
i = endIndex - 1;
}
- if (stack.isStackable()) {
+ if (fun.bm.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();
@@ -867,7 +875,7 @@ public abstract class AbstractContainerMenu {
@@ -871,7 +878,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()) <= fun.bm.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;
}
@@ -875,7 +883,7 @@ public abstract class AbstractContainerMenu {
@@ -879,7 +886,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 -> fun.bm.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();
};
}
@@ -897,7 +905,7 @@ public abstract class AbstractContainerMenu {
@@ -902,7 +909,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..dd978d1590fb896775a240ed87e38d85744017d3 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..dd978d1590fb896775a240ed87e38d85
public int getMaxStackSize(ItemStack stack) {
- return Math.min(this.getMaxStackSize(), stack.getMaxStackSize());
+ return Math.min(this.getMaxStackSize(), fun.bm.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 6154da1158756cff072d7e8cae10e20eafed1eec..687249e1ad1cb09edbe2939f1db2f90e146cd664 100644
index 33c3a5e39e8e1aa023baab76ef94f8aed481480e..a27e1d53abc2d8b8912226add33510067e10ed52 100644
--- a/net/minecraft/world/item/ItemStack.java
+++ b/net/minecraft/world/item/ItemStack.java
@@ -162,7 +162,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;
@@ -189,7 +189,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 fun.bm.lophine.utils.ShulkerBoxesUtil.decodeMaxStackSize(itemStack);
+ return org.leavesmc.leaves.util.ItemOverstackUtils.decodeMaxStackSize(itemStack);
}
}
@@ -198,13 +199,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 = fun.bm.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 6154da1158756cff072d7e8cae10e20eafed1eec..687249e1ad1cb09edbe2939f1db2f90e
} finally {
net.minecraft.network.chat.ComponentSerialization.DONT_RENDER_TRANSLATABLES.set(prev);
}
@@ -299,7 +302,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 = fun.bm.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 1a927b08f86c0585e62d8c4207954ab36d3316ee..e9899a03b15a2ca3dbd941775f14b041bbcfc85e 100644
--- a/net/minecraft/world/level/block/AbstractCauldronBlock.java
+++ b/net/minecraft/world/level/block/AbstractCauldronBlock.java
@@ -57,9 +57,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 (fun.bm.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 36a72a11d28f99bfe85868461925b778cc01478e..831900a4ed6ca335707289391a095af7717a01a4 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 5a094257a31f0500278a706a418e1697f8810ffb..1f132848ae284668c05d6ca17d5890fa341e14cd 100644
index 10b2cc20bbf3b70e4e09dbbe14b90506e6366158..1535bb8cec8692e8d26eaff090288f1b8f75c253 100644
--- a/net/minecraft/world/level/block/entity/BaseContainerBlockEntity.java
+++ b/net/minecraft/world/level/block/entity/BaseContainerBlockEntity.java
@@ -145,7 +145,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 5de769e7bd56bb3355c3c711d46ce5e103ea4409..061501979195cf43c6da4d4b6cf6359251978bd3 100644
index 4b634dbb45dfb0823e7b21cf2354f89e5a6331e9..ef32bf1351aa28953bfc625d7a7d5175181a7acc 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
@@ -770,9 +770,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
@@ -1,153 +0,0 @@
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] 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/bfde470a867b74d177fe1b9a2a3ed5c49cd126c7/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/ShearsItem.java b/net/minecraft/world/item/ShearsItem.java
index 8cf3e51e12f9cf98836657e722edb23943f9e866..dc3e33223ac7b11a969ef1a8e3fa73a9b8b44696 100644
--- a/net/minecraft/world/item/ShearsItem.java
+++ b/net/minecraft/world/item/ShearsItem.java
@@ -24,6 +24,30 @@ import net.minecraft.world.level.block.GrowingPlantHeadBlock;
import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.world.level.gameevent.GameEvent;
+// Leaves start - shears wrench
+import net.minecraft.Util;
+import net.minecraft.network.chat.Component;
+import net.minecraft.world.level.block.ComparatorBlock;
+import net.minecraft.world.level.block.DispenserBlock;
+import net.minecraft.world.level.block.HopperBlock;
+import net.minecraft.world.level.block.ObserverBlock;
+import net.minecraft.world.level.block.RepeaterBlock;
+import net.minecraft.world.level.block.CrafterBlock;
+import net.minecraft.world.level.block.LeverBlock;
+import net.minecraft.world.level.block.CocoaBlock;
+import net.minecraft.world.level.block.TrapDoorBlock;
+import net.minecraft.world.level.block.FenceGateBlock;
+import net.minecraft.world.level.block.LightningRodBlock;
+import net.minecraft.world.level.block.RailBlock;
+import net.minecraft.world.level.block.BaseRailBlock;
+import net.minecraft.world.level.block.PoweredRailBlock;
+import net.minecraft.world.level.block.CalibratedSculkSensorBlock;
+import net.minecraft.world.level.block.piston.PistonBaseBlock;
+import net.minecraft.world.level.block.state.StateDefinition;
+import net.minecraft.world.level.block.state.properties.RailShape;
+import net.minecraft.world.level.block.state.properties.Property;
+// Leaves end - shears wrench
+
public class ShearsItem extends Item {
public ShearsItem(Item.Properties properties) {
super(properties);
@@ -80,7 +104,108 @@ public class ShearsItem extends Item {
return InteractionResult.SUCCESS;
} else {
+ // Leaves start - shears wrench
+ Block block = blockState.getBlock();
+ if (fun.bm.lophine.config.modules.misc.RedStoneConfig.shears &&
+ block instanceof ObserverBlock ||
+ block instanceof DispenserBlock ||
+ block instanceof PistonBaseBlock ||
+ block instanceof HopperBlock ||
+ block instanceof RepeaterBlock ||
+ block instanceof ComparatorBlock ||
+ block instanceof CrafterBlock ||
+ block instanceof LeverBlock ||
+ block instanceof CocoaBlock ||
+ block instanceof TrapDoorBlock ||
+ block instanceof FenceGateBlock ||
+ block instanceof LightningRodBlock ||
+ block instanceof CalibratedSculkSensorBlock ||
+ block instanceof BaseRailBlock
+ ) {
+ StateDefinition<Block, BlockState> blockstatelist = block.getStateDefinition();
+ Property<?> iblockstate;
+ if (block instanceof CrafterBlock) iblockstate = blockstatelist.getProperty("orientation");
+ else if (block instanceof BaseRailBlock) iblockstate = blockstatelist.getProperty("shape");
+ else iblockstate = blockstatelist.getProperty("facing");
+ Player player = context.getPlayer();
+
+ if (iblockstate == null || player == null) {
+ return InteractionResult.FAIL;
+ }
+
+ if (block instanceof BaseRailBlock) {
+ if (block instanceof RailBlock) {
+ if (blockState.getValue(RailBlock.SHAPE).isSlope()) {
+ return InteractionResult.FAIL;
+ }
+ } else {
+ if (getNameHelper(blockState, PoweredRailBlock.POWERED).equals("true")) {
+ return InteractionResult.FAIL;
+ }
+ if (blockState.getValue(PoweredRailBlock.SHAPE).isSlope()) {
+ return InteractionResult.FAIL;
+ }
+ }
+ }
+
+ if (block instanceof PistonBaseBlock) {
+ if (getNameHelper(blockState, PistonBaseBlock.EXTENDED).equals("true")) {
+ return InteractionResult.FAIL;
+ }
+ }
+
+ if (block instanceof RepeaterBlock || block instanceof ComparatorBlock) {
+ if (getNameHelper(blockState, ComparatorBlock.POWERED).equals("true")) {
+ return InteractionResult.FAIL;
+ }
+ if (block instanceof RepeaterBlock) {
+ if (getNameHelper(blockState, RepeaterBlock.LOCKED).equals("true")) {
+ return InteractionResult.FAIL;
+ }
+ }
+ }
+
+ if (block instanceof CrafterBlock) {
+ if (getNameHelper(blockState, CrafterBlock.CRAFTING).equals("true")) {
+ return InteractionResult.FAIL;
+ }
+ }
+
+ BlockState iblockdata1 = ShearsItem.cycleState(blockState, iblockstate, player.isSecondaryUseActive());
+ level.setBlock(clickedPos, iblockdata1, 18);
+ ShearsItem.message(player, Component.translatable("item.minecraft.debug_stick.update", iblockstate.getName(), ShearsItem.getNameHelper(iblockdata1, iblockstate)));
+ return InteractionResult.CONSUME;
+ }
+ // Leaves end - shears wrench
+
return super.useOn(context);
}
}
+
+ // Leaves start - shears wrench
+ private static <T extends Comparable<T>> BlockState cycleState(BlockState state, Property<T> property, boolean inverse) {
+ List<T> possibleValues = property.getPossibleValues();
+ if (possibleValues.getFirst() instanceof RailShape) {
+ boolean isRailBlock = state.getBlock() instanceof RailBlock;
+ possibleValues = possibleValues.stream().filter(possibleValue -> {
+ RailShape shape = (RailShape) possibleValue;
+ if (isRailBlock) return !shape.isSlope();
+ return !shape.isSlope() && shape != RailShape.NORTH_EAST && shape != RailShape.NORTH_WEST && shape != RailShape.SOUTH_EAST && shape != RailShape.SOUTH_WEST;
+ }).toList();
+ }
+ return state.setValue(property, ShearsItem.getRelative(possibleValues, state.getValue(property), inverse)); // CraftBukkit - decompile error
+ }
+
+ private static <T> T getRelative(Iterable<T> elements, T current, boolean inverse) {
+ return inverse ? Util.findPreviousInIterable(elements, current) : Util.findNextInIterable(elements, current);
+ }
+
+ private static void message(Player player, Component message) {
+ ((ServerPlayer) player).sendSystemMessage(message, true);
+ }
+
+ private static <T extends Comparable<T>> String getNameHelper(BlockState state, Property<T> property) {
+ return property.getName(state.getValue(property));
+ }
+ // Leaves end - shears wrench
}
@@ -1,21 +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] Old Explosion Damage Calculator
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
index b3e897a4afee742b5055d652ad7047a660d8ea04..33471f3d0e2cbd54eaf095c19555f8fdaeb55878 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
+ if (fun.bm.lophine.config.modules.function.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;
}
@@ -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..c9af615ff9120c07787f436dbb6024e54e3a498b 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.function.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..f45aa44a7a17946aeb406a447f18de34320a4fa2 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.function.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.function.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.function.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.function.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.function.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..2d163f3d5eb1af4d539d24c0256855bbec960de4 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.function.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,51 +0,0 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Helvetica Volubi <suisuroru@blue-millennium.fun>
Date: Wed, 18 Jun 2025 14:24:54 +0800
Subject: [PATCH] Old replaceable by mushrooms
diff --git a/net/minecraft/world/level/Level.java b/net/minecraft/world/level/Level.java
index 93d722d437121e605cee0e168ac17ee9bef0610e..cb2a2fbd17157726b285665fdca0ab9663e0d1de 100644
--- a/net/minecraft/world/level/Level.java
+++ b/net/minecraft/world/level/Level.java
@@ -1111,7 +1111,7 @@ public abstract class Level implements LevelAccessor, UUIDLookup<Entity>, AutoCl
if (worldData.captureTreeGeneration) { // Folia - region threading
// Paper start - Protect Bedrock and End Portal/Frames from being destroyed
BlockState type = getBlockState(pos);
- if (!type.isDestroyable()) return false;
+ if (!type.isDestroyable() && !(fun.bm.lophine.config.modules.misc.OldFeatureConfig.oldReplaceableByMushrooms && flags == 3)) return false;
// Paper end - Protect Bedrock and End Portal/Frames from being destroyed
CraftBlockState blockstate = worldData.capturedBlockStates.get(pos); // Folia - region threading
if (blockstate == null) {
diff --git a/net/minecraft/world/level/block/state/BlockBehaviour.java b/net/minecraft/world/level/block/state/BlockBehaviour.java
index 834e27ef2f7b342b074ff9e1e390e02f3ca1c399..f5765e3bbf9e735d6f959e1bf6ee1846ee11b4e9 100644
--- a/net/minecraft/world/level/block/state/BlockBehaviour.java
+++ b/net/minecraft/world/level/block/state/BlockBehaviour.java
@@ -770,6 +770,14 @@ public abstract class BlockBehaviour implements FeatureElement {
return this.solidRender;
}
+ public boolean mushroomCheck(BlockState state) {
+ BlockState blockState = state.asState();
+ if (blockState.canOcclude()) {
+ return !Block.isShapeFullBlock(blockState.getOcclusionShape());
+ }
+ return true;
+ }
+
public final boolean canOcclude() { // Paper - Perf: Final for inlining
return this.canOcclude;
}
diff --git a/net/minecraft/world/level/levelgen/feature/AbstractHugeMushroomFeature.java b/net/minecraft/world/level/levelgen/feature/AbstractHugeMushroomFeature.java
index 3a37d66ab3c27c9abd60f35ef3bf3a93f8d7c3cd..2aad65d76cdd0800ac593a5bb3ce84ad1925eab0 100644
--- a/net/minecraft/world/level/levelgen/feature/AbstractHugeMushroomFeature.java
+++ b/net/minecraft/world/level/levelgen/feature/AbstractHugeMushroomFeature.java
@@ -26,7 +26,7 @@ public abstract class AbstractHugeMushroomFeature extends Feature<HugeMushroomFe
protected void placeMushroomBlock(LevelAccessor level, BlockPos.MutableBlockPos mutablePos, BlockState state) {
BlockState blockState = level.getBlockState(mutablePos);
- if (blockState.isAir() || blockState.is(BlockTags.REPLACEABLE_BY_MUSHROOMS)) {
+ if (blockState.isAir() || blockState.is(BlockTags.REPLACEABLE_BY_MUSHROOMS) || (fun.bm.lophine.config.modules.misc.OldFeatureConfig.oldReplaceableByMushrooms && state.mushroomCheck(blockState))) { // Lophine - old replaceable by mushrooms
this.setBlock(level, mutablePos, state);
}
}
@@ -8,16 +8,16 @@ 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 a24c332a8f9cb2ebc6f9926c6dabf1238238028d..58c2a6a3304e1bea25980b04046fe7c0017bc90d 100644
index 593dbcda4b497291eb1ecdc64b947a8d279f8be6..cb863a36fc67e993956ec3be655e6e4b9764c051 100644
--- a/net/minecraft/server/players/PlayerList.java
+++ b/net/minecraft/server/players/PlayerList.java
@@ -1086,6 +1086,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 = 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;
+ player.enderChestSlotCount = fun.bm.lophine.config.modules.function.ContainerExpansionConfig.enderchestRows < 7 && fun.bm.lophine.config.modules.function.ContainerExpansionConfig.enderchestRows > 0 ? 9 * fun.bm.lophine.config.modules.function.ContainerExpansionConfig.enderchestRows : 27;
+ // Purpur end - Barrels and enderchests 6 rows
}
@@ -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 beb74bc8398de8b48f41af7daef14d624826310e..5dd6e89931d397b8afdb84024ed46b0d06dcf347 100644
index 51148b7b95c93a9e25da70ac9275c63f1458d84c..dba8acfcacfe9a7b7e0fb9943fba49468214616e 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(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);
+ super(fun.bm.lophine.config.modules.function.ContainerExpansionConfig.enderchestRows < 7 && fun.bm.lophine.config.modules.function.ContainerExpansionConfig.enderchestRows > 0 ? 9 * fun.bm.lophine.config.modules.function.ContainerExpansionConfig.enderchestRows : 27);
this.owner = owner;
// CraftBukkit end
}
@@ -94,7 +94,7 @@ index beb74bc8398de8b48f41af7daef14d624826310e..5dd6e89931d397b8afdb84024ed46b0d
this.activeChest = enderChestBlockEntity;
}
diff --git a/net/minecraft/world/level/block/EnderChestBlock.java b/net/minecraft/world/level/block/EnderChestBlock.java
index 5077a9ff7b78801bdc53536a37aee07b8d86ee4d..fe374f08b7829b846b2a1c2ac3ca500ca3e48684 100644
index 5077a9ff7b78801bdc53536a37aee07b8d86ee4d..cef29289b7bc7fdf9613c0cd52f2bf4f715f288d 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..fe374f08b7829b846b2a1c2ac3ca500c
new SimpleMenuProvider(
- (containerId, playerInventory, player1) -> ChestMenu.threeRows(containerId, playerInventory, enderChestInventory), CONTAINER_TITLE
- )
+ (containerId, playerInventory, player1) -> switch (fun.bm.lophine.config.modules.misc.ContainerExpansionConfig.enderchestRows) {
+ (containerId, playerInventory, player1) -> switch (fun.bm.lophine.config.modules.function.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..fe374f08b7829b846b2a1c2ac3ca500c
// 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 d679ab599dfd0bdbdc3ab5530d7fcd1c38baf7fa..6b4e2956222a0ca7f72f73032dc98878fbd257ee 100644
index de99652629c7301a4bc0e9ec48bed9a26a338f0d..b43445d8313c330a0376aaa4eb872495ebd87b5f 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 (fun.bm.lophine.config.modules.misc.ContainerExpansionConfig.barrelRows) {
+ private NonNullList<ItemStack> items = NonNullList.withSize(switch (fun.bm.lophine.config.modules.function.ContainerExpansionConfig.barrelRows) {
+ case 6 -> 54;
+ case 5 -> 45;
+ case 4 -> 36;
@@ -137,13 +137,13 @@ index d679ab599dfd0bdbdc3ab5530d7fcd1c38baf7fa..6b4e2956222a0ca7f72f73032dc98878
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 (fun.bm.lophine.config.modules.misc.ContainerExpansionConfig.barrelRows) {
+ return switch (fun.bm.lophine.config.modules.function.ContainerExpansionConfig.barrelRows) {
+ case 6 -> 54;
+ case 5 -> 45;
+ case 4 -> 36;
@@ -155,13 +155,13 @@ index d679ab599dfd0bdbdc3ab5530d7fcd1c38baf7fa..6b4e2956222a0ca7f72f73032dc98878
}
@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 (fun.bm.lophine.config.modules.misc.ContainerExpansionConfig.barrelRows) {
+ return switch (fun.bm.lophine.config.modules.function.ContainerExpansionConfig.barrelRows) {
+ case 6 -> ChestMenu.sixRows(id, player, this);
+ case 5 -> ChestMenu.fiveRows(id, player, this);
+ case 4 -> ChestMenu.fourRows(id, player, this);
@@ -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 2d163f3d5eb1af4d539d24c0256855bbec960de4..2cc0a7b5f088c29d9343b369b6e71c8a3ebdef75 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 c6c6432c2efeb7b95c2894b188cd966321ae3186..1cd9af71ee66df5f277748c0a5a59a30bd57accc 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()) {
@@ -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 f06b9d8ad9512d35f3f460eb77c869690321c67f..e17f9629369f8653b484b4cc71de4874b5faa4d6 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 c93feb64b6088a4878fcdb6e7c733a5f37e9385b..79c911003c7b1c3d94e7e9d3974eeadcc16bde3a 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()) {
@@ -5,7 +5,7 @@ Subject: [PATCH] Old zombie reinforcement
diff --git a/net/minecraft/world/entity/monster/Zombie.java b/net/minecraft/world/entity/monster/Zombie.java
index 14d0758aca54d9a25e369dfa8c89c930f27946c9..6f1f26b7f759e56f843e674167470da943785728 100644
index 14d0758aca54d9a25e369dfa8c89c930f27946c9..1324f2c8c0d6571bac36855f08354a84e0f08b70 100644
--- a/net/minecraft/world/entity/monster/Zombie.java
+++ b/net/minecraft/world/entity/monster/Zombie.java
@@ -341,7 +341,7 @@ public class Zombie extends Monster {
@@ -13,7 +13,7 @@ index 14d0758aca54d9a25e369dfa8c89c930f27946c9..6f1f26b7f759e56f843e674167470da9
int floor1 = Mth.floor(this.getY());
int floor2 = Mth.floor(this.getZ());
- EntityType<? extends Zombie> type = this.getType();
+ EntityType<? extends Zombie> type = fun.bm.lophine.config.modules.misc.OldFeatureConfig.oldZombieReinforcement ? EntityType.ZOMBIE : this.getType(); // Lophine - old zombie reinforcement
+ EntityType<? extends Zombie> type = fun.bm.lophine.config.modules.function.OldFeatureConfig.oldZombieReinforcement ? EntityType.ZOMBIE : this.getType(); // Lophine - old zombie reinforcement
Zombie zombie = type.create(level, EntitySpawnReason.REINFORCEMENT);
if (zombie == null) {
return true;
@@ -5,7 +5,7 @@ Subject: [PATCH] Spawn invulnerable time
diff --git a/net/minecraft/server/level/ServerPlayer.java b/net/minecraft/server/level/ServerPlayer.java
index b7bc75648634aefc549a69631ab6e401eb902e7d..0e2d575200f3ff3ec1600a0663152e3c37ea67b0 100644
index de1263404395c28209234a556347284fa557f81c..44f0e3a675c612e9ca2bc40cc3898bdbca0f6718 100644
--- a/net/minecraft/server/level/ServerPlayer.java
+++ b/net/minecraft/server/level/ServerPlayer.java
@@ -232,6 +232,7 @@ public class ServerPlayer extends Player implements ca.spottedleaf.moonrise.patc
@@ -24,12 +24,12 @@ index b7bc75648634aefc549a69631ab6e401eb902e7d..0e2d575200f3ff3ec1600a0663152e3c
if (this.invulnerableTime > 0) {
this.invulnerableTime--;
}
@@ -1431,6 +1433,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 (fun.bm.lophine.config.modules.misc.OldFeatureConfig.spawnInvulnerableTime) {
+ if (fun.bm.lophine.config.modules.function.OldFeatureConfig.spawnInvulnerableTime) {
+ if (this.spawnInvulnerableTime > 0 && !damageSource.is(net.minecraft.tags.DamageTypeTags.BYPASSES_INVULNERABILITY)) {
+ return false;
+ }
@@ -0,0 +1,824 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Bacteriawa <A3167717663@hotmail.com>
Date: Sat, 30 Aug 2025 17:16:32 +0800
Subject: [PATCH] Leaves: Leaves Fakeplayer
Co-authored by: violetc <58360096+s-yh-china@users.noreply.github.com>
As a part of : Leaves (https://github.com/LeavesMC/Leaves)
Licensed under: GPL-3.0 (https://www.gnu.org/licenses/gpl-3.0.html)
diff --git a/net/minecraft/advancements/critereon/SimpleCriterionTrigger.java b/net/minecraft/advancements/critereon/SimpleCriterionTrigger.java
index a82d84283632342bd30bc3449983431ba43583e0..f59526f6bfa1b4af5b474f0b438513c96afb491c 100644
--- a/net/minecraft/advancements/critereon/SimpleCriterionTrigger.java
+++ b/net/minecraft/advancements/critereon/SimpleCriterionTrigger.java
@@ -39,6 +39,7 @@ public abstract class SimpleCriterionTrigger<T extends SimpleCriterionTrigger.Si
}
protected void trigger(ServerPlayer player, Predicate<T> testTrigger) {
+ if (player instanceof org.leavesmc.leaves.bot.ServerBot) return; // Leaves - bot skip
PlayerAdvancements advancements = player.getAdvancements();
Set<CriterionTrigger.Listener<T>> set = (Set) advancements.criterionData.get(this); // Paper - fix PlayerAdvancements leak
if (set != null && !set.isEmpty()) {
diff --git a/net/minecraft/network/Connection.java b/net/minecraft/network/Connection.java
index 66ec0424a46dcd49cf44467357d80b1a2d84d3b2..04ae8de63af0a8abe578f14c8ef85fd4beca0969 100644
--- a/net/minecraft/network/Connection.java
+++ b/net/minecraft/network/Connection.java
@@ -96,7 +96,7 @@ public class Connection extends SimpleChannelInboundHandler<Packet<?>> {
@Nullable
private volatile PacketListener disconnectListener;
@Nullable
- private volatile PacketListener packetListener;
+ protected volatile PacketListener packetListener; // Leaves - private -> protected
@Nullable
private DisconnectionDetails disconnectionDetails;
private boolean encrypted;
diff --git a/net/minecraft/server/MinecraftServer.java b/net/minecraft/server/MinecraftServer.java
index 0b7c014114f1f8648824fe7c9b75fde3ea79162a..55d3b9a612943fda29716b9e7969b82e43947669 100644
--- a/net/minecraft/server/MinecraftServer.java
+++ b/net/minecraft/server/MinecraftServer.java
@@ -348,6 +348,8 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
}
// Folia end - regionised ticking
+ private org.leavesmc.leaves.bot.BotList botList; // Leaves - fakeplayer
+
public static <S extends MinecraftServer> S spin(Function<Thread, S> threadFunction) {
ca.spottedleaf.dataconverter.minecraft.datatypes.MCTypeRegistry.init(); // Paper - rewrite data converter system
AtomicReference<S> atomicReference = new AtomicReference<>();
@@ -1066,6 +1068,7 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
LOGGER.info("Stopping server");
Commands.COMMAND_SENDING_POOL.shutdownNow(); // Paper - Perf: Async command map building; Shutdown and don't bother finishing
+ this.getBotList().removeAll(); // Leaves - save or remove bot
// CraftBukkit start
if (this.server != null) {
if (false) this.server.spark.disable(); // Paper - spark // Luminol - Force disable builtin spark
@@ -1589,7 +1592,7 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
int i = this.pauseWhileEmptySeconds() * 20;
this.removeDisabledPluginsBlockingSleep(); // Paper - API to allow/disallow tick sleeping
if (false && i > 0) { // Folia - region threading - this is complicated to implement, and even if done correctly is messy
- if (this.playerList.getPlayerCount() == 0 && !this.tickRateManager.isSprinting() && this.pluginsBlockingSleep.isEmpty()) { // Paper - API to allow/disallow tick sleeping
+ if (this.playerList.getPlayerCount() == 0 && this.botList.bots.isEmpty() && !this.tickRateManager.isSprinting() && this.pluginsBlockingSleep.isEmpty()) { // Paper - API to allow/disallow tick sleeping // Leaves - fakeplayer
this.emptyTicks++;
} else {
this.emptyTicks = 0;
@@ -1913,6 +1916,7 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
public void tickConnection() {
this.getConnection().tick();
+ this.botList.networkTick(); // Leaves - fakeplayer
}
private void synchronizeTime(ServerLevel level) {
@@ -2991,6 +2995,16 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
return 0;
}
+ // Leaves start - fakeplayer
+ protected void setBotList(org.leavesmc.leaves.bot.BotList botList) {
+ this.botList = botList;
+ }
+
+ public org.leavesmc.leaves.bot.BotList getBotList() {
+ return botList;
+ }
+ // Leaves end - fakeplayer
+
public record ReloadableResources(CloseableResourceManager resourceManager, ReloadableServerResources managers) implements AutoCloseable {
@Override
public void close() {
diff --git a/net/minecraft/server/PlayerAdvancements.java b/net/minecraft/server/PlayerAdvancements.java
index fdeca41d40705f28864ce4443d01cd872c9d51b0..5c0e338dc1b0eb5724d10a73d6fc7975f9d2e5e5 100644
--- a/net/minecraft/server/PlayerAdvancements.java
+++ b/net/minecraft/server/PlayerAdvancements.java
@@ -167,6 +167,11 @@ public class PlayerAdvancements {
}
public boolean award(AdvancementHolder advancement, String criterionKey) {
+ // Leaves start - bot can't get advancement
+ if (player instanceof org.leavesmc.leaves.bot.ServerBot) {
+ return false;
+ }
+ // Leaves end - bot can't get advancement
boolean flag = false;
AdvancementProgress orStartProgress = this.getOrStartProgress(advancement);
boolean isDone = orStartProgress.isDone();
diff --git a/net/minecraft/server/commands/BanIpCommands.java b/net/minecraft/server/commands/BanIpCommands.java
index bb5dbfeb6915a808d6f70e332bf9f0a3f9b7d19a..62f23d47b55eb59956d69153d508b5c9ab544adf 100644
--- a/net/minecraft/server/commands/BanIpCommands.java
+++ b/net/minecraft/server/commands/BanIpCommands.java
@@ -44,6 +44,12 @@ public class BanIpCommands {
return banIp(source, username, reason);
} else {
ServerPlayer playerByName = source.getServer().getPlayerList().getPlayerByName(username);
+ // Leaves start - disable ban
+ if (playerByName instanceof org.leavesmc.leaves.bot.ServerBot) {
+ source.sendFailure(Component.literal("Permission denied"));
+ return 0;
+ }
+ // Leaves end - disable ban
if (playerByName != null) {
return banIp(source, playerByName.getIpAddress(), reason);
} else {
diff --git a/net/minecraft/server/commands/BanPlayerCommands.java b/net/minecraft/server/commands/BanPlayerCommands.java
index ac3ba9d0ea344fa189912d359b718fbe05e7aa49..61a4db144f721b47ac1000df72263bceb6e38ec0 100644
--- a/net/minecraft/server/commands/BanPlayerCommands.java
+++ b/net/minecraft/server/commands/BanPlayerCommands.java
@@ -44,8 +44,16 @@ public class BanPlayerCommands {
private static int banPlayers(CommandSourceStack source, Collection<GameProfile> gameProfiles, @Nullable Component reason) throws CommandSyntaxException {
UserBanList bans = source.getServer().getPlayerList().getBans();
int i = 0;
-
+ boolean hasBot = false; // Leaves - disable kick
for (GameProfile gameProfile : gameProfiles) {
+ // Leaves start - disable ban
+ if (gameProfile instanceof org.leavesmc.leaves.bot.BotList.CustomGameProfile) {
+ source.sendFailure(Component.literal("Permission denied"));
+ hasBot = true;
+ continue;
+ }
+ // Leaves end - disable ban
+ ServerPlayer player = source.getServer().getPlayerList().getPlayer(gameProfile.getId());
if (!bans.isBanned(gameProfile)) {
UserBanListEntry userBanListEntry = new UserBanListEntry(
gameProfile, null, source.getTextName(), null, reason == null ? null : reason.getString()
@@ -55,7 +63,6 @@ public class BanPlayerCommands {
source.sendSuccess(
() -> Component.translatable("commands.ban.success", Component.literal(gameProfile.getName()), userBanListEntry.getReason()), true
);
- ServerPlayer player = source.getServer().getPlayerList().getPlayer(gameProfile.getId());
if (player != null) {
player.connection.disconnect(Component.translatable("multiplayer.disconnect.banned"), org.bukkit.event.player.PlayerKickEvent.Cause.BANNED); // Paper - kick event cause
}
@@ -63,7 +70,13 @@ public class BanPlayerCommands {
}
if (i == 0) {
- throw ERROR_ALREADY_BANNED.create();
+ // Leaves start - disable kick
+ if (hasBot) {
+ return i;
+ } else {
+ throw ERROR_ALREADY_BANNED.create();
+ }
+ // Leaves end - disable kick
} else {
return i;
}
diff --git a/net/minecraft/server/commands/KickCommand.java b/net/minecraft/server/commands/KickCommand.java
index 14e2e0fcf20c8fa875bbefb97a673be4928d099a..8af40a77dc3da1599da2793168488f88686b8785 100644
--- a/net/minecraft/server/commands/KickCommand.java
+++ b/net/minecraft/server/commands/KickCommand.java
@@ -46,9 +46,17 @@ public class KickCommand {
if (!source.getServer().isPublished()) {
throw ERROR_SINGLEPLAYER.create();
} else {
+ boolean hasBot = false; // Leaves - disable kick
int i = 0;
for (ServerPlayer serverPlayer : players) {
+ // Leaves start - disable kick
+ if (serverPlayer instanceof org.leavesmc.leaves.bot.ServerBot) {
+ source.sendFailure(Component.literal("Permission denied"));
+ hasBot = true;
+ continue;
+ }
+ // Leaves end - disable kick
if (!source.getServer().isSingleplayerOwner(serverPlayer.getGameProfile())) {
serverPlayer.connection.disconnect(reason, org.bukkit.event.player.PlayerKickEvent.Cause.KICK_COMMAND); // Paper - kick event cause
source.sendSuccess(() -> Component.translatable("commands.kick.success", serverPlayer.getDisplayName(), reason), true);
@@ -57,7 +65,13 @@ public class KickCommand {
}
if (i == 0) {
- throw ERROR_KICKING_OWNER.create();
+ // Leaves start - disable kick
+ if (hasBot) {
+ return i;
+ } else {
+ throw ERROR_KICKING_OWNER.create();
+ }
+ // Leaves end - disable kick
} else {
return i;
}
diff --git a/net/minecraft/server/commands/OpCommand.java b/net/minecraft/server/commands/OpCommand.java
index f2286b96b8f40b4588f817913c42ae7b4a92340f..e6c7bbb023000b9de90c1256274ff5aba4a6478a 100644
--- a/net/minecraft/server/commands/OpCommand.java
+++ b/net/minecraft/server/commands/OpCommand.java
@@ -43,6 +43,7 @@ public class OpCommand {
int i = 0;
for (GameProfile gameProfile : gameProfiles) {
+ if (gameProfile instanceof org.leavesmc.leaves.bot.BotList.CustomGameProfile) continue; // Leaves - disable op
if (!playerList.isOp(gameProfile)) {
playerList.op(gameProfile);
i++;
diff --git a/net/minecraft/server/dedicated/DedicatedServer.java b/net/minecraft/server/dedicated/DedicatedServer.java
index 79c911003c7b1c3d94e7e9d3974eeadcc16bde3a..328428f0ef8a2305f661c167e6fe1f604b0d6890 100644
--- a/net/minecraft/server/dedicated/DedicatedServer.java
+++ b/net/minecraft/server/dedicated/DedicatedServer.java
@@ -162,6 +162,7 @@ public class DedicatedServer extends MinecraftServer implements ServerInterface
}
// Spigot start
+ this.setBotList(new org.leavesmc.leaves.bot.BotList(this)); // Leaves - fakeplayer
this.setPlayerList(new DedicatedPlayerList(this, this.registries(), this.playerDataStorage));
org.spigotmc.SpigotConfig.init((java.io.File) this.options.valueOf("spigot-settings"));
org.spigotmc.SpigotConfig.registerCommands();
@@ -173,6 +174,7 @@ public class DedicatedServer extends MinecraftServer implements ServerInterface
// 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
+ this.getBotList().loadBotInfo(); // Leaves - load resident bot info
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()) {
diff --git a/net/minecraft/server/level/ChunkMap.java b/net/minecraft/server/level/ChunkMap.java
index 70740381c6501c1a518c52b24381edd16792507f..5e31b499a894113b4be1982a1071348fcbaf9ded 100644
--- a/net/minecraft/server/level/ChunkMap.java
+++ b/net/minecraft/server/level/ChunkMap.java
@@ -134,7 +134,7 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
public final AtomicInteger tickingGenerated = new AtomicInteger(); // Paper - public
private final String storageName;
//private final PlayerMap playerMap = new PlayerMap(); // Folia - region threading
- //public final Int2ObjectMap<ChunkMap.TrackedEntity> entityMap = new Int2ObjectOpenHashMap<>(); // Folia - region threading
+ public final Int2ObjectMap<ChunkMap.TrackedEntity> entityMap = new Int2ObjectOpenHashMap<>(); // Folia - region threading
private final Long2ByteMap chunkTypeCache = new Long2ByteOpenHashMap();
// Paper - rewrite chunk system
public int serverViewDistance;
@@ -1309,6 +1309,13 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
}
} else if (this.seenBy.remove(player.connection)) {
this.serverEntity.removePairing(player);
+ // Leaves start - render bot
+ if (entity instanceof org.leavesmc.leaves.bot.ServerBot bot) {
+ if (bot.needSendFakeData(player)) {
+ bot.sendFakeData(player.connection, false);
+ }
+ }
+ // Leaves end - render bot
}
}
}
diff --git a/net/minecraft/server/level/ServerLevel.java b/net/minecraft/server/level/ServerLevel.java
index 576dde41a4b48aac9079cca902f4371a31470af3..952b92c55d7192791a19ecebe80894f6cf992049 100644
--- a/net/minecraft/server/level/ServerLevel.java
+++ b/net/minecraft/server/level/ServerLevel.java
@@ -207,6 +207,7 @@ public class ServerLevel extends Level implements ServerEntityGetter, WorldGenLe
private final StructureCheck structureCheck;
public final boolean tickTime; // Folia - region threading
private final RandomSequences randomSequences;
+ final List<ServerPlayer> realPlayers; // Leaves - skip
// CraftBukkit start
public final LevelStorageSource.LevelStorageAccess levelStorageAccess;
@@ -635,6 +636,7 @@ public class ServerLevel extends Level implements ServerEntityGetter, WorldGenLe
// Paper end - rewrite chunk system
this.getCraftServer().addWorld(this.getWorld()); // CraftBukkit
this.updateTickData(); // Folia - region threading - make sure it is initialised before ticked
+ this.realPlayers = Lists.newArrayList(); // Leaves - skip
}
// Folia start - region threading
@@ -2389,6 +2391,12 @@ public class ServerLevel extends Level implements ServerEntityGetter, WorldGenLe
return this.players;
}
+ // Leaves start - fakeplayer skip
+ public List<ServerPlayer> realPlayers() {
+ return this.realPlayers;
+ }
+ // Leaves end - fakeplayer skip
+
@Override
public void updatePOIOnBlockStateChange(BlockPos pos, BlockState oldState, BlockState newState) {
Optional<Holder<PoiType>> optional = PoiTypes.forState(oldState);
@@ -2826,6 +2834,11 @@ public class ServerLevel extends Level implements ServerEntityGetter, WorldGenLe
// ServerLevel.this.getChunkSource().addEntity(entity); // Paper - ignore and warn about illegal addEntity calls instead of crashing server; moved down below valid=true
if (entity instanceof ServerPlayer serverPlayer) {
ServerLevel.this.players.add(serverPlayer);
+ // Leaves start - skip
+ if (!(serverPlayer instanceof org.leavesmc.leaves.bot.ServerBot)) {
+ ServerLevel.this.realPlayers.add(serverPlayer);
+ }
+ // Leaves end - skip
if (serverPlayer.isReceivingWaypoints()) {
ServerLevel.this.getWaypointManager().addPlayer(serverPlayer);
}
@@ -2913,6 +2926,11 @@ public class ServerLevel extends Level implements ServerEntityGetter, WorldGenLe
ServerLevel.this.getChunkSource().removeEntity(entity);
if (entity instanceof ServerPlayer serverPlayer) {
ServerLevel.this.players.remove(serverPlayer);
+ // Leaves start - skip
+ if (!(serverPlayer instanceof org.leavesmc.leaves.bot.ServerBot)) {
+ ServerLevel.this.realPlayers.remove(serverPlayer);
+ }
+ // Leaves end - skip
ServerLevel.this.getWaypointManager().removePlayer(serverPlayer);
ServerLevel.this.updateSleepingPlayerList();
}
diff --git a/net/minecraft/server/level/ServerPlayer.java b/net/minecraft/server/level/ServerPlayer.java
index 44f0e3a675c612e9ca2bc40cc3898bdbca0f6718..4b2dba8b30b72fb3acc00957b3e19d6fc178c99e 100644
--- a/net/minecraft/server/level/ServerPlayer.java
+++ b/net/minecraft/server/level/ServerPlayer.java
@@ -219,7 +219,7 @@ public class ServerPlayer extends Player implements ca.spottedleaf.moonrise.patc
private static final boolean DEFAULT_SPAWN_EXTRA_PARTICLES_ON_FALL = false;
public ServerGamePacketListenerImpl connection;
private final MinecraftServer server;
- public final ServerPlayerGameMode gameMode;
+ public ServerPlayerGameMode gameMode; // Leaves - not final
private final PlayerAdvancements advancements;
private final ServerStatsCounter stats;
private float lastRecordedHealthAndAbsorption = Float.MIN_VALUE;
@@ -232,7 +232,7 @@ public class ServerPlayer extends Player implements ca.spottedleaf.moonrise.patc
private int lastSentFood = -99999999;
private boolean lastFoodSaturationZero = true;
public int lastSentExp = -99999999;
- private int spawnInvulnerableTime = 60; // Lophine - spawn invulnerable time
+ public int spawnInvulnerableTime = 60; // Lophine - spawn invulnerable time
private ChatVisiblity chatVisibility = ChatVisiblity.FULL;
public ParticleStatus particleStatus = ParticleStatus.ALL;
private boolean canChatColor = true;
@@ -241,7 +241,7 @@ public class ServerPlayer extends Player implements ca.spottedleaf.moonrise.patc
private Entity camera;
public boolean isChangingDimension;
public boolean seenCredits = false;
- private final ServerRecipeBook recipeBook;
+ protected ServerRecipeBook recipeBook; // Leaves - not final and private -> protected
@Nullable
private Vec3 levitationStartPos;
private int levitationStartTime;
@@ -436,6 +436,10 @@ public class ServerPlayer extends Player implements ca.spottedleaf.moonrise.patc
// Paper start - rewrite chunk system
private ca.spottedleaf.moonrise.patches.chunk_system.player.RegionizedPlayerChunkLoader.PlayerChunkLoaderData chunkLoader;
private final ca.spottedleaf.moonrise.patches.chunk_system.player.RegionizedPlayerChunkLoader.ViewDistanceHolder viewDistanceHolder = new ca.spottedleaf.moonrise.patches.chunk_system.player.RegionizedPlayerChunkLoader.ViewDistanceHolder();
+ // Leaves start - player operation limiter
+ private int instaBreakCountPerTick = 0;
+ private int placeBlockCountPerTick = 0;
+ // Leaves end - player operation limiter
@Override
public final boolean moonrise$isRealPlayer() {
@@ -924,6 +928,7 @@ public class ServerPlayer extends Player implements ca.spottedleaf.moonrise.patc
}
// CraftBukkit end
this.tickClientLoadTimeout();
+ this.resetOperationCountPerTick(); // Leaves - player operation limiter
this.gameMode.tick();
this.wardenSpawnTracker.tick();
if (this.spawnInvulnerableTime > 0) --this.spawnInvulnerableTime; // Lophine - spawn invulnerable time
@@ -2029,6 +2034,11 @@ public class ServerPlayer extends Player implements ca.spottedleaf.moonrise.patc
this.lastSentHealth = -1.0F;
this.lastSentFood = -1;
this.teleportSpectators(teleportTransition, serverLevel);
+ // Leaves start - bot support
+ if (fun.bm.lophine.config.modules.function.FakeplayerConfig.enable) {
+ this.server.getBotList().bots.forEach(bot -> bot.sendFakeDataIfNeed(this, true)); // Leaves - render bot
+ }
+ // Leaves end - bot support
// CraftBukkit start
org.bukkit.event.player.PlayerChangedWorldEvent changeEvent = new org.bukkit.event.player.PlayerChangedWorldEvent(this.getBukkitEntity(), serverLevel.getWorld());
this.level().getCraftServer().getPluginManager().callEvent(changeEvent);
@@ -3577,4 +3587,31 @@ public class ServerPlayer extends Player implements ca.spottedleaf.moonrise.patc
return (org.bukkit.craftbukkit.entity.CraftPlayer) super.getBukkitEntity();
}
// CraftBukkit end
+
+ // Leaves start - player operation limiter
+ protected void resetOperationCountPerTick() {
+ instaBreakCountPerTick = 0;
+ placeBlockCountPerTick = 0;
+ }
+
+ public int getInstaBreakCountPerTick() {
+ return instaBreakCountPerTick;
+ }
+
+ public int getPlaceBlockCountPerTick() {
+ return placeBlockCountPerTick;
+ }
+
+ public void addInstaBreakCountPerTick() {
+ ++instaBreakCountPerTick;
+ }
+
+ public void addPlaceBlockCountPerTick() {
+ ++placeBlockCountPerTick;
+ }
+
+ public boolean allowOperation() {
+ return (instaBreakCountPerTick == 0 || placeBlockCountPerTick == 0) && (instaBreakCountPerTick <= 1 && placeBlockCountPerTick <= 2);
+ }
+ // Leaves end - player operation limiter
}
diff --git a/net/minecraft/server/players/GameProfileCache.java b/net/minecraft/server/players/GameProfileCache.java
index 144a2644c15f276f02bb3be859dc5d05a677ac55..c6a89e8936cffa49b3de153ed3c40e3cc9b8ae61 100644
--- a/net/minecraft/server/players/GameProfileCache.java
+++ b/net/minecraft/server/players/GameProfileCache.java
@@ -125,6 +125,12 @@ public class GameProfileCache {
// Paper end
public Optional<GameProfile> get(String name) {
+ // Leaves start - fix bot
+ org.leavesmc.leaves.bot.ServerBot bot = org.leavesmc.leaves.bot.BotList.INSTANCE.getBotByName(name);
+ if (bot != null) {
+ return Optional.of(bot.getGameProfile());
+ }
+ // Leaves end - fix bot
String string = name.toLowerCase(Locale.ROOT);
boolean stateLocked = true; try { this.stateLock.lock(); // Paper - Fix GameProfileCache concurrency
GameProfileCache.GameProfileInfo gameProfileInfo = this.profilesByName.get(string);
diff --git a/net/minecraft/server/players/PlayerList.java b/net/minecraft/server/players/PlayerList.java
index cb863a36fc67e993956ec3be655e6e4b9764c051..908ec29a6e285122d1a8a43dc547b74af2697442 100644
--- a/net/minecraft/server/players/PlayerList.java
+++ b/net/minecraft/server/players/PlayerList.java
@@ -425,6 +425,19 @@ public abstract class PlayerList {
org.leavesmc.leaves.protocol.core.LeavesProtocolManager.handlePlayerJoin(player);
+ // Leaves start - bot support
+ if (fun.bm.lophine.config.modules.function.FakeplayerConfig.enable) {
+ org.leavesmc.leaves.bot.ServerBot bot = this.server.getBotList().getBotByName(player.getScoreboardName());
+ if (bot != null) {
+ this.server.getBotList().removeBot(bot, org.leavesmc.leaves.event.bot.BotRemoveEvent.RemoveReason.INTERNAL, player.getBukkitEntity(), false);
+ }
+ this.server.getBotList().bots.forEach(bot1 -> {
+ bot1.sendPlayerInfo(player);
+ bot1.sendFakeDataIfNeed(player, true);
+ }); // Leaves - render bot
+ }
+ // Leaves end - bot support
+
final net.kyori.adventure.text.Component jm = playerJoinEvent.joinMessage();
if (jm != null && !jm.equals(net.kyori.adventure.text.Component.empty())) { // Paper - Adventure
@@ -922,6 +935,12 @@ public abstract class PlayerList {
}
// Paper end - Add PlayerPostRespawnEvent
+ // Leaves start - bot support
+ if (fun.bm.lophine.config.modules.function.FakeplayerConfig.enable) {
+ this.server.getBotList().bots.forEach(bot -> bot.sendFakeDataIfNeed(serverPlayer, true)); // Leaves - render bot
+ }
+ // Leaves end - bot support
+
// CraftBukkit end
return serverPlayer;
@@ -1026,11 +1045,16 @@ public abstract class PlayerList {
public String[] getPlayerNamesArray() {
List<ServerPlayer> players = new java.util.ArrayList<>(this.players); // Folia - region threading
- String[] strings = new String[this.players.size()];
+ String[] strings = new String[this.players.size() + this.server.getBotList().bots.size()]; // Leaves - fakeplayer support
for (int i = 0; i < players.size(); i++) { // Folia - region threading
strings[i] = players.get(i).getGameProfile().getName(); // Folia - region threading
}
+ // Leaves start - fakeplayer support
+ for (int i = this.players.size(); i < strings.length; ++i) {
+ strings[i] = this.server.getBotList().bots.get(i - this.players.size()).getGameProfile().getName();
+ }
+ // Leaves end - fakeplayer support
return strings;
}
@@ -1141,7 +1165,14 @@ public abstract class PlayerList {
@Nullable
public ServerPlayer getPlayerByName(String username) {
- return this.playersByName.get(username.toLowerCase(java.util.Locale.ROOT)); // Spigot
+ // Leaves start - fakeplayer support
+ username = username.toLowerCase(java.util.Locale.ROOT);
+ ServerPlayer player = this.playersByName.get(username);
+ if (player == null) {
+ player = this.server.getBotList().getBotByName(username);
+ }
+ return player; // Spigot
+ // Leaves end - fakeplayer support
}
public void broadcast(@Nullable Player except, double x, double y, double z, double radius, ResourceKey<Level> dimension, Packet<?> packet) {
@@ -1482,7 +1513,13 @@ public abstract class PlayerList {
@Nullable
public ServerPlayer getPlayer(UUID playerUUID) {
- return this.playersByUUID.get(playerUUID);
+ // Leaves start - fakeplayer support
+ ServerPlayer player = this.playersByUUID.get(playerUUID);
+ if (player == null) {
+ player = this.server.getBotList().getBot(playerUUID);
+ }
+ return player;
+ // Leaves start - fakeplayer support
}
public boolean canBypassPlayerLimit(GameProfile profile) {
diff --git a/net/minecraft/server/waypoints/ServerWaypointManager.java b/net/minecraft/server/waypoints/ServerWaypointManager.java
index 52fd730998535ea071bfc99b7cc2c254b9b656d7..0abf6f0265fcb916f2c2c76fb1313bd13444f4ad 100644
--- a/net/minecraft/server/waypoints/ServerWaypointManager.java
+++ b/net/minecraft/server/waypoints/ServerWaypointManager.java
@@ -24,6 +24,11 @@ public class ServerWaypointManager implements WaypointManager<WaypointTransmitte
public void trackWaypoint(WaypointTransmitter waypoint) {
// Lophine start - unsafe waypoint bar
if (!fun.bm.lophine.config.modules.experiment.CommandConfig.waypoint) return;
+ // Leaves start - fakeplayer
+ if (waypoint instanceof org.leavesmc.leaves.bot.ServerBot bot && !bot.getConfigValue(org.leavesmc.leaves.bot.agent.Configs.ENABLE_LOCATOR_BAR)) {
+ return;
+ }
+ // Leaves end - fakeplayer
this.waypoints.add(waypoint);
for (ServerPlayer serverPlayer : this.players) {
@@ -59,6 +64,11 @@ public class ServerWaypointManager implements WaypointManager<WaypointTransmitte
// Folia - region threading
// Lophine start - unsafe waypoint bar
if (!fun.bm.lophine.config.modules.experiment.CommandConfig.waypoint) return;
+ // Leaves start - fakeplayer
+ if (player instanceof org.leavesmc.leaves.bot.ServerBot) {
+ return;
+ }
+ // Leaves end - fakeplayer
this.players.add(player);
for (WaypointTransmitter waypointTransmitter : this.waypoints) {
diff --git a/net/minecraft/world/entity/Entity.java b/net/minecraft/world/entity/Entity.java
index bfd29fc6a5af3cd28a462ce97bbc04634402b74c..319789f635b8b6bba2e8843c7fb38ef1fdf51677 100644
--- a/net/minecraft/world/entity/Entity.java
+++ b/net/minecraft/world/entity/Entity.java
@@ -1225,7 +1225,7 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess
BlockPos onPosLegacy = this.getOnPosLegacy();
BlockState blockState = this.level().getBlockState(onPosLegacy);
- if (this.isLocalInstanceAuthoritative()) {
+ if (this.isLocalInstanceAuthoritative() || this instanceof org.leavesmc.leaves.bot.ServerBot) { // Leaves - ServerBot needs check fall damage
this.checkFallDamage(vec3.y, this.onGround(), blockState, onPosLegacy);
}
@@ -1539,7 +1539,7 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess
}
// Paper start - optimise collisions
- private Vec3 collide(Vec3 movement) {
+ public Vec3 collide(Vec3 movement) { // Leaves - private -> public
final boolean xZero = movement.x == 0.0;
final boolean yZero = movement.y == 0.0;
final boolean zZero = movement.z == 0.0;
diff --git a/net/minecraft/world/entity/LivingEntity.java b/net/minecraft/world/entity/LivingEntity.java
index f4f4f886b6558d39d292a45665953f497f84be53..12466e0294123a5922ee7694984d4d277a390ddd 100644
--- a/net/minecraft/world/entity/LivingEntity.java
+++ b/net/minecraft/world/entity/LivingEntity.java
@@ -3212,7 +3212,7 @@ public abstract class LivingEntity extends Entity implements Attackable, Waypoin
private void travelRidden(Player player, Vec3 travelVector) {
Vec3 riddenInput = this.getRiddenInput(player, travelVector);
this.tickRidden(player, riddenInput);
- if (this.canSimulateMovement()) {
+ if (this.canSimulateMovement() || this.getControllingPassenger() instanceof org.leavesmc.leaves.bot.ServerBot) { // Leaves - Fakeplayer
this.setSpeed(this.getRiddenSpeed(player));
this.travel(riddenInput);
} else {
@@ -4062,7 +4062,7 @@ public abstract class LivingEntity extends Entity implements Attackable, Waypoin
// Paper start - lag compensate eating
// we add 1 to the expected time to avoid lag compensating when we should not
final boolean shouldLagCompensate = this.useItem.has(DataComponents.FOOD) && this.eatStartTime != -1 && (System.nanoTime() - this.eatStartTime) > ((1L + this.totalEatTimeTicks) * 50L * (1000L * 1000L));
- if ((--this.useItemRemaining == 0 || shouldLagCompensate) && !this.level().isClientSide && !usingItem.useOnRelease()) {
+ if ((--this.useItemRemaining == 0 || shouldLagCompensate) && !(this instanceof org.leavesmc.leaves.bot.ServerBot) && !this.level().isClientSide && !usingItem.useOnRelease()) { // Leaves - Fakeplayer skip this check
this.useItemRemaining = 0;
// Paper end - lag compensate eating
this.completeUsingItem();
@@ -4239,6 +4239,23 @@ public abstract class LivingEntity extends Entity implements Attackable, Waypoin
this.stopUsingItem();
}
+ // Leaves start - Fakeplayer
+ public boolean releaseUsingItemWithResult() {
+ ItemStack itemInHand = this.getItemInHand(this.getUsedItemHand());
+ boolean result = false;
+ if (!this.useItem.isEmpty() && ItemStack.isSameItem(itemInHand, this.useItem)) {
+ this.useItem = itemInHand;
+ result = this.useItem.releaseUsingWithResult(this.level(), this, this.getUseItemRemainingTicks());
+ if (this.useItem.useOnRelease()) {
+ this.updatingUsingItem();
+ }
+ }
+
+ this.stopUsingItem();
+ return result;
+ }
+ // Leaves end - Fakeplayer
+
public void stopUsingItem() {
if (!this.level().isClientSide) {
boolean isUsingItem = this.isUsingItem();
diff --git a/net/minecraft/world/entity/player/Player.java b/net/minecraft/world/entity/player/Player.java
index 6c933c27ce155eb516a9ede96fb2c3bd19251bfa..6e0784af90aab5c25100613f97d4492fa7d1b277 100644
--- a/net/minecraft/world/entity/player/Player.java
+++ b/net/minecraft/world/entity/player/Player.java
@@ -205,7 +205,7 @@ public abstract class Player extends LivingEntity {
private int lastLevelUpTime;
public GameProfile gameProfile;
private boolean reducedDebugInfo;
- private ItemStack lastItemInMainHand = ItemStack.EMPTY;
+ protected ItemStack lastItemInMainHand = ItemStack.EMPTY;
private final ItemCooldowns cooldowns = this.createItemCooldowns();
private Optional<GlobalPos> lastDeathLocation = Optional.empty();
@Nullable
@@ -412,6 +412,12 @@ public abstract class Player extends LivingEntity {
}
}
+ // Leaves start - fakeplayer
+ protected void livingEntityTick() {
+ super.tick();
+ }
+ // Leaves end - fakeplayer
+
@Override
protected float getMaxHeadRotationRelativeToBody() {
return this.isBlocking() ? 15.0F : super.getMaxHeadRotationRelativeToBody();
@@ -714,7 +720,7 @@ public abstract class Player extends LivingEntity {
}
}
- private void touch(Entity entity) {
+ public void touch(Entity entity) { // Leaves - private -> public
entity.playerTouch(this);
}
@@ -1339,7 +1345,7 @@ public abstract class Player extends LivingEntity {
this.sweepAttack();
}
- if (target instanceof ServerPlayer && target.hurtMarked) {
+ if ((target instanceof ServerPlayer && !(target instanceof org.leavesmc.leaves.bot.ServerBot)) && target.hurtMarked) { // Leaves - bot knockback
// CraftBukkit start - Add Velocity Event
boolean cancelled = false;
org.bukkit.entity.Player player = (org.bukkit.entity.Player) target.getBukkitEntity();
diff --git a/net/minecraft/world/entity/projectile/FishingHook.java b/net/minecraft/world/entity/projectile/FishingHook.java
index 7760ab1fe365b4bf1ab6c2ff953e8659a28008a8..76f6b0932633bc5670a1cdc76ff78b3bc725401d 100644
--- a/net/minecraft/world/entity/projectile/FishingHook.java
+++ b/net/minecraft/world/entity/projectile/FishingHook.java
@@ -58,7 +58,7 @@ public class FishingHook extends Projectile {
public static final EntityDataAccessor<Integer> DATA_HOOKED_ENTITY = SynchedEntityData.defineId(FishingHook.class, EntityDataSerializers.INT);
private static final EntityDataAccessor<Boolean> DATA_BITING = SynchedEntityData.defineId(FishingHook.class, EntityDataSerializers.BOOLEAN);
private int life;
- private int nibble;
+ public int nibble; // Leaves - private -> public
public int timeUntilLured;
public int timeUntilHooked;
public float fishAngle;
diff --git a/net/minecraft/world/entity/vehicle/AbstractBoat.java b/net/minecraft/world/entity/vehicle/AbstractBoat.java
index 91ac54fb0ae9a06945476cbd1caa20be32c4a9fe..be380db0cb2f243b4a3cf8bd1b06d38debc5f6fd 100644
--- a/net/minecraft/world/entity/vehicle/AbstractBoat.java
+++ b/net/minecraft/world/entity/vehicle/AbstractBoat.java
@@ -271,6 +271,11 @@ public abstract class AbstractBoat extends VehicleEntity implements Leashable {
}
this.move(MoverType.SELF, this.getDeltaMovement());
+ } else if (this.getControllingPassenger() instanceof org.leavesmc.leaves.bot.ServerBot) { // Leaves start - Fakeplayer
+ this.floatBoat();
+ this.controlBoat();
+ this.move(MoverType.SELF, this.getDeltaMovement());
+ // Leaves end - Fakeplayer
} else {
this.setDeltaMovement(Vec3.ZERO);
}
@@ -383,6 +388,13 @@ public abstract class AbstractBoat extends VehicleEntity implements Leashable {
}
}
+ // Leaves start - Fakeplayer
+ @Override
+ public boolean canSimulateMovement() {
+ return super.canSimulateMovement() || this.getControllingPassenger() instanceof org.leavesmc.leaves.bot.ServerBot;
+ }
+ // Leaves end - Fakeplayer
+
@Nullable
protected SoundEvent getPaddleSound() {
return switch (this.getStatus()) {
diff --git a/net/minecraft/world/inventory/AbstractContainerMenu.java b/net/minecraft/world/inventory/AbstractContainerMenu.java
index bdc0ca1f625dc264bef6e5722ec66121ba48625a..525610be29d5077a3199287347b1b71cf9fd8abf 100644
--- a/net/minecraft/world/inventory/AbstractContainerMenu.java
+++ b/net/minecraft/world/inventory/AbstractContainerMenu.java
@@ -407,6 +407,7 @@ public abstract class AbstractContainerMenu {
private void doClick(int slotId, int button, ClickType clickType, Player player) {
Inventory inventory = player.getInventory();
+ if (!doClickCheck(slotId, button, clickType, player)) return; // Leaves - doClick check
if (clickType == ClickType.QUICK_CRAFT) {
int i = this.quickcraftStatus;
this.quickcraftStatus = getQuickcraftHeader(button);
@@ -685,6 +686,22 @@ public abstract class AbstractContainerMenu {
}
}
+ // Leaves start - doClick check
+ private boolean doClickCheck(int slotIndex, int button, ClickType actionType, Player player) {
+ if (slotIndex < 0) {
+ return true;
+ }
+
+ Slot slot = getSlot(slotIndex);
+ ItemStack itemStack = slot.getItem();
+ net.minecraft.world.item.component.CustomData customData = itemStack.get(net.minecraft.core.component.DataComponents.CUSTOM_DATA);
+ if (customData != null && customData.contains("Leaves.Gui.Placeholder")) {
+ return !customData.copyTag().getBoolean("Leaves.Gui.Placeholder").orElse(false);
+ }
+ return true;
+ }
+ // Leaves end - doClick check
+
private boolean tryItemClickBehaviourOverride(Player player, ClickAction action, Slot slot, ItemStack clickedItem, ItemStack carriedItem) {
FeatureFlagSet featureFlagSet = player.level().enabledFeatures();
return carriedItem.isItemEnabled(featureFlagSet) && carriedItem.overrideStackedOnOther(slot, action, player)
diff --git a/net/minecraft/world/item/ItemStack.java b/net/minecraft/world/item/ItemStack.java
index a27e1d53abc2d8b8912226add33510067e10ed52..be0a267e5564a30c16f408cb311a448f354d60b4 100644
--- a/net/minecraft/world/item/ItemStack.java
+++ b/net/minecraft/world/item/ItemStack.java
@@ -450,7 +450,7 @@ public final class ItemStack implements DataComponentHolder, ChangePublisher<net
placeEvent = org.bukkit.craftbukkit.event.CraftEventFactory.callBlockPlaceEvent(serverLevel, player, hand, blocks.getFirst(), clickedPos);
}
- if (placeEvent != null && (placeEvent.isCancelled() || !placeEvent.canBuild())) {
+ if (placeEvent != null && (placeEvent.isCancelled() || !placeEvent.canBuild()) && (!(player instanceof org.leavesmc.leaves.bot.ServerBot))) { // Leaves - Fakeplayer skip this check
interactionResult = InteractionResult.FAIL; // cancel placement
// PAIL: Remove this when MC-99075 fixed
player.containerMenu.forceHeldSlot(hand);
@@ -946,6 +946,20 @@ public final class ItemStack implements DataComponentHolder, ChangePublisher<net
}
}
+ // Leaves start - Fakeplayer
+ public boolean releaseUsingWithResult(Level level, LivingEntity livingEntity, int timeLeft) {
+ ItemStack itemStack = this.copy();
+ if (this.getItem().releaseUsing(this, level, livingEntity, timeLeft)) {
+ ItemStack itemStack1 = this.applyAfterUseComponentSideEffects(livingEntity, itemStack);
+ if (itemStack1 != this) {
+ livingEntity.setItemInHand(livingEntity.getUsedItemHand(), itemStack1);
+ }
+ return true;
+ }
+ return false;
+ }
+ // Leaves end - Fakeplayer
+
public boolean useOnRelease() {
return this.getItem().useOnRelease(this);
}
diff --git a/net/minecraft/world/level/block/piston/PistonMovingBlockEntity.java b/net/minecraft/world/level/block/piston/PistonMovingBlockEntity.java
index bff9574bece5f5581d9cc51a3f2b68b5c13c25c9..4bfe53c15c9b2effd24dddc48565efc9c102e56a 100644
--- a/net/minecraft/world/level/block/piston/PistonMovingBlockEntity.java
+++ b/net/minecraft/world/level/block/piston/PistonMovingBlockEntity.java
@@ -145,7 +145,7 @@ public class PistonMovingBlockEntity extends BlockEntity {
break;
}
- if (!(entity instanceof ServerPlayer)) {
+ if (!(entity instanceof ServerPlayer) || (entity instanceof org.leavesmc.leaves.bot.ServerBot)) { // Leaves - bot slime block
Vec3 deltaMovement = entity.getDeltaMovement();
double d1 = deltaMovement.x;
double d2 = deltaMovement.y;
diff --git a/net/minecraft/world/level/levelgen/PhantomSpawner.java b/net/minecraft/world/level/levelgen/PhantomSpawner.java
index ecfd6a04ea1bac75bbb7e88554b73fc67e7f7358..2c9a55eecf44eaa62ef57597a94d657ad0b9cfc4 100644
--- a/net/minecraft/world/level/levelgen/PhantomSpawner.java
+++ b/net/minecraft/world/level/levelgen/PhantomSpawner.java
@@ -49,6 +49,11 @@ public class PhantomSpawner implements CustomSpawner {
ServerStatsCounter stats = serverPlayer.getStats();
int i = Mth.clamp(stats.getValue(Stats.CUSTOM.get(Stats.TIME_SINCE_REST)), 1, Integer.MAX_VALUE);
int i1 = 24000;
+ // Leaves start - fakeplayer spawn
+ if (serverPlayer instanceof org.leavesmc.leaves.bot.ServerBot bot && bot.getConfigValue(org.leavesmc.leaves.bot.agent.Configs.SPAWN_PHANTOM)) {
+ i1 = Math.max(bot.notSleepTicks, 1);
+ }
+ // Leaves end - fakeplayer spawn
if (randomSource.nextInt(i) >= 72000) {
BlockPos blockPos1 = blockPos.above(20 + randomSource.nextInt(15))
.east(-10 + randomSource.nextInt(21))
diff --git a/net/minecraft/world/level/storage/LevelResource.java b/net/minecraft/world/level/storage/LevelResource.java
index bef794c3f58c41d910aa0bcc63fbdeea7225fddf..a601da588e6973cc5b87d3e3eeba49b53f6d9a6d 100644
--- a/net/minecraft/world/level/storage/LevelResource.java
+++ b/net/minecraft/world/level/storage/LevelResource.java
@@ -15,7 +15,7 @@ public class LevelResource {
public static final LevelResource ROOT = new LevelResource(".");
private final String id;
- private LevelResource(String id) {
+ public LevelResource(String id) { // Leaves - private -> public
this.id = id;
}
diff --git a/net/minecraft/world/level/storage/PlayerDataStorage.java b/net/minecraft/world/level/storage/PlayerDataStorage.java
index fe44d8d17d2622b3d6021c11579af85ef96737bb..0aae211dc2048f8cd14213c2a868394d1ed16070 100644
--- a/net/minecraft/world/level/storage/PlayerDataStorage.java
+++ b/net/minecraft/world/level/storage/PlayerDataStorage.java
@@ -19,7 +19,7 @@ import net.minecraft.util.datafix.DataFixTypes;
import net.minecraft.world.entity.player.Player;
import org.slf4j.Logger;
-public class PlayerDataStorage {
+public class PlayerDataStorage implements org.leavesmc.leaves.bot.IPlayerDataStorage {
private static final Logger LOGGER = LogUtils.getLogger();
private final File playerDir;
protected final DataFixer fixerUpper;
File diff suppressed because it is too large Load Diff
@@ -1,11 +1,14 @@
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 2f41a92465b9da28e026297cc3528898bb1c8412..fb5300ec1bd19b3dfd6a2b4106440b9f73c48205 100644
index 2f41a92465b9da28e026297cc3528898bb1c8412..168301a2f1c3081ae15d3695c2dcc72b4538cdfb 100644
--- a/src/main/java/org/bukkit/craftbukkit/inventory/CraftContainer.java
+++ b/src/main/java/org/bukkit/craftbukkit/inventory/CraftContainer.java
@@ -148,9 +148,26 @@ public class CraftContainer extends AbstractContainerMenu {
@@ -15,7 +18,7 @@ index 2f41a92465b9da28e026297cc3528898bb1c8412..fb5300ec1bd19b3dfd6a2b4106440b9f
- 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 (fun.bm.lophine.config.modules.misc.ContainerExpansionConfig.enderchestRows) {
+ this.delegate = new ChestMenu(switch (fun.bm.lophine.config.modules.function.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 2f41a92465b9da28e026297cc3528898bb1c8412..fb5300ec1bd19b3dfd6a2b4106440b9f
+ }, windowId, bottom, top, top.getContainerSize() / 9);
break;
+ case BARREL:
+ this.delegate = new ChestMenu(switch (fun.bm.lophine.config.modules.misc.ContainerExpansionConfig.barrelRows) {
+ this.delegate = new ChestMenu(switch (fun.bm.lophine.config.modules.function.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 5d05a8a843e605b6cb6d16883d17dd73d4c21e06..57ea85aebc72eb002d3dcf04144499feeb82081a 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;
@@ -0,0 +1,174 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Bacteriawa <A3167717663@hotmail.com>
Date: Sat, 30 Aug 2025 17:07:02 +0800
Subject: [PATCH] Leaves: Leaves Fakeplayer
Co-authored by: violetc <58360096+s-yh-china@users.noreply.github.com>
As a part of : Leaves (https://github.com/LeavesMC/Leaves)
Licensed under: GPL-3.0 (https://www.gnu.org/licenses/gpl-3.0.html)
diff --git a/src/main/java/io/papermc/paper/plugin/manager/PaperEventManager.java b/src/main/java/io/papermc/paper/plugin/manager/PaperEventManager.java
index 9180f23529a31b6b0a5b38bb7cda3e32d487f691..cc8686b791f4c559e51bb06b52b02e295b6e0547 100644
--- a/src/main/java/io/papermc/paper/plugin/manager/PaperEventManager.java
+++ b/src/main/java/io/papermc/paper/plugin/manager/PaperEventManager.java
@@ -47,6 +47,23 @@ class PaperEventManager {
throw new IllegalStateException(event.getEventName() + " may only be triggered synchronously.");
}
+ // Leaves start - skip bot
+ if (event instanceof org.bukkit.event.player.PlayerEvent playerEvent && playerEvent.getPlayer() instanceof org.leavesmc.leaves.entity.bot.Bot) {
+ return;
+ }
+ // Leaves end - skip bot
+
+ // Leaves start - process bot load/save
+ if (fun.bm.lophine.config.modules.function.FakeplayerConfig.enable && fun.bm.lophine.config.modules.function.FakeplayerConfig.canResident) {
+ if (event instanceof org.bukkit.event.world.WorldLoadEvent worldLoadEvent) {
+ org.leavesmc.leaves.bot.BotList.INSTANCE.loadResume(worldLoadEvent.getWorld().getUID().toString());
+ } else if (event instanceof org.bukkit.event.world.WorldUnloadEvent worldUnloadEvent) {
+ org.leavesmc.leaves.bot.BotList.INSTANCE.removeAllIn(worldUnloadEvent.getWorld().getUID().toString());
+ }
+ }
+ // Leaves end - process bot load/save
+
+
for (RegisteredListener registration : listeners) {
if (!registration.getPlugin().isEnabled()) {
continue;
diff --git a/src/main/java/org/bukkit/craftbukkit/CraftRegionAccessor.java b/src/main/java/org/bukkit/craftbukkit/CraftRegionAccessor.java
index 0a10f49ee410d93e95ceb90108200a1a9d12b54b..d2eee37d810a6d5cf514bc71dea66a4d2b0d09e2 100644
--- a/src/main/java/org/bukkit/craftbukkit/CraftRegionAccessor.java
+++ b/src/main/java/org/bukkit/craftbukkit/CraftRegionAccessor.java
@@ -423,6 +423,7 @@ public abstract class CraftRegionAccessor implements RegionAccessor {
@SuppressWarnings("unchecked")
public <T extends Entity> T addEntity(T entity) {
Preconditions.checkArgument(!entity.isInWorld(), "Entity has already been added to a world");
+ Preconditions.checkState(!(entity instanceof org.leavesmc.leaves.entity.bot.CraftBot), "[Leaves] Fakeplayers do not support changing world, Please use leaves fakeplayer-api instead!");
net.minecraft.world.entity.Entity nmsEntity = ((CraftEntity) entity).getHandle();
if (nmsEntity.level() != this.getHandle().getLevel()) {
nmsEntity = nmsEntity.teleport(new TeleportTransition(this.getHandle().getLevel(), nmsEntity, TeleportTransition.DO_NOTHING));
diff --git a/src/main/java/org/bukkit/craftbukkit/CraftServer.java b/src/main/java/org/bukkit/craftbukkit/CraftServer.java
index 57ea85aebc72eb002d3dcf04144499feeb82081a..e03de696e7652b7e211bf61ff6d6cdf44a587146 100644
--- a/src/main/java/org/bukkit/craftbukkit/CraftServer.java
+++ b/src/main/java/org/bukkit/craftbukkit/CraftServer.java
@@ -316,6 +316,7 @@ public final class CraftServer implements Server {
private final io.papermc.paper.potion.PaperPotionBrewer potionBrewer;
public io.papermc.paper.SparksFly spark; // Paper - spark // Luminol - Force disable builtin spark
private final ServerConfiguration serverConfig = new PaperServerConfiguration();
+ private final org.leavesmc.leaves.entity.bot.CraftBotManager botManager; // Leaves
// Paper start - Folia region threading API
private final io.papermc.paper.threadedregions.scheduler.FoliaRegionScheduler regionizedScheduler = new io.papermc.paper.threadedregions.scheduler.FoliaRegionScheduler(); // Folia - region threading
@@ -498,6 +499,7 @@ public final class CraftServer implements Server {
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
+ this.botManager = new org.leavesmc.leaves.entity.bot.CraftBotManager(); // Leaves
}
public boolean getCommandBlockOverride(String command) {
@@ -1482,7 +1484,7 @@ public final class CraftServer implements Server {
return false;
}
- if (!handle.players().isEmpty()) {
+ if (!handle.realPlayers().isEmpty()) { // Leaves - skip
return false;
}
@@ -3260,6 +3262,13 @@ public final class CraftServer implements Server {
this.console.addPluginAllowingSleep(plugin.getName(), value);
}
+ // Leaves start - Bot API
+ @Override
+ public org.leavesmc.leaves.entity.bot.CraftBotManager getBotManager() {
+ return botManager;
+ }
+ // Leaves end - Bot API
+
// Folia start - region TPS API
/**
* Gets the TPS from the region which owns the specified location, or {@code null} if no region owns
diff --git a/src/main/java/org/bukkit/craftbukkit/CraftWorld.java b/src/main/java/org/bukkit/craftbukkit/CraftWorld.java
index 9b9eea4babcd67a088e9400b369d168d11c06ac1..3177db282013e6dc3c7215fc2a851e2630a29409 100644
--- a/src/main/java/org/bukkit/craftbukkit/CraftWorld.java
+++ b/src/main/java/org/bukkit/craftbukkit/CraftWorld.java
@@ -249,7 +249,7 @@ public class CraftWorld extends CraftRegionAccessor implements World {
@Override
public int getPlayerCount() {
- return world.players().size();
+ return world.realPlayers().size(); // Leaves - skip
}
@Override
@@ -1279,9 +1279,9 @@ public class CraftWorld extends CraftRegionAccessor implements World {
@Override
public List<Player> getPlayers() {
- List<Player> list = new ArrayList<Player>(this.world.players().size());
+ List<Player> list = new ArrayList<Player>(this.world.realPlayers().size()); // Leaves - skip
- for (net.minecraft.world.entity.player.Player human : this.world.players()) {
+ for (net.minecraft.world.entity.player.Player human : this.world.realPlayers()) { // Leaves - skip
HumanEntity bukkitEntity = human.getBukkitEntity();
if ((bukkitEntity != null) && (bukkitEntity instanceof Player)) {
@@ -1988,7 +1988,7 @@ public class CraftWorld extends CraftRegionAccessor implements World {
public void playSound(final net.kyori.adventure.sound.Sound sound) {
org.spigotmc.AsyncCatcher.catchOp("play sound"); // Paper
final long seed = sound.seed().orElseGet(this.world.getRandom()::nextLong);
- for (ServerPlayer player : this.getHandle().players()) {
+ for (ServerPlayer player : this.getHandle().realPlayers()) { // Leaves - skip
player.connection.send(io.papermc.paper.adventure.PaperAdventure.asSoundPacket(sound, player.getX(), player.getY(), player.getZ(), seed, null));
}
}
@@ -2016,7 +2016,7 @@ public class CraftWorld extends CraftRegionAccessor implements World {
org.spigotmc.AsyncCatcher.catchOp("play sound"); // Paper
final long seed = sound.seed().orElseGet(this.getHandle().getRandom()::nextLong);
if (emitter == net.kyori.adventure.sound.Sound.Emitter.self()) {
- for (ServerPlayer player : this.getHandle().players()) {
+ for (ServerPlayer player : this.getHandle().realPlayers()) { // Leaves - skip
player.connection.send(io.papermc.paper.adventure.PaperAdventure.asSoundPacket(sound, player, seed, null));
}
} else if (emitter instanceof CraftEntity craftEntity) {
@@ -2248,7 +2248,7 @@ public class CraftWorld extends CraftRegionAccessor implements World {
Preconditions.checkArgument(particle.getDataType().isInstance(data), "data (%s) should be %s", data.getClass(), particle.getDataType());
}
this.getHandle().sendParticlesSource(
- receivers == null ? this.getHandle().players() : receivers.stream().map(player -> ((CraftPlayer) player).getHandle()).collect(java.util.stream.Collectors.toList()), // Paper - Particle API
+ receivers == null ? this.getHandle().realPlayers() : receivers.stream().map(player -> ((CraftPlayer) player).getHandle()).collect(java.util.stream.Collectors.toList()), // Paper - Particle API // Leaves - skip
sender != null ? ((CraftPlayer) sender).getHandle() : null, // Sender // Paper - Particle API
CraftParticle.createParticleParam(particle, data), // Particle
force,
diff --git a/src/main/java/org/bukkit/craftbukkit/entity/CraftEntity.java b/src/main/java/org/bukkit/craftbukkit/entity/CraftEntity.java
index 852e1ffef6a022caad7c8eff34091e50112a2290..8eb5d014d9ed688ffebaffb4ce0bb40896961a04 100644
--- a/src/main/java/org/bukkit/craftbukkit/entity/CraftEntity.java
+++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftEntity.java
@@ -126,6 +126,8 @@ public abstract class CraftEntity implements org.bukkit.entity.Entity {
return new CraftHumanEntity(server, (net.minecraft.world.entity.player.Player) entity);
}
+ if (entity instanceof org.leavesmc.leaves.bot.ServerBot bot) { return new org.leavesmc.leaves.entity.bot.CraftBot(server, bot); }
+
// Special case complex part, since there is no extra entity type for them
if (entity instanceof EnderDragonPart complexPart) {
if (complexPart.parentMob instanceof EnderDragon) {
diff --git a/src/main/java/org/bukkit/craftbukkit/event/CraftEventFactory.java b/src/main/java/org/bukkit/craftbukkit/event/CraftEventFactory.java
index ffb10b412d19907e6f63b7e3ca67ef061eec96ba..de14df49b11f1243de9aa6870a3c8e3f429502fd 100644
--- a/src/main/java/org/bukkit/craftbukkit/event/CraftEventFactory.java
+++ b/src/main/java/org/bukkit/craftbukkit/event/CraftEventFactory.java
@@ -897,7 +897,11 @@ public class CraftEventFactory {
event.setKeepInventory(keepInventory);
event.setKeepLevel(victim.keepLevel); // SPIGOT-2222: pre-set keepLevel
populateFields(victim, event); // Paper - make cancellable
- Bukkit.getServer().getPluginManager().callEvent(event);
+ // Leaves start - disable bot death event
+ if (!(victim instanceof org.leavesmc.leaves.bot.ServerBot)) {
+ Bukkit.getServer().getPluginManager().callEvent(event);
+ }
+ // Leaves end - disable bot death event
// Paper start - make cancellable
if (event.isCancelled()) {
return event;
@@ -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) {
}
}
@@ -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;
}
}
}
@@ -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;
}
}
@@ -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();
}
@@ -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 + ")";
}
}
@@ -1,9 +1,12 @@
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;
import me.earthme.luminol.enums.EnumConfigCategory;
@ConfigClassInfo(configAttribution = EnumConfigCategory.EXPERIMENT, mainName = "command")
public class CommandConfig implements IConfigModule {
@ConfigInfo(baseName = "tick_command_enabled", comments =
"""
@@ -16,13 +19,15 @@ public class CommandConfig implements IConfigModule {
--- datapack hot-update unsupported, please restart server""")
public static boolean datapack = false;
@Override
public EnumConfigCategory getCategory() {
return EnumConfigCategory.EXPERIMENT;
}
@ConfigInfo(baseName = "function_command_enabled", comments =
"""
Allow to use function command""")
public static boolean function = false;
@Override
public String getBaseName() {
return "command";
}
@TransformedConfig(name = "enable-waypoint", category = {"experiment", "waypoint bar"})
@TransformedConfig(name = "enable-waypoint", category = {"experiment", "waypoint_bar"})
@ConfigInfo(baseName = "waypoint_command_enabled", comments = """
Allow to use waypoint command
""")
public static boolean waypoint = false;
}
@@ -1,22 +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;
import me.earthme.luminol.enums.EnumConfigCategory;
@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;
@Override
public EnumConfigCategory getCategory() {
return EnumConfigCategory.EXPERIMENT;
}
@Override
public String getBaseName() {
return "entity_damage_source_trace";
}
}
@@ -1,9 +1,11 @@
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.enums.EnumConfigCategory;
@ConfigClassInfo(configAttribution = EnumConfigCategory.EXPERIMENT, mainName = "ray_tracking_entity_tracker")
public class RayTrackingEntityTrackerConfig implements IConfigModule {
@ConfigInfo(baseName = "enabled")
public static boolean enabled = false;
@@ -15,14 +17,4 @@ public class RayTrackingEntityTrackerConfig implements IConfigModule {
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";
}
}
@@ -1,24 +0,0 @@
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.ConfigInfo;
import me.earthme.luminol.config.flags.TransformedConfig;
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;
@Override
public EnumConfigCategory getCategory() {
return EnumConfigCategory.EXPERIMENT;
}
@Override
public String getBaseName() {
return "waypoint_bar";
}
}
@@ -0,0 +1,15 @@
package fun.bm.lophine.config.modules.fixes;
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;
import me.earthme.luminol.enums.EnumConfigCategory;
@ConfigClassInfo(configAttribution = EnumConfigCategory.FIXES, mainName = "end-void-ring")
public class EndVoidRingsConfig implements IConfigModule {
@TransformedConfig(name = "enabled", category = {"gameplay", "end_void_rings"})
@ConfigInfo(baseName = "enabled", comments = """
If enabled, it will generate end void rings, like MC-159283.""")
public static boolean enabled = false;
}
@@ -0,0 +1,32 @@
package fun.bm.lophine.config.modules.function;
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;
import me.earthme.luminol.enums.EnumConfigCategory;
@ConfigClassInfo(configAttribution = EnumConfigCategory.FUNCTION, mainName = "container_expansion")
public class ContainerExpansionConfig implements IConfigModule {
@TransformedConfig(name = "barrel_rows", category = {"misc", "container_expansion"})
@ConfigInfo(baseName = "barrel_rows", comments =
"""
range: 1~6""")
public static int barrelRows = 3;
@TransformedConfig(name = "enderchest_rows", category = {"misc", "container_expansion"})
@ConfigInfo(baseName = "enderchest_rows", comments =
"""
range: 1~6""")
public static int enderchestRows = 3;
@TransformedConfig(name = "shulker_stackable_count", category = {"misc", "container_expansion"})
@ConfigInfo(baseName = "shulker_stackable_count", comments =
"""
range: 1~64""")
public static int shulkerCount = 1;
@TransformedConfig(name = "same_nbt_shulker_stackable", category = {"misc", "container_expansion"})
@ConfigInfo(baseName = "same_nbt_shulker_stackable")
public static boolean nbtShulkerStackable = false;
}
@@ -0,0 +1,108 @@
package fun.bm.lophine.config.modules.function;
import com.electronwill.nightconfig.core.file.CommentedFileConfig;
import me.earthme.luminol.config.IConfigModule;
import me.earthme.luminol.config.flags.ConfigClassInfo;
import me.earthme.luminol.config.flags.ConfigInfo;
import me.earthme.luminol.enums.EnumConfigCategory;
import net.minecraft.server.MinecraftServer;
import org.bukkit.Bukkit;
import org.leavesmc.leaves.bot.BotCommand;
import org.leavesmc.leaves.bot.ServerBot;
import org.leavesmc.leaves.bot.agent.Actions;
import java.util.List;
import java.util.Locale;
@ConfigClassInfo(configAttribution = EnumConfigCategory.FUNCTION, mainName = "fakeplayer")
public class FakeplayerConfig implements IConfigModule {
@ConfigInfo(baseName = "enable", comments = """
Enable fakeplayer functionality""")
public static boolean enable = true;
@ConfigInfo(baseName = "unable-fakeplayer-names", comments = """
List of names that cannot be used for fakeplayers""")
public static List<String> unableNames = List.of("player-name");
@ConfigInfo(baseName = "limit", comments = """
Maximum number of fakeplayers allowed""")
public static int limit = 10;
@ConfigInfo(baseName = "prefix", comments = """
Prefix for fakeplayer names""")
public static String prefix = "";
@ConfigInfo(baseName = "suffix", comments = """
Suffix for fakeplayer names""")
public static String suffix = "";
@ConfigInfo(baseName = "regen-amount", comments = """
Regeneration amount for fakeplayers""")
public static double regenAmount = 0.0;
@ConfigInfo(baseName = "resident-fakeplayer", comments = """
Allow fakeplayers to be resident""")
public static boolean canResident = false;
@ConfigInfo(baseName = "open-fakeplayer-inventory", comments = """
Allow opening fakeplayer inventory""")
public static boolean canOpenInventory = false;
@ConfigInfo(baseName = "use-action", comments = """
Allow fakeplayers to use actions""")
public static boolean canUseAction = true;
@ConfigInfo(baseName = "modify-config", comments = """
Allow modifying fakeplayer config""")
public static boolean canModifyConfig = false;
@ConfigInfo(baseName = "manual-save-and-load", comments = """
Allow manual save and load of fakeplayers""")
public static boolean canManualSaveAndLoad = false;
@ConfigInfo(baseName = "cache-skin", comments = """
Use skin cache for fakeplayers""")
public static boolean useSkinCache = false;
@ConfigInfo(baseName = "always-send-data", comments = """
Always send data for fakeplayers""")
public static boolean canSendDataAlways = true;
@ConfigInfo(baseName = "skip-sleep-check", comments = """
Skip sleep check for fakeplayers""")
public static boolean canSkipSleep = false;
@ConfigInfo(baseName = "spawn-phantom", comments = """
Allow phantoms to spawn for fakeplayers""")
public static boolean canSpawnPhantom = false;
@ConfigInfo(baseName = "simulation-distance", comments = """
Simulation distance for fakeplayers (-1 for default)""")
public static int simulationDistance = -1;
@ConfigInfo(baseName = "enable-locator-bar", comments = """
Enable locator bar for fakeplayers""")
public static boolean enableLocatorBar = false;
public static ServerBot.TickType tickType = ServerBot.TickType.ENTITY_LIST;
public static void unregisterCommand(String name) {
name = name.toLowerCase(Locale.ENGLISH).trim();
MinecraftServer.getServer().server.getCommandMap().getKnownCommands().remove(name);
MinecraftServer.getServer().server.getCommandMap().getKnownCommands().remove("leaves:" + name);
MinecraftServer.getServer().server.syncCommands();
}
public static int getSimulationDistance(ServerBot bot) {
return simulationDistance == -1 ? bot.getBukkitEntity().getSimulationDistance() : simulationDistance;
}
public void onLoaded(CommentedFileConfig configInstance) {
if (enable) {
Bukkit.getCommandMap().register("bot", "lophine", new BotCommand());
Actions.registerAll();
} else {
unregisterCommand("bot");
}
}
}

Some files were not shown because too many files have changed in this diff Show More