Compare commits

...

129 Commits

Author SHA1 Message Date
Helvetica Volubi ef3d1625b8 backport: add removed check before all checks start in addEffects 2026-01-13 21:44:57 +08:00
Helvetica Volubi c4acc049d8 fix dragon problem in overworld
origin fixed in Luminol 1.21.11
2026-01-04 18:54:26 +08:00
dependabot[bot] 3dc7b1edb7 [ci skip]build(deps): bump JetBrains/qodana-action from 2025.2 to 2025.3 (#114)
Bumps [JetBrains/qodana-action](https://github.com/jetbrains/qodana-action) from 2025.2 to 2025.3.
- [Release notes](https://github.com/jetbrains/qodana-action/releases)
- [Commits](https://github.com/jetbrains/qodana-action/compare/v2025.2...v2025.3)

---
updated-dependencies:
- dependency-name: JetBrains/qodana-action
  dependency-version: '2025.3'
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-12-29 00:56:08 +08:00
Helvetica Volubi ab2992a2ef fix some bug caused by 5622d49d49 2025-12-27 20:51:20 +08:00
Helvetica Volubi 5622d49d49 backport: refix waypoint
related issue: https://github.com/LuminolMC/Lophine/issues/110

origin commit: bed0bfff06
2025-12-26 03:49:37 +08:00
Helvetica Volubi 7fdce6aedd backport: fix waypoint module
origin commit: db772aaf62
2025-12-23 10:26:39 +08:00
Helvetica Volubi c26d8d1ee3 [ci skip]before we start to update to 1.21.11, we need push to maven repo 2025-12-18 03:43:04 +08:00
Helvetica Volubi 7bc12d2487 fix: add null check in RegionizedWorldData >> conections
some of our function patches will crash because of get null in conn.getPlayer(), then when we process player.chunkPosition() will get NPE, so we need to fix it.
2025-12-17 00:26:36 +08:00
dependabot[bot] 2c3aa4b68e [ci skip]build(deps): bump actions/upload-artifact from 5 to 6 (#106)
Bumps [actions/upload-artifact](https://github.com/actions/upload-artifact) from 5 to 6.
- [Release notes](https://github.com/actions/upload-artifact/releases)
- [Commits](https://github.com/actions/upload-artifact/compare/v5...v6)

---
updated-dependencies:
- dependency-name: actions/upload-artifact
  dependency-version: '6'
  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-12-15 15:44:44 +08:00
Bacteriawa 91312b7a15 Update Luminol 2025-12-14 00:18:07 +08:00
Helvetica Volubi 77c412bfe8 feat: update concurrent in waypoint 2025-12-11 02:05:57 +08:00
Helvetica Volubi c06d875df7 feat: add optimized concurrent table for waypoint management 2025-12-10 15:22:58 +08:00
Helvetica Volubi 5e96851513 fix a bug in entities counter 2025-12-10 02:52:55 +08:00
Helvetica Volubi 91b3ad527c fix waypoint manager
we use thread safe method to load it
2025-12-10 01:49:10 +08:00
dependabot[bot] 959a1c346c [ci skip]build(deps): bump actions/checkout from 3 to 6 (#102)
Bumps [actions/checkout](https://github.com/actions/checkout) from 3 to 6.
- [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/v3...v6)

---
updated-dependencies:
- dependency-name: actions/checkout
  dependency-version: '6'
  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-12-08 15:51:58 +08:00
Helvetica Volubi b594382a6a [ci skip] fixup ci 2025-12-07 19:06:04 +08:00
Helvetica Volubi 37f1b4e5f1 fix: fix some async bugs 2025-12-07 18:42:46 +08:00
mrhua269 af2af944bf Fix issues of photographer with folia region threading 2025-12-06 23:25:37 +08:00
mrhua269 54688d8e34 Fix issues of fakeplayer with folia region threading 2025-12-06 22:35:51 +08:00
Helvetica Volubi 7b7f0b6d1f [ci skip] fix up branch in qodana 2025-12-06 21:58:02 +08:00
Helvetica Volubi 0ab3983145 Update Luminol
We need to check folia-supported,so we update it now
2025-12-06 16:52:56 +08:00
Helvetica Volubi 24a9d5cea8 fix https://github.com/MC-XiaoHei/ISeeYou/issues/68#issuecomment-3619619509 2025-12-06 15:45:34 +08:00
Helvetica Volubi 8ee77c3354 [ci skip] remove unused check
we have checked in protocol manager
2025-12-06 15:45:12 +08:00
qodana-cloud[bot] 98df254bad [ci skip]Add qodana CI checks (#100)
* Add qodana.yaml file

* Add github workflow file

---------

Co-authored-by: Qodana Application <qodana-support@jetbrains.com>
2025-12-04 14:19:47 +08:00
Helvetica Volubi 19f001ad1b [ci skip] remove unused data 2025-12-04 14:05:46 +08:00
Helvetica Volubi 106dfeaa38 fix: fix global entity count max limit problem 2025-12-04 13:37:48 +08:00
Helvetica Volubi 403ae2b2d0 feat: chunkCount 2025-12-04 12:13:14 +08:00
Helvetica Volubi 96c99519d5 fix: fix an async bug
if latest task is not done, skip this tick so that we can not block thread and run the task have not done, after it done, the new data will be processed in the next tick
2025-12-03 20:33:13 +08:00
Helvetica Volubi 5c4031b836 [ci skip] fixup template & comment 2025-12-03 20:20:59 +08:00
Helvetica Volubi 376857ff97 feat: global entities counter
We provided a new way to use entities counter to spawn mobs, maybe have some bugs, so we set this to experiment config

 - Update file update
 - Update Luminol
2025-12-03 16:31:34 +08:00
Helvetica Volubi fa1ab90579 fix a ConcurrentModificationException caused by ServuxStructuresProtocol
Update Luminol
2025-11-30 15:53:28 +08:00
Creeam fc9c6937b6 Add LeavesConfig dummy class (#99)
For some plugin detections
2025-11-30 07:28:01 +00:00
Helvetica Volubi 12d8ba9050 Update Luminol 2025-11-29 06:03:01 +08:00
Helvetica Volubi 38a1ce0443 fix: fix crash caused by uncheck data access 2025-11-26 22:32:20 +08:00
dependabot[bot] a5a415e495 build(deps): bump actions/checkout from 5 to 6 (#93)
Bumps [actions/checkout](https://github.com/actions/checkout) from 5 to 6.
- [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/v5...v6)

---
updated-dependencies:
- dependency-name: actions/checkout
  dependency-version: '6'
  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-11-24 08:21:49 +00:00
Helvetica Volubi b7bf1b5916 Update Luminol 2025-11-24 00:16:50 +08:00
Helvetica Volubi 4a8888c578 try fixes bugs caused by Bukkit.getScheduler().runTaskLater
we use (FoliaGlobalRegionScheduler.GlobalScheduledTask) Bukkit.getGlobalRegionScheduler().runDelayed to instead of Bukkit.getScheduler().runTaskLater, and we also provided a brand-new way to cancel task by task id
2025-11-22 01:57:16 +08:00
Helvetica Volubi 2c59a32afe [ci skip] Update context 2025-11-20 15:36:46 +08:00
Helvetica Volubi cc5382977d Update Luminol 2025-11-20 15:32:41 +08:00
xiaoxijun c07dde1c0b feat: CCE Update Suppression support and other dependences (#84)
add cce update suppression & prevent crashes caused by update suppres & dustTrapdoorReintroduced & instantBlockUpdater & old Block remove behaviour & do not reset placed block on exception & do not prevent block entity and entity crash at LevelChunk (#84)

* add cce update suppression & prevent crashes caused by update suppression

* no need for this transformed flag

* Add Redstone ignore upwards update

* fix up license

* move config

* instant Block Updater

* feat: Update Suppression!

* Leaves: Do not reset placed block on exception & Do not prevent block entity and entity crash at LevelChunk

---------

Co-authored-by: Helvetica Volubi <suisuroru@blue-millennium.fun>
Co-authored-by: Helvetica Volubi <88063803+Suisuroru@users.noreply.github.com>
2025-11-19 00:04:34 +08:00
Helvetica Volubi 871a003a2e Update Luminol to fix open-gui bug 2025-11-17 17:54:27 +08:00
Helvetica Volubi d6a981a6e9 [ci skip] skip release and push in unexpect branch
See https://github.com/LuminolMC/Luminol/commit/ec746a66f0813f60eb59de11b8207f773fc4728c
2025-11-17 17:26:24 +08:00
Helvetica Volubi bc166a9fb6 [ci skip] refactor code 2025-11-16 21:22:06 +08:00
Helvetica Volubi feebf388f8 Add Replay Api (#86)
* feat: base replay api support

* fix an async bug
2025-11-16 21:05:01 +08:00
Helvetica Volubi dbcf764e76 fix ServuxStructuresProtocol (#89)
* Add LeaveHooks to fix bug caused by servux structures protocol

* add missing file

* add comment

* add license
2025-11-16 20:51:19 +08:00
Helvetica Volubi 735fe8855a try to fix a bug in fake player spawn 2025-11-16 18:53:16 +08:00
Helvetica Volubi 1b45a090f6 Update Luminol 2025-11-16 14:09:42 +08:00
Helvetica Volubi bb1335d390 [ci skip] Update Luminol
do not release yet, release at next commit
2025-11-15 15:38:52 +08:00
Helvetica Volubi 00743c841f Update Luminol 2025-11-14 22:27:06 +08:00
xiaoxijun bdb9c9dec4 Add Alternative block placement Protocol (#81)
* Add Alternative block placement Protocol

* fix: fix config error caused by config system

- TODO: fix config system read enum bug in luminol

* remove Leaves logger & use mojang logger

* update code from leaves

---------

Co-authored-by: Helvetica Volubi <suisuroru@blue-millennium.fun>
Co-authored-by: Helvetica Volubi <88063803+Suisuroru@users.noreply.github.com>
2025-11-12 19:15:21 +08:00
Helvetica Volubi ac3653dda7 [ci skip]Update Gradle wrapper to version 9.2.0 2025-11-12 00:56:44 +08:00
Helvetica Volubi a08fd04751 Update Luminol to fix a sentry bug 2025-11-12 00:20:46 +08:00
Helvetica Volubi f5debe9ee1 [ci skip] Update Luminol 2025-11-10 17:27:08 +08:00
Helvetica Volubi 1a84d2e670 Update Luminol to fix a crash bug in config command system 2025-11-10 00:27:08 +08:00
Helvetica Volubi e96891275d [ci skip] update datapack command save 2025-11-09 02:38:51 +08:00
Helvetica Volubi 9e342c6683 Update Luminol 2025-11-06 00:12:13 +08:00
Helvetica Volubi 00f7f5ee2b fix: fixes #69 2025-11-01 02:53:48 +08:00
Helvetica Volubi 77b9582c4a Update Luminol 2025-10-31 22:23:17 +08:00
Helvetica Volubi 184048fd26 Update Luminol 2025-10-31 00:23:24 +08:00
Helvetica Volubi 7ccca03023 try to fix BBOR protocol 2025-10-29 02:09:22 +08:00
Helvetica Volubi b34fa8237e feat: provided void trade instead of infinite trade
fixes #77
2025-10-29 00:20:45 +08:00
Helvetica Volubi 42a4d18717 refactor: deprecated tickCount 2025-10-27 21:40:42 +08:00
Helvetica Volubi 2d8d7f5784 refactor: prepare to drop reuse tickCount 2025-10-27 21:11:51 +08:00
Helvetica Volubi bf509bcded feat: wool hopper counter(#27) 2025-10-27 19:59:03 +08:00
Helvetica Volubi ef6759a9ab [ci skip] Update Luminol to skip release by robot 2025-10-27 05:40:37 +00:00
Helvetica Volubi bc1483ea7a fix: try to fix Concurrent throw in protocol 2025-10-27 00:10:44 +08:00
Helvetica Volubi 5768a60ba8 fix: rewrite request block data method 2025-10-25 03:22:14 +08:00
Helvetica Volubi 05434f422c fix: fixes #63 2025-10-23 00:20:15 +08:00
Helvetica Volubi 21e9299352 Update code from Leaves 2025-10-22 11:34:52 +08:00
Helvetica Volubi 83e7718346 fix: try fix #63 2025-10-22 02:41:51 +08:00
Helvetica Volubi 5845b976c1 fix: try fix #70 2025-10-22 02:15:15 +08:00
xiaoxijun 4ca94280ae Add support for more mod protocols (#73)
* Add Syncmatica protocols

* Add AppleSkin protocols

* Add BBOR Protocol

* Add Jade Protocol

* Add Xaero Map Protocol

* Add Support REI protocol

* Fixed an issue where the configuration file was not loading correctly.

* move config file directory

* add license & refactor code

* update leaves fix

https://github.com/LeavesMC/Leaves/issues/747#event-19986621829

* Update Luminol

* fix up jade protocol

* fix up rei

---------

Co-authored-by: Helvetica Volubi <suisuroru@blue-millennium.fun>
Co-authored-by: Helvetica Volubi <88063803+Suisuroru@users.noreply.github.com>
2025-10-20 00:06:40 +08:00
Helvetica Volubi 4cc49c6d99 fix: fix bot save command 2025-10-20 00:02:21 +08:00
Helvetica Volubi 93c0258c7e fix: use local lastTickCount to save data 2025-10-19 23:40:17 +08:00
Helvetica Volubi 43a5ea0385 Update Luminol 2025-10-18 02:34:13 +08:00
Helvetica Volubi 678882a435 Update Luminol & fix a bug in shutdown server 2025-10-14 19:14:12 +08:00
Helvetica Volubi 8e5884147f fix: fix async bugs
fixes #70
2025-10-12 11:54:30 +08:00
Helvetica Volubi b999cf9f5f [ci skip] update ci 2025-10-11 23:34:49 +08:00
Helvetica Volubi 847bcd54e0 Update Luminol 2025-10-06 14:20:14 +08:00
Helvetica Volubi 15a55403ff Modify merge ItemEntity logic
Add followTickSequenceMerge to modify merge items, due to Paper's modification of the merge radius, when the merge radius is large and stacks containing many items get stuck in an unexpected position, individual items may never reach their destination. This configuration option is added to fix this behavior.
2025-10-04 01:15:49 +08:00
Helvetica Volubi 482809df6e [ci skip]fix: fix default translations support 2025-10-03 18:24:30 +08:00
Helvetica Volubi 5db8ec4bd9 fix: pre fix a bug in I18n (will cause bug in 1.21.9) 2025-10-03 17:03:37 +08:00
Helvetica Volubi d60cd46c15 Update Luminol 2025-10-02 16:51:47 +08:00
Helvetica Volubi df40d4bdc9 Update Luminol 2025-10-01 23:21:57 +08:00
Helvetica Volubi 0084883933 feat: add config to enable scoreboard command unsafe 2025-09-29 22:59:44 +08:00
Helvetica Volubi 1d1e4ec70b fix: fixes #62 2025-09-29 16:49:58 +08:00
Helvetica Volubi 1927a66d8e fix:fix a bug in config of multi-format 2025-09-29 00:14:55 +08:00
Helvetica Volubi 38442749e7 fix: fixes #65 2025-09-28 17:39:20 +08:00
Helvetica Volubi 70dae5976d fix: try fixes #63 2025-09-28 15:49:30 +08:00
Helvetica Volubi b08756b4b3 fix: fixes #64 2025-09-28 14:44:04 +08:00
Helvetica Volubi 5e658adad3 Update Luminol 2025-09-28 13:12:54 +08:00
Bacteriawa 4d6e08312a Update Luminol 2025-09-25 23:43:14 +08:00
Helvetica Volubi 9efe6499d0 Update Luminol 2025-09-24 01:08:36 +08:00
Helvetica Volubi 319946e8c1 fixes #61 2025-09-21 21:14:01 +08:00
Helvetica Volubi 28ce6ae7b1 Update Luminol & fix up CI 2025-09-19 15:10:55 +08:00
Helvetica Volubi af4a8a8731 Update Luminol 2025-09-13 22:17:35 +08:00
Helvetica Volubi 7059c6a062 Update Luminol & Leaves Redstone Shears Wrench 2025-09-13 04:03:53 +08:00
Helvetica Volubi 98a8b65665 Update Luminol 2025-09-13 02:39:56 +08:00
Helvetica Volubi ac6b1e2377 Update Luminol 2025-09-12 05:09:04 +08:00
Helvetica Volubi 0e964ae71c fixes #58 2025-09-11 17:24:13 +08:00
Helvetica Volubi ba6bd9f71c Update Luminol 2025-09-06 17:15:36 +08:00
Helvetica Volubi 4513cfd637 Fake Player command use brigadier (#57)
* pre update for new command system branch

* update ref
2025-09-05 03:30:27 +08:00
Helvetica Volubi d87cca6a97 fix: fix shutdown problem if bot was not removed in world 2025-09-04 21:38:52 +08:00
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
372 changed files with 28441 additions and 1041 deletions
+17 -3
View File
@@ -1,7 +1,7 @@
name: Bug反馈 Bug report
description:
报告Bug,插件不兼容情况,以及其他相关问题。
Report issues with plugin incompatbility or other behavior related issues.
Report issues with plugin incompatibility or other behavior related issues.
labels:
- bug
- pending
@@ -25,7 +25,7 @@ body:
LophineMC服务端发行版本 LophineMC server release version (contains the git commit's hash)<br>
通过执行`/version`命令将获取到的内容复制到这里。 Run `/version` on your server and paste the full, unmodified output here.
placeholder: >
e.g. git-Lophine-"0635a89" (MC: 1.20.4)
e.g. git-Lophine-"fa1ab90" (MC: 1.21.8)
validations:
required: true
- type: textarea
@@ -41,7 +41,7 @@ body:
attributes:
label: 观测到的实际行为 Observed/Actual behavior
description: 你实际看到的。 What you actually saw.
placeholder: e.g. 使用 /tpsbar [NAME] 命令切换不了TPSBAR
placeholder: e.g. 使用 /tpsbar 命令切换不了TPSBAR
validations:
required: true
- type: textarea
@@ -65,6 +65,20 @@ body:
To list plugins, run `/plugins`. For datapacks, run `/datapack list`.
validations:
required: true
- type: textarea
id: config-file-list
attributes:
label: 配置文件列表(如果可以)。 Config File List (if possible).
description:
服务器上的配置文件列表。
All config files on your server.
例如以下文件(或者文件夹):
Example files (or folders)
- \spigot.yml
- \bukkit.yml
- \config\\\*
- \lophine_config\\\*
- \luminol_config\\\*
- type: textarea
id: other
attributes:
+1 -1
View File
@@ -28,7 +28,7 @@ body:
validations:
required: true
- type: textarea
id: othre
id: other
attributes:
label: 其他 Other
description: 添加其他的信息。 Add any other context or screenshots about the feature request below.
+92 -12
View File
@@ -7,6 +7,20 @@ on:
pull_request:
branches:
- "**"
workflow_dispatch:
inputs:
force-release:
description: "Force disable release if you enter 2, force release if you enter 1, default release if not released else skip release"
required: false
default: ""
force-push:
description: "Force disable push to repo if you enter 2, force push to repo if you enter 1, default push if not released else skip push repo"
required: false
default: ""
comments:
description: "Add comments to release"
required: false
default: ""
permissions: write-all
@@ -16,16 +30,16 @@ jobs:
environment: default
steps:
- name: Checkout Git Repository
uses: actions/checkout@v4
uses: actions/checkout@v6
- name: Set up JDK
uses: actions/setup-java@v4
- name: Set Up JDK
uses: actions/setup-java@v5
with:
distribution: zulu
java-version: 21
- name: Setup Gradle
uses: gradle/actions/setup-gradle@v4
uses: gradle/actions/setup-gradle@v5
- name: Grant execute permission for gradlew
run: chmod +x gradlew
@@ -36,36 +50,102 @@ jobs:
- name: Apply All Patches
run: ./gradlew --refresh-dependencies applyAllPatches
- name: CreateMojmapPaperclipJar
- name: Build Paperclip Jar
run: ./gradlew --refresh-dependencies createMojmapPaperclipJar
- name: Upload Artifact
uses: actions/upload-artifact@v4
uses: actions/upload-artifact@v6
with:
name: ${{ env.project_id_b }} CI Artifacts
path: lophine-server/build/libs/*-paperclip-*-mojmap.jar
- name: SetENV
- name: Set Environment
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
- name: Check If Release Exists
if: github.event_name != 'pull_request' && env.flag_release == 'true'
run: |
git fetch --tags
if [ -n "$(git tag -l "${{ env.tag }}")" ]; then
echo "Release already exists"
release_exists=true
else
echo "Release does not exist"
release_exists=false
fi
if [ "$release_exists" == "true" ]; then
counter=1
while [ -n "$(git tag -l "${{ env.tag }}-${counter}")" ]; do
counter=$((counter + 1))
done
echo "tag=${{ env.tag }}-${counter}" >> $GITHUB_ENV
echo "release_count= - ${counter}" >> $GITHUB_ENV
fi
echo "release_exists=${release_exists}" >> $GITHUB_ENV
- name: Check If Branch In Expectations
if: github.event_name != 'pull_request'
run: |
if [[ "$GITHUB_REF_NAME" == ver/* ]] || [[ "$GITHUB_REF_NAME" == exp/* ]] || [[ "$GITHUB_REF_NAME" == dev/* ]]; then
echo "Branch is in expected prefixes"
echo "unexpect=false" >> $GITHUB_ENV
else
echo "Branch is NOT in expected prefixes"
echo "unexpect=true" >> $GITHUB_ENV
fi
- name: Publish To Repo
if: ( github.event_name != 'pull_request' && !( env.unexpect == 'true' ) && env.flag_release == 'true' && env.release_exists != 'true' && !( inputs.force-release == '2' )) || inputs.force-push == '1'
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' && env.flag_release == 'true'
- name: Create Release - Pre Process
if: ( github.event_name != 'pull_request' && !( env.unexpect == 'true' ) && env.flag_release == 'true' && env.release_exists != 'true' && !(inputs.force-release == '2')) || inputs.force-release == '1'
run: |
if [ "${{ inputs.comments }}" == "" ]; then
echo "No comments provided"
echo "flag_comment=false" >> $GITHUB_ENV
else
echo "Comments provided: ${{ inputs.comments }}"
echo "flag_comment=true" >> $GITHUB_ENV
fi
- name: Create Release - No Comment
if: env.flag_comment == 'false'
uses: ncipollo/release-action@v1
with:
tag: ${{ env.tag }}
name: ${{ env.project_id_b }} ${{ env.mcversion }} - ${{ env.commit_id }}
name: ${{ env.project_id_b }} ${{ env.mcversion }} - ${{ env.commit_id }}${{ env.release_count }}
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 }}
generateReleaseNotes: true
prerelease: ${{ env.pre }}
makeLatest: ${{ env.make_latest }}
token: ${{ secrets.GITHUB_TOKEN }}
- name: Create Release - With Comment
if: env.flag_comment == 'true'
uses: ncipollo/release-action@v1
with:
tag: ${{ env.tag }}
name: ${{ env.project_id_b }} ${{ env.mcversion }} - ${{ env.commit_id }}${{ env.release_count }}
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
### Comments
> ${{ inputs.comments }}
### Branch Info
> ${{ github.ref_name }}
### Commit Message
${{ env.commit_msg }}
artifacts: ${{ env.jar_dir }}
+29
View File
@@ -0,0 +1,29 @@
name: Qodana
on:
workflow_dispatch:
pull_request:
push:
branches: # Specify your branches here
- ver/* # Stable branch
- exp/* # Experimental branch
- dev/* # Development Branch
jobs:
qodana:
runs-on: ubuntu-latest
permissions:
contents: write
pull-requests: write
checks: write
steps:
- uses: actions/checkout@v6
with:
ref: ${{ github.event.pull_request.head.sha }} # to check out the actual pull request commit, not the merge commit
fetch-depth: 0 # a full history is required for pull request analysis
- name: 'Qodana Scan'
uses: JetBrains/qodana-action@v2025.3
with:
pr-mode: false
env:
QODANA_TOKEN: ${{ secrets.QODANA_TOKEN_1658551725 }}
QODANA_ENDPOINT: 'https://qodana.cloud'
+93 -44
View File
@@ -1,104 +1,149 @@
<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>
## ✨ 核心特性
![Created At](https://img.shields.io/github/created-at/LuminolMC/Lophine?style=flat-square)
[![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) | **中文**
## 📥 下载
## 特性
### 稳定版本
所有发布版本都可以在 [Releases](https://github.com/LuminolMC/Lophine/releases) 页面找到。
- 支持部分Bukkit插件
- 可配置的原版特性
- Tpsbar 支持
- 针对folia bug的一些修复
- linear和b_linear(linear的重新实现)存档格式支持
- 对单线程区域性能的优化
- 更多有用的功能
### 开发版本
如果您想体验最新功能,可以通过以下步骤自行构建。
## 下载
### 构建步骤
任何版本都可以在 [Release](https://github.com/LuminolMC/Lophine/releases), 中找到,你也可以通过[以下步骤](./README.md#构建)自己构建。
```bash
# 克隆项目
git clone https://github.com/LuminolMC/Lophine.git
cd Lophine
## 构建
要构建一个paperclip jar,你需要运行以下命令。你可以在lophine-server/build/libs中找到jar(注意:需要`JDK21`
```shell
# 应用补丁并构建 Paperclip JAR
./gradlew applyAllPatches && ./gradlew createMojmapPaperclipJar
```
## 使用API
构建完成后,您可以在 `lophine-server/build/libs` 目录中找到生成的 JAR 文件。
使用 Gradle:
## 🔌 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")
compileOnly("fun.bm.lophine:lophine-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>
<dependencies>
<dependency>
<groupId>me.earthme.luminol</groupId>
<groupId>fun.bm.lophine</groupId>
<artifactId>luminol-api</artifactId>
<version>$VERSION</version>
</dependency>
</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
### 获取帮助
当您遇到任何问题时,请向我们提问,我们将尽力解决,但请记得清楚地描述您的问题并提供足够的日志等信息。
- 📋 [提交 Issue](https://github.com/LuminolMC/Lophine/issues)
- 💬 [GitHub Discussions](https://github.com/LuminolMC/Lophine/discussions)
- 📖 [项目文档](./docs/)
## 贡献代码
## 🐛 问题反馈
可查看 [Contributing](./docs/CONTRIBUTING.md)
当您遇到任何问题时,请向我们提问,我们将尽力解决。请记得:
## BStats
- 📝 **清楚描述问题** - 详细说明问题的具体表现
- 📋 **提供完整日志** - 包含错误日志和相关配置信息
- 🔍 **环境信息** - 说明服务器版本、插件列表等环境详情
- 🔄 **复现步骤** - 如果可能,请提供问题复现的具体步骤
## 🤝 贡献代码
我们欢迎社区贡献!详细的贡献指南请查看:
- 📖 [贡献指南 (中文)](./docs/CONTRIBUTING.md)
- 📖 [Contributing Guide (English)](./docs/CONTRIBUTING_EN.md)
## 📊 项目统计
### 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" />
@@ -106,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>
+103 -51
View File
@@ -1,101 +1,149 @@
<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>Lophine 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
![Created At](https://img.shields.io/github/created-at/LuminolMC/Lophine?style=flat-square)
[![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
### Stable Releases
All release versions can be found on the [Releases](https://github.com/LuminolMC/Lophine/releases) page.
- Supported some Bukkit plugin
- Configurable vanilla features
- Tpsbar support
- Useful fixes of bugs of folia
- Linear and BufferedLinear(AKA. b_linear)(a reimplementation of linear) region format support
- Useful optimizations to improve the performance of single threaded region
- More functions
### Development Builds
If you want to experience the latest features, you can build it yourself following the steps below.
## Download
### Build Steps
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).
```bash
# Clone the project
git clone https://github.com/LuminolMC/Lophine.git
cd Lophine
## 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
# Apply patches and build Paperclip JAR
./gradlew applyAllPatches && ./gradlew createMojmapPaperclipJar
```
## Using API
After building, you can find the generated JAR file in the `lophine-server/build/libs` directory.
For gradle:
## 🔌 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")
compileOnly("fun.bm.lophine:lophine-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>fun.bm.lophine</groupId>
<artifactId>luminol-api</artifactId>
<version>$VERSION</version>
<scope>provided</scope>
</dependency>
</dependencies>
```
## Contact
## 💬 Community & Support
> If you are interested in this project or have any issue, feel free to ask us.
> If you're interested in this project or have any questions, feel free to ask us.
**QQ Group: [1015048616](http://qm.qq.com/cgi-bin/qm/qr?_wv=1027&k=hTPlI5j6XB8pgk4sdx6RkjhBPGG1r4IR&authKey=pnu6uCKQP7Sja2CJWC15Qi3BeI%2FAsh8tU4m5muufMBjbB3zz%2BwHBZCTRRdSNKhld&noverify=0&group_code=1015048616)** | QQ Channel: [Click To Join](https://pd.qq.com/s/eq9krf9j) | Telegram: [Click To Join](https://t.me/LuminolMinecraft) | Discord: [Click To Join](https://discord.gg/Qd7m3V6eDx)
### Join Our Community
## About Issue
- **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)
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>
### Get Help
## Pull Requests
- 📋 [Submit Issues](https://github.com/LuminolMC/Lophine/issues)
- 💬 [GitHub Discussions](https://github.com/LuminolMC/Lophine/discussions)
- 📖 [Project Documentation](./docs/)
See [Contributing](./docs/CONTRIBUTING_EN.md)
## 🐛 Bug Reports
## BStats
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](https://bstats.org/signatures/server-implementation/Lophine.svg "bStats")
## Special Thanks
## 🎉 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
### Project Sponsors
![legacy-lands-logo](public/image/legacy-lands-logo.png)
<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>
## Please ⭐star us!
---
## ⭐ Give Us a Star!
> Every free ⭐Star you give is the motivation for our every step forward.
### Star History
<a href="https://star-history.com/#LuminolMC/Luminol&LuminolMC/LightingLuminol&LuminolMC/Lophine&Date">
<picture>
@@ -104,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")
+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=baa7a4f47b93c32527ea671d16193af8def87486
luminolRef=e324edacf6651633674a0abce91ffbd290943dd5
org.gradle.configuration-cache=true
org.gradle.caching=true
+1 -1
View File
@@ -1,6 +1,6 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-8.14.1-bin.zip
distributionUrl=https\://services.gradle.org/distributions/gradle-9.2.0-bin.zip
networkTimeout=10000
validateDistributionUrl=true
zipStoreBase=GRADLE_USER_HOME
@@ -0,0 +1,47 @@
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] Leaves: Add fakeplayer api
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/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,37 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Helvetica Volubi <suisuroru@blue-millennium.fun>
Date: Sun, 16 Nov 2025 15:50:28 +0800
Subject: [PATCH] Leaves: Replay Mod API
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/org/bukkit/Bukkit.java b/src/main/java/org/bukkit/Bukkit.java
index 89f00f4b6ff64305fa44ebb71f22a113c1296d26..e9d20fe8b88aa63fb62effd430b5e22c8dbe1307 100644
--- a/src/main/java/org/bukkit/Bukkit.java
+++ b/src/main/java/org/bukkit/Bukkit.java
@@ -3063,4 +3063,10 @@ public final class Bukkit {
return server.getBotManager();
}
// Leaves end - Bot API
+
+ // Leaves start - Photographer API
+ public static @NotNull org.leavesmc.leaves.entity.photographer.PhotographerManager getPhotographerManager() {
+ return server.getPhotographerManager();
+ }
+ // Leaves end - Photographer API
}
diff --git a/src/main/java/org/bukkit/Server.java b/src/main/java/org/bukkit/Server.java
index c7ae463fb166750bbd0638a2ccf81753b293a381..4caadc8f9b328d978ece84c1480e32f24868531f 100644
--- a/src/main/java/org/bukkit/Server.java
+++ b/src/main/java/org/bukkit/Server.java
@@ -2762,4 +2762,8 @@ public interface Server extends PluginMessageRecipient, net.kyori.adventure.audi
*/
@NotNull org.leavesmc.leaves.entity.bot.BotManager getBotManager();
// Leaves end - Bot API
+
+ // Leaves start - Photographer API
+ @NotNull org.leavesmc.leaves.entity.photographer.PhotographerManager getPhotographerManager();
+ // Leaves end - Photographer 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,43 @@
/*
* 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.photographer;
import org.bukkit.entity.Player;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.io.File;
public interface Photographer extends Player {
@NotNull String getId();
void setRecordFile(@NotNull File file);
void stopRecording();
void stopRecording(boolean async);
void stopRecording(boolean async, boolean save);
void pauseRecording();
void resumeRecording();
void setFollowPlayer(@Nullable Player player);
}
@@ -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.entity.photographer;
import org.bukkit.Location;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.leavesmc.leaves.replay.BukkitRecorderOption;
import java.util.Collection;
import java.util.UUID;
public interface PhotographerManager {
@Nullable Photographer getPhotographer(@NotNull UUID uuid);
@Nullable Photographer getPhotographer(@NotNull String id);
@Nullable Photographer createPhotographer(@NotNull String id, @NotNull Location location);
@Nullable Photographer createPhotographer(@NotNull String id, @NotNull Location location, @NotNull BukkitRecorderOption recorderOption);
void removePhotographer(@NotNull String id);
void removePhotographer(@NotNull UUID uuid);
void removeAllPhotographers();
Collection<Photographer> getPhotographers();
}
@@ -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;
}
}
@@ -0,0 +1,35 @@
/*
* This file is part of Leaves (https://github.com/LeavesMC/Leaves)
*
* Leaves is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Leaves is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Leaves. If not, see <https://www.gnu.org/licenses/>.
*/
package org.leavesmc.leaves.replay;
public class BukkitRecorderOption {
// public int recordDistance = -1;
public String serverName = "Lophine"; // Lophine - rebrand to Lophine to avoid confusion
public BukkitRecordWeather forceWeather = BukkitRecordWeather.NULL;
public int forceDayTime = -1;
public boolean ignoreChat = false;
// public boolean ignoreItem = false;
public enum BukkitRecordWeather {
CLEAR,
RAIN,
THUNDER,
NULL
}
}
+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")
@@ -276,14 +_,14 @@
@@ -275,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,
@@ -447,7 +_,7 @@
@@ -446,7 +_,7 @@
}
fill {
@@ -5,27 +5,27 @@ 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 a67fc80eb3182415557b4bdf7f146aefb43103da..52fec4c88fee3c027c954365538516aaa8335c7f 100644
index 0bebc2cc87fb9c1c526e4de4f3bc157552307a72..cb1657939b86c039fcfe6403d3f202c2c5879e88 100644
--- a/src/main/java/me/earthme/luminol/config/ConfigManager.java
+++ b/src/main/java/me/earthme/luminol/config/ConfigManager.java
@@ -21,6 +21,7 @@ public class ConfigManager {
@@ -23,6 +23,7 @@ public class ConfigManager {
public static void initConfigs() {
configfiles.put("luminol", ConfigsInstance.of(new File("luminol_config"), "luminol", "me.earthme.luminol.config.modules"));
+ configfiles.put("lophine", ConfigsInstance.of(new File("lophine_config"), "lophine", "fun.bm.lophine.config.modules")); // add lophine config to global config
configfiles.put("luminol", ConfigsInstance.of("luminol", "me.earthme.luminol.config.modules"));
+ configfiles.put("lophine", ConfigsInstance.of("lophine", "fun.bm.lophine.config.modules")); // add lophine config to global config
preLoad();
}
diff --git a/src/main/java/me/earthme/luminol/config/modules/misc/ServerModNameConfig.java b/src/main/java/me/earthme/luminol/config/modules/misc/ServerModNameConfig.java
index ded1f0667327a70a923ebda55c41b1c9094d3e37..57ddc0832b919b115b85c81ac30f136e32f1e77e 100644
index df5d926fb4401dd93d658fe14ae4d9d58cff68f9..b6d84fea596cfa78032151ab2f206b6b3d5dd197 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(category = EnumConfigCategory.MISC, name = "server_mod_name")
public class ServerModNameConfig implements IConfigModule {
@ConfigInfo(baseName = "name")
@ConfigInfo(name = "name")
- public static String serverModName = "Luminol";
+ public static String serverModName = "Lophine";
@ConfigInfo(baseName = "vanilla_spoof")
@ConfigInfo(name = "vanilla_spoof")
public static boolean fakeVanilla = false;
@@ -1,30 +1,30 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Helvetica Volubi <suisuroru@blue-millennium.fun>
Date: Mon, 28 Jul 2025 23:02:47 +0800
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/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/PaperPacketLimiterConfig.java b/src/main/java/me/earthme/luminol/config/modules/misc/PaperPacketLimiterConfig.java
index c269446dda05cfc848407f80f53820c8fcd0c7b4..e69ffae53aa24496a29a4c4890cdcb366d2bec12 100644
--- a/src/main/java/me/earthme/luminol/config/modules/misc/PaperPacketLimiterConfig.java
+++ b/src/main/java/me/earthme/luminol/config/modules/misc/PaperPacketLimiterConfig.java
@@ -5,6 +5,7 @@ import me.earthme.luminol.config.IConfigModule;
import me.earthme.luminol.config.flags.ConfigInfo;
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 4051384e82dba0416838c3c9889a6e368367b601..a5535b479cdcca870d0c8be6ed6fbcb32f266ac2 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", directory = {"experiment", "force_the_data_command_to_be_enabled"})
@ConfigInfo(name = "enable_data_command")
public static boolean data = false;
+ @TransformedConfig(name = "command_block_enabled", directory = {"experiment", "command"}, originInstance = "lophine")
@TransformedConfig(name = "enabled", directory = {"experiment", "force_enable_command_block_command_execution"})
@ConfigInfo(name = "enable_command_block", comments = """
Force to enable command blocks.
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 35e6014f04093050a9a62b895b43e5c5d8c6a28e..11abfb884f972fd73ca4c13a6e309de1bcd23fd5 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(category = EnumConfigCategory.OPTIMIZATIONS, name = "force_disable_packet_limiter_of_paper")
public class PaperPacketLimiterConfig implements IConfigModule {
+ @me.earthme.luminol.config.flags.TransformedConfig(name = "unlimit-packet", category = {"misc", "network"}, originInstance = "lophine")
@ConfigInfo(baseName = "force_disable")
+ @TransformedConfig(name = "unlimit-packet", directory = {"misc", "network"}, originInstance = "lophine")
@TransformedConfig(name = "force_disable", directory = {"misc", "force_disable_packet_limiter_of_paper"})
@ConfigInfo(name = "force_disable")
public static boolean forceDisable = false;
@@ -8,72 +8,35 @@ As a part of : Leaves (https://github.com/LeavesMC/Leaves/blob/d93e9766d3797d130
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 5995c499298454a060433746c64be5fcc8d202e2..3452399de0fa464ce04a7cf1a05b440346bef662 100644
index 112dc08eb62c4c50fb4859f51a366cedf027ca00..b5f661fd20b382725ad117c56ce2c94ded32b1b7 100644
--- a/src/main/java/org/leavesmc/leaves/util/ItemOverstackUtils.java
+++ b/src/main/java/org/leavesmc/leaves/util/ItemOverstackUtils.java
@@ -17,29 +17,30 @@
@@ -17,6 +17,7 @@
package org.leavesmc.leaves.util;
+import fun.bm.lophine.config.modules.misc.ContainerExpansionConfig;
//import io.papermc.paper.datacomponent.DataComponentTypes;
//import io.papermc.paper.datacomponent.item.ItemEnchantments;
+import fun.bm.lophine.config.modules.function.ContainerExpansionConfig;
import io.papermc.paper.datacomponent.DataComponentTypes;
import io.papermc.paper.datacomponent.item.ItemEnchantments;
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.BlockItem;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.item.component.CustomData;
-//import net.minecraft.world.item.component.ItemContainerContents;
-//import net.minecraft.world.level.block.ShulkerBoxBlock;
+import net.minecraft.world.item.component.ItemContainerContents;
+import net.minecraft.world.level.block.ShulkerBoxBlock;
//import org.bukkit.enchantments.Enchantment;
-//import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.NotNull;
//import org.leavesmc.leaves.LeavesConfig;
@@ -135,9 +136,9 @@ public class ItemOverstackUtils {
import java.util.List;
-//import java.util.Objects;
+import java.util.Objects;
import java.util.Optional;
public class ItemOverstackUtils {
private static final List<ItemUtil> overstackUtils = List.of(
-/* new ShulkerBox(),
- new CurseEnchantedBook()*/
+ new ShulkerBox()
+ // new CurseEnchantedBook() // only use shulker box part
);
public static int getItemStackMaxCount(ItemStack stack) {
@@ -109,6 +110,7 @@ public class ItemOverstackUtils {
public static float getItemStackSignalStrength(int maxStackSize, ItemStack itemStack) {
float result = (float) itemStack.getCount() / Math.min(maxStackSize, itemStack.getMaxStackSize());
+ // only use shulker box part
/* if (LeavesConfig.modify.oldMC.allowGrindstoneOverstacking && CurseEnchantedBook.isCursedEnchantedBook(itemStack)) {
return result;
}*/
@@ -134,9 +136,9 @@ public class ItemOverstackUtils {
}
}
-/* private static class ShulkerBox implements ItemUtil {
+ private static class ShulkerBox implements ItemUtil {
private static class ShulkerBox implements ItemUtil {
public static boolean shulkerBoxCheck(@NotNull ItemStack stack1, @NotNull ItemStack stack2) {
- if (LeavesConfig.modify.shulkerBox.sameNbtStackable) {
- /*if (LeavesConfig.modify.shulkerBox.sameNbtStackable) {
+ if (ContainerExpansionConfig.nbtShulkerStackable) {
return Objects.equals(stack1.getComponents(), stack2.getComponents());
}
- }*/ // Luminol
+ }
return shulkerBoxNoItem(stack1) && shulkerBoxNoItem(stack2) && Objects.equals(stack1.getComponents(), stack2.getComponents());
@@ -148,24 +150,24 @@ public class ItemOverstackUtils {
}
@@ -147,16 +148,15 @@ public class ItemOverstackUtils {
@Override
public boolean isEnabled() {
- return LeavesConfig.modify.shulkerBox.shulkerBoxStackSize > 1;
- // return LeavesConfig.modify.shulkerBox.stackableShulkerBoxes > 1; // Luminol
- return false; // Luminol
+ return ContainerExpansionConfig.shulkerCount > 1;
}
@@ -81,41 +44,33 @@ index 5995c499298454a060433746c64be5fcc8d202e2..3452399de0fa464ce04a7cf1a05b4403
public boolean tryStackItems(ItemEntity self, ItemEntity other) {
ItemStack selfStack = self.getItem();
if (!isEnabled() ||
- !(selfStack.getItem() instanceof BlockItem blockItem) ||
- !(blockItem.getBlock() instanceof ShulkerBoxBlock)
- !(selfStack.getItem() instanceof net.minecraft.world.item.BlockItem blockItem) ||
- !(blockItem.getBlock() instanceof net.minecraft.world.level.block.ShulkerBoxBlock)
+ !(selfStack.getItem() instanceof BlockItem blockItem) ||
+ !(blockItem.getBlock() instanceof ShulkerBoxBlock)
) {
return false;
}
@@ -164,8 +164,8 @@ public class ItemOverstackUtils {
ItemStack otherStack = other.getItem();
if (selfStack.getItem() == otherStack.getItem()
- && shulkerBoxCheck(selfStack, otherStack)
- && selfStack.getCount() != org.leavesmc.leaves.LeavesConfig.modify.shulkerBox.shulkerBoxStackSize) {
- int amount = Math.min(otherStack.getCount(), org.leavesmc.leaves.LeavesConfig.modify.shulkerBox.shulkerBoxStackSize - selfStack.getCount());
+ && shulkerBoxCheck(selfStack, otherStack)
&& shulkerBoxCheck(selfStack, otherStack)
- && selfStack.getCount() != /*org.leavesmc.leaves.LeavesConfig.modify.shulkerBox.stackableShulkerBoxes*/ 1) { // Luminol
- int amount = Math.min(otherStack.getCount(), /*org.leavesmc.leaves.LeavesConfig.modify.shulkerBox.stackableShulkerBoxes*/1 - selfStack.getCount()); // Luminol
+ && selfStack.getCount() != ContainerExpansionConfig.shulkerCount) {
+ int amount = Math.min(otherStack.getCount(), ContainerExpansionConfig.shulkerCount - selfStack.getCount());
selfStack.grow(amount);
self.setItem(selfStack);
@@ -187,14 +189,15 @@ public class ItemOverstackUtils {
@@ -187,9 +187,8 @@ public class ItemOverstackUtils {
@Override
public int getMaxServerStackCount(ItemStack stack) {
if (isEnabled() && stack.getItem() instanceof BlockItem bi &&
- bi.getBlock() instanceof ShulkerBoxBlock && (LeavesConfig.modify.shulkerBox.sameNbtStackable || shulkerBoxNoItem(stack))) {
- return LeavesConfig.modify.shulkerBox.shulkerBoxStackSize;
- bi.getBlock() instanceof ShulkerBoxBlock && (/*LeavesConfig.modify.shulkerBox.sameNbtStackable ||*/ shulkerBoxNoItem(stack))) { // Luminol
- // return LeavesConfig.modify.shulkerBox.stackableShulkerBoxes; // Luminol
- return 1; // Luminol
+ bi.getBlock() instanceof ShulkerBoxBlock && (ContainerExpansionConfig.nbtShulkerStackable || shulkerBoxNoItem(stack))) {
+ return ContainerExpansionConfig.shulkerCount;
}
return -1;
}
}
- public static class CurseEnchantedBook implements ItemUtil {
+// only use shulker box part
+/* public static class CurseEnchantedBook implements ItemUtil {
public static boolean isCursedEnchantedBook(ItemStack stack) {
ItemEnchantments enchantments = stack.getBukkitStack().getData(DataComponentTypes.STORED_ENCHANTMENTS);
if (enchantments == null || enchantments.enchantments().size() != 1) {
@@ -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,60 +0,0 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Helvetica Volubi <suisuroru@blue-millennium.fun>
Date: Mon, 7 Jul 2025 16:30:54 +0800
Subject: [PATCH] Add config to allow infinite trade
diff --git a/net/minecraft/world/entity/npc/Villager.java b/net/minecraft/world/entity/npc/Villager.java
index 79242037e868a140513f032e74e8594c0c6ff41c..8570c5089b605125680f8558ae201550df2d0a47 100644
--- a/net/minecraft/world/entity/npc/Villager.java
+++ b/net/minecraft/world/entity/npc/Villager.java
@@ -490,8 +490,15 @@ public class Villager extends AbstractVillager implements ReputationEventHandler
}
private void resendOffersToTradingPlayer() {
- MerchantOffers offers = this.getOffers();
+ // Lophine start - infinite trade
+ MerchantOffers offers = this.getOffers().copy();
Player tradingPlayer = this.getTradingPlayer();
+ if (fun.bm.lophine.config.modules.misc.VillagerConfig.villagerInfiniteTrade) {
+ for (MerchantOffer merchantOffer : offers) {
+ merchantOffer.maxUses = 524288;
+ }
+ }
+ // Lophine end - infinite trade
if (tradingPlayer != null && !offers.isEmpty()) {
tradingPlayer.sendMerchantOffers(
tradingPlayer.containerMenu.containerId,
diff --git a/net/minecraft/world/item/trading/Merchant.java b/net/minecraft/world/item/trading/Merchant.java
index 6e2fa442ddcd4ef3c4fe789b9ebac9dd409c9de7..28d743cc7beeb92a33683b8eea84096f94d90fb2 100644
--- a/net/minecraft/world/item/trading/Merchant.java
+++ b/net/minecraft/world/item/trading/Merchant.java
@@ -42,7 +42,14 @@ public interface Merchant {
new SimpleMenuProvider((containerId, inventory, player1) -> new MerchantMenu(containerId, inventory, this), displayName)
);
if (optionalInt.isPresent()) {
- MerchantOffers offers = this.getOffers();
+ // Lophine start - infinite trade
+ MerchantOffers offers = this.getOffers().copy();
+ if (fun.bm.lophine.config.modules.misc.VillagerConfig.villagerInfiniteTrade) {
+ for (MerchantOffer merchantOffer : offers) {
+ merchantOffer.maxUses = 5242884;
+ }
+ }
+ // Lophine end - infinite trade
if (!offers.isEmpty()) {
player.sendMerchantOffers(optionalInt.getAsInt(), offers, level, this.getVillagerXp(), this.showProgressBar(), this.canRestock());
}
diff --git a/net/minecraft/world/item/trading/MerchantOffer.java b/net/minecraft/world/item/trading/MerchantOffer.java
index 64c99df8ff305fa28c75dc03fc5ef8c61634ad84..21ec77d5167cfdf05043ea474165148fb2b0c2d9 100644
--- a/net/minecraft/world/item/trading/MerchantOffer.java
+++ b/net/minecraft/world/item/trading/MerchantOffer.java
@@ -196,7 +196,7 @@ public class MerchantOffer {
}
public boolean isOutOfStock() {
- return this.uses >= this.maxUses;
+ return (this.uses >= this.maxUses && !fun.bm.lophine.config.modules.misc.VillagerConfig.villagerInfiniteTrade); // Lophine - infinite trade
}
public void setToOutOfStock() {
@@ -1,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..3f1520bdd5ef708824712ea62974fedb35067f23 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.fixes.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();
@@ -5,7 +5,7 @@ Subject: [PATCH] Add config to enable cross region damage trace
diff --git a/net/minecraft/server/level/ServerPlayer.java b/net/minecraft/server/level/ServerPlayer.java
index 11e6197dec541b28733715f0d7eaa4c7b834d632..6b7c92fdf562dfa46eee8e8acbc82b02fc3226c5 100644
index 11e6197dec541b28733715f0d7eaa4c7b834d632..de1263404395c28209234a556347284fa557f81c 100644
--- a/net/minecraft/server/level/ServerPlayer.java
+++ b/net/minecraft/server/level/ServerPlayer.java
@@ -1345,6 +1345,13 @@ public class ServerPlayer extends Player implements ca.spottedleaf.moonrise.patc
@@ -40,7 +40,7 @@ index 11e6197dec541b28733715f0d7eaa4c7b834d632..6b7c92fdf562dfa46eee8e8acbc82b02
+ } catch (Throwable ex) {
+ LOGGER.error(ex.getMessage(), ex);
+ }
+ }, null, 1L );
+ }, null, 1L);
+ }
+ // Luminol End - Cross Region Damage trace
+
@@ -48,7 +48,7 @@ index 11e6197dec541b28733715f0d7eaa4c7b834d632..6b7c92fdf562dfa46eee8e8acbc82b02
AABB aabb = new AABB(this.blockPosition()).inflate(32.0, 10.0, 32.0);
this.level()
diff --git a/net/minecraft/world/entity/LivingEntity.java b/net/minecraft/world/entity/LivingEntity.java
index 567d63ae589705e13403814b36e35d00c241a69e..3f4b1c54f9207ba1854a8c8da965cbaca5ea517d 100644
index 0c6d48363cd65cd22b4cc0ab97be977e133b64be..e94687b3aa960f9c630f459fedc17bc01846eaf7 100644
--- a/net/minecraft/world/entity/LivingEntity.java
+++ b/net/minecraft/world/entity/LivingEntity.java
@@ -1948,6 +1948,13 @@ public abstract class LivingEntity extends Entity implements Attackable, Waypoin
@@ -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 75f327fff68097c1c9f65767552f686421e1e397..ed7421895311d89ad9726804893eff423868d375 100644
index 38092dcc3dedc6c855e5598126be47f892223547..a8b6e89035579557b59a20ddbe9b32943e6b7164 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 75f327fff68097c1c9f65767552f686421e1e397..ed7421895311d89ad9726804893eff42
// 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
@@ -6467,4 +6467,46 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess
@@ -6463,4 +6463,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,59 +0,0 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Helvetica Volubi <suisuroru@blue-millennium.fun>
Date: Wed, 2 Jul 2025 23:36:25 +0800
Subject: [PATCH] Add config to enable datapack command
diff --git a/net/minecraft/commands/Commands.java b/net/minecraft/commands/Commands.java
index 8a90aa5c145f53afbb3f2371f97cb09974f30bbb..b6e10157c572dadb1bff18f8431166966eed46d9 100644
--- a/net/minecraft/commands/Commands.java
+++ b/net/minecraft/commands/Commands.java
@@ -192,7 +192,9 @@ public class Commands {
if(me.earthme.luminol.config.modules.experiment.CommandDataConfig.enabled) {
DataCommands.register(this.dispatcher); // Folia - region threading - TODO
}
- //DataPackCommand.register(this.dispatcher, context); // Folia - region threading - TODO
+ if (fun.bm.lophine.config.modules.experiment.CommandConfig.datapack) {
+ DataPackCommand.register(this.dispatcher, context); // Folia - region threading - TODO
+ }
//DebugCommand.register(this.dispatcher); // Folia - region threading - TODO
DefaultGameModeCommands.register(this.dispatcher);
//DialogCommand.register(this.dispatcher, context); // Folia - region threading - TODO
diff --git a/net/minecraft/server/MinecraftServer.java b/net/minecraft/server/MinecraftServer.java
index 100e8fdc52c201d386e3c779d19f6b5abde39820..d08a5b553f4e5932d960fbcdeb6a43419730e32a 100644
--- a/net/minecraft/server/MinecraftServer.java
+++ b/net/minecraft/server/MinecraftServer.java
@@ -2352,6 +2352,16 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
return this.reloadResources(selectedIds, io.papermc.paper.event.server.ServerResourcesReloadedEvent.Cause.PLUGIN);
}
+ // Lophine start - region thread skip reload // TODO - add reload support
+ public void saveDatapacks(Collection<String> selectedIds) {
+ this.packRepository.setSelected(selectedIds, false); // Paper - add pendingReload flag to determine required pack loading - false as this is *after* a reload (see above)
+ WorldDataConfiguration worldDataConfiguration = new WorldDataConfiguration(
+ getSelectedPacks(this.packRepository, true), this.worldData.enabledFeatures()
+ );
+ this.worldData.setDataConfiguration(worldDataConfiguration);
+ }
+ // Lophine end - region thread skip reload // TODO - add reload support
+
public CompletableFuture<Void> reloadResources(Collection<String> selectedIds, io.papermc.paper.event.server.ServerResourcesReloadedEvent.Cause cause) {
// Paper end - Add ServerResourcesReloadedEvent
CompletableFuture<Void> completableFuture = CompletableFuture.<ImmutableList>supplyAsync(
diff --git a/net/minecraft/server/commands/ReloadCommand.java b/net/minecraft/server/commands/ReloadCommand.java
index b540a3c3a02f7242578a63477e34b9063e86f1d2..9f0adf157872e1c68cd7752a24eacdc636e8d17a 100644
--- a/net/minecraft/server/commands/ReloadCommand.java
+++ b/net/minecraft/server/commands/ReloadCommand.java
@@ -16,6 +16,12 @@ public class ReloadCommand {
private static final Logger LOGGER = LogUtils.getLogger();
public static void reloadPacks(Collection<String> selectedIds, CommandSourceStack source) {
+ // Lophine start - region thread skip reload // TODO - add reload support
+ if (true) {
+ source.getServer().saveDatapacks(selectedIds);
+ return;
+ }
+ // Lophine end - region thread skip reload // TODO - add reload support
source.getServer().reloadResources(selectedIds, io.papermc.paper.event.server.ServerResourcesReloadedEvent.Cause.COMMAND).exceptionally(throwable -> { // Paper - Add ServerResourcesReloadedEvent
LOGGER.warn("Failed to execute reload", throwable);
source.sendFailure(Component.translatable("commands.reload.failure"));
@@ -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 11697e65a7f7871920ca7bad37de07f78a3da1f8..25ccbfa12d57d540195ecc16aa54a144f8ecc0e8 100644
--- a/net/minecraft/commands/Commands.java
+++ b/net/minecraft/commands/Commands.java
@@ -204,7 +204,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);
@@ -0,0 +1,23 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Helvetica Volubi <suisuroru@blue-millennium.fun>
Date: Mon, 29 Sep 2025 22:56:03 +0800
Subject: [PATCH] Add config to enable scoreboard command
diff --git a/net/minecraft/commands/Commands.java b/net/minecraft/commands/Commands.java
index 25ccbfa12d57d540195ecc16aa54a144f8ecc0e8..22d211bee83e3ef34d6ac81dd89c0e73924bee90 100644
--- a/net/minecraft/commands/Commands.java
+++ b/net/minecraft/commands/Commands.java
@@ -229,7 +229,11 @@ public class Commands {
//RotateCommand.register(this.dispatcher); // Folia - region threading - TODO later
SayCommand.register(this.dispatcher);
//ScheduleCommand.register(this.dispatcher); // Folia - region threading
- //ScoreboardCommand.register(this.dispatcher, context); // Folia - region threading
+ // Lophine start - Add a config to enable scoreboard command
+ if (fun.bm.lophine.config.modules.experiment.CommandConfig.scoreboard) {
+ ScoreboardCommand.register(this.dispatcher, context); // Folia - region threading
+ }
+ // Lophine start - Add a config to enable scoreboard command
SeedCommand.register(this.dispatcher, selection != Commands.CommandSelection.INTEGRATED);
VersionCommand.register(this.dispatcher, selection != Commands.CommandSelection.INTEGRATED);
SetBlockCommand.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 b6e10157c572dadb1bff18f8431166966eed46d9..e2fc15d84fafde6847ecead23c55169449275e1b 100644
index 22d211bee83e3ef34d6ac81dd89c0e73924bee90..1fb359073342a657e9e493403263b56be5b3393f 100644
--- a/net/minecraft/commands/Commands.java
+++ b/net/minecraft/commands/Commands.java
@@ -245,7 +245,11 @@ public class Commands {
@@ -249,7 +249,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 b6e10157c572dadb1bff18f8431166966eed46d9..e2fc15d84fafde6847ecead23c551694
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 d08a5b553f4e5932d960fbcdeb6a43419730e32a..a0f1db50adf5163a671d3d9e9df7adb7de4250c6 100644
index 75027e73859c955a54034640f905cb5d91a6a2f4..2f4970242c754cfba8ff5656a63af07d9d0a723c 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,92 +0,0 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Helvetica Volubi <suisuroru@blue-millennium.fun>
Date: Mon, 7 Jul 2025 18:19:00 +0800
Subject: [PATCH] Add config to enable unsafe waypoint bar
diff --git a/net/minecraft/commands/Commands.java b/net/minecraft/commands/Commands.java
index e2fc15d84fafde6847ecead23c55169449275e1b..b0a06cdfaa49237f372ecd131d47289160476b07 100644
--- a/net/minecraft/commands/Commands.java
+++ b/net/minecraft/commands/Commands.java
@@ -253,7 +253,11 @@ public class Commands {
TimeCommand.register(this.dispatcher);
TitleCommand.register(this.dispatcher, context);
//TriggerCommand.register(this.dispatcher); // Folia - region threading - TODO later
- //WaypointCommand.register(this.dispatcher, context); // Folia - region threading - TODO later
+ // Lophine start - unsafe waypoint bar
+ if (fun.bm.lophine.config.modules.experiment.WaypointConfig.forceEnableWaypointUnsafe) {
+ WaypointCommand.register(this.dispatcher, context); // Folia - region threading - TODO later
+ }
+ // Lophine end - unsafe waypoint bar
WeatherCommand.register(this.dispatcher);
WorldBorderCommand.register(this.dispatcher);
if (JvmProfiler.INSTANCE.isAvailable()) {
diff --git a/net/minecraft/server/waypoints/ServerWaypointManager.java b/net/minecraft/server/waypoints/ServerWaypointManager.java
index 0f8cacbb8fe55a60e2f0c98bf36c005b29f41a4b..36e9b027ce5213f8914728293633e78e9905c1b2 100644
--- a/net/minecraft/server/waypoints/ServerWaypointManager.java
+++ b/net/minecraft/server/waypoints/ServerWaypointManager.java
@@ -22,7 +22,14 @@ public class ServerWaypointManager implements WaypointManager<WaypointTransmitte
@Override
public void trackWaypoint(WaypointTransmitter waypoint) {
- // Folia - region threading
+ // Lophine start - unsafe waypoint bar
+ if (!fun.bm.lophine.config.modules.experiment.WaypointConfig.forceEnableWaypointUnsafe) return;
+ this.waypoints.add(waypoint);
+
+ for (ServerPlayer serverPlayer : this.players) {
+ this.createConnection(serverPlayer, waypoint);
+ }
+ // Lophine end - unsafe waypoint bar
}
@Override
@@ -50,14 +57,48 @@ public class ServerWaypointManager implements WaypointManager<WaypointTransmitte
public void addPlayer(ServerPlayer player) {
// Folia - region threading
+ // Lophine start - unsafe waypoint bar
+ if (!fun.bm.lophine.config.modules.experiment.WaypointConfig.forceEnableWaypointUnsafe) return;
+ this.players.add(player);
+
+ for (WaypointTransmitter waypointTransmitter : this.waypoints) {
+ this.createConnection(player, waypointTransmitter);
+ }
+
+ if (player.isTransmittingWaypoint()) {
+ this.trackWaypoint((WaypointTransmitter)player);
+ }
+ // Lophine end - unsafe waypoint bar
}
public void updatePlayer(ServerPlayer player) {
// Folia - region threading
+ // Lophine start - unsafe waypoint bar
+ if (!fun.bm.lophine.config.modules.experiment.WaypointConfig.forceEnableWaypointUnsafe) return;
+ Map<WaypointTransmitter, WaypointTransmitter.Connection> map = this.connections.row(player);
+ SetView<WaypointTransmitter> set = Sets.difference(this.waypoints, map.keySet());
+
+ for (Entry<WaypointTransmitter, WaypointTransmitter.Connection> entry : ImmutableSet.copyOf(map.entrySet())) {
+ this.updateConnection(player, entry.getKey(), entry.getValue());
+ }
+
+ for (WaypointTransmitter waypointTransmitter : set) {
+ this.createConnection(player, waypointTransmitter);
+ }
+ // Lophine end - unsafe waypoint bar
}
public void removePlayer(ServerPlayer player) {
// Folia - region threading
+ // Lophine start - unsafe waypoint bar
+ if (!fun.bm.lophine.config.modules.experiment.WaypointConfig.forceEnableWaypointUnsafe) return;
+ this.connections.row(player).values().removeIf(connection -> {
+ connection.disconnect();
+ return true;
+ });
+ this.untrackWaypoint((WaypointTransmitter)player);
+ this.players.remove(player);
+ // Lophine end - unsafe waypoint bar
}
public void breakAllConnections() {
@@ -0,0 +1,177 @@
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 waypoint command & bar
use concurrent method to fix it (now, they are thread safe)
diff --git a/net/minecraft/commands/Commands.java b/net/minecraft/commands/Commands.java
index 1fb359073342a657e9e493403263b56be5b3393f..451a685782da46f5683ea6707289bbaf3193771f 100644
--- a/net/minecraft/commands/Commands.java
+++ b/net/minecraft/commands/Commands.java
@@ -257,7 +257,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.CommandConfig.waypoint) {
+ WaypointCommand.register(this.dispatcher, context); // Folia - region threading - TODO later
+ }
+ // Lophine end - unsafe waypoint bar
WeatherCommand.register(this.dispatcher);
WorldBorderCommand.register(this.dispatcher);
if (JvmProfiler.INSTANCE.isAvailable()) {
diff --git a/net/minecraft/server/waypoints/ServerWaypointManager.java b/net/minecraft/server/waypoints/ServerWaypointManager.java
index 0f8cacbb8fe55a60e2f0c98bf36c005b29f41a4b..65a4956ba2f0e38da7ae4f8cd7ed6e4b02c015a9 100644
--- a/net/minecraft/server/waypoints/ServerWaypointManager.java
+++ b/net/minecraft/server/waypoints/ServerWaypointManager.java
@@ -16,22 +16,31 @@ import net.minecraft.world.waypoints.WaypointManager;
import net.minecraft.world.waypoints.WaypointTransmitter;
public class ServerWaypointManager implements WaypointManager<WaypointTransmitter> {
- private final Set<WaypointTransmitter> waypoints = new HashSet<>();
- private final Set<ServerPlayer> players = new HashSet<>();
- private final Table<ServerPlayer, WaypointTransmitter, WaypointTransmitter.Connection> connections = HashBasedTable.create();
+ private final Set<WaypointTransmitter> waypoints = new java.util.concurrent.CopyOnWriteArraySet<>(); // Lophine - concurrent
+ private final Set<ServerPlayer> players = new java.util.concurrent.CopyOnWriteArraySet<>(); // Lophine - concurrent
+ private final fun.bm.lophine.utils.concurrent.AbstractConcurrentTable<ServerPlayer, WaypointTransmitter, WaypointTransmitter.Connection> connections = fun.bm.lophine.config.modules.optimizations.WayPointOptimizedTableConfig.optimizedTable ? new fun.bm.lophine.utils.concurrent.OptimizedConcurrentTable<>() : new fun.bm.lophine.utils.concurrent.ConcurrentTable<>(); // Lophine - concurrent
@Override
public void trackWaypoint(WaypointTransmitter waypoint) {
- // Folia - region threading
+ // Lophine start - unsafe waypoint bar
+ if (!fun.bm.lophine.config.modules.experiment.CommandConfig.waypoint) return;
+ this.waypoints.add(waypoint);
+
+ for (ServerPlayer serverPlayer : this.players) {
+ this.createConnection(serverPlayer, waypoint);
+ }
+ // Lophine end - unsafe waypoint bar
}
@Override
public void updateWaypoint(WaypointTransmitter waypoint) {
if (this.waypoints.contains(waypoint)) {
- Map<ServerPlayer, WaypointTransmitter.Connection> map = Tables.transpose(this.connections).row(waypoint);
- SetView<ServerPlayer> set = Sets.difference(this.players, map.keySet());
+ java.util.List<Entry<ServerPlayer, WaypointTransmitter.Connection>> map = this.connections.getXZ(waypoint); // Lophine - concurrent
+ Set<ServerPlayer> players1 = new HashSet<>(); // Lophine - concurrent
+ if (!map.isEmpty()) map.forEach((entry) -> players1.add(entry.getKey())); // Lophine - concurrent
+ SetView<ServerPlayer> set = Sets.difference(this.players, players1); // Lophine - concurrent
- for (Entry<ServerPlayer, WaypointTransmitter.Connection> entry : ImmutableSet.copyOf(map.entrySet())) {
+ for (Entry<ServerPlayer, WaypointTransmitter.Connection> entry : map) { // Lophine - concurrent
this.updateConnection(entry.getKey(), waypoint, entry.getValue());
}
@@ -43,26 +52,65 @@ public class ServerWaypointManager implements WaypointManager<WaypointTransmitte
@Override
public void untrackWaypoint(WaypointTransmitter waypoint) {
- this.connections.column(waypoint).forEach((serverPlayer, connection) -> connection.disconnect());
- Tables.transpose(this.connections).row(waypoint).clear();
+ this.connections.getXZ(waypoint).forEach((entry) -> {
+ entry.getValue().disconnect();
+ this.connections.remove(entry.getKey(), waypoint, entry.getValue());
+ }); // Lophine - concurrent
this.waypoints.remove(waypoint);
}
public void addPlayer(ServerPlayer player) {
// Folia - region threading
+ // Lophine start - unsafe waypoint bar
+ if (!fun.bm.lophine.config.modules.experiment.CommandConfig.waypoint) return;
+ this.players.add(player);
+
+ for (WaypointTransmitter waypointTransmitter : this.waypoints) {
+ this.createConnection(player, waypointTransmitter);
+ }
+
+ if (player.isTransmittingWaypoint()) {
+ this.trackWaypoint((WaypointTransmitter)player);
+ }
+ // Lophine end - unsafe waypoint bar
}
public void updatePlayer(ServerPlayer player) {
// Folia - region threading
+ // Lophine start - unsafe waypoint bar
+ if (!fun.bm.lophine.config.modules.experiment.CommandConfig.waypoint) return;
+ java.util.List<Entry<WaypointTransmitter, WaypointTransmitter.Connection>> map = this.connections.getYZ(player); // Lophine - concurrent
+ Set<WaypointTransmitter> waypoints1 = new HashSet<>();// Lophine - concurrent
+ if (!map.isEmpty()) map.forEach((entry) -> waypoints1.add(entry.getKey()));// Lophine - concurrent
+ SetView<WaypointTransmitter> set = Sets.difference(this.waypoints, waypoints1); // Lophine - concurrent
+
+ for (Entry<WaypointTransmitter, WaypointTransmitter.Connection> entry : map) { // Lophine - concurrent
+ this.updateConnection(player, entry.getKey(), entry.getValue());
+ }
+
+ for (WaypointTransmitter waypointTransmitter : set) {
+ this.createConnection(player, waypointTransmitter);
+ }
+ // Lophine end - unsafe waypoint bar
}
public void removePlayer(ServerPlayer player) {
// Folia - region threading
+ // Lophine start - unsafe waypoint bar
+ if (!fun.bm.lophine.config.modules.experiment.CommandConfig.waypoint) return;
+ this.connections.getYZ(player).forEach((entry) -> {
+ entry.getValue().disconnect();
+ this.connections.remove(player, entry.getKey(), entry.getValue());
+ }); // Lophine - concurrent
+ this.untrackWaypoint((WaypointTransmitter)player);
+ this.players.remove(player);
+ // Lophine end - unsafe waypoint bar
}
public void breakAllConnections() {
- this.connections.values().forEach(WaypointTransmitter.Connection::disconnect);
- this.connections.clear();
+ java.util.List<WaypointTransmitter.Connection> map = this.connections.getAllZ(); // Lophine - concurrent
+ if (!map.isEmpty()) map.forEach(WaypointTransmitter.Connection::disconnect); // Lophine - concurrent
+ this.connections.clearAll(); // Lophine - concurrent
}
public void remakeConnections(WaypointTransmitter waypoint) {
@@ -83,13 +131,19 @@ public class ServerWaypointManager implements WaypointManager<WaypointTransmitte
if (player != waypoint) {
if (isLocatorBarEnabledFor(player)) {
waypoint.makeWaypointConnectionWith(player).ifPresentOrElse(connection -> {
- this.connections.put(player, waypoint, connection);
+ this.connections.putOrUpdate(player, waypoint, connection);
connection.connect();
}, () -> {
- WaypointTransmitter.Connection connection = this.connections.remove(player, waypoint);
- if (connection != null) {
- connection.disconnect();
- }
+ // Lophine start - concurrent
+ java.util.List<WaypointTransmitter.Connection> c = this.connections.getZ(player, waypoint);
+ if (c.isEmpty()) return; // Lophine - concurrent
+ c.forEach((connection) -> {
+ this.connections.remove(player, waypoint, connection);
+ if (connection != null) {
+ connection.disconnect();
+ }
+ });
+ // Lophine end - concurrent
});
}
}
@@ -103,10 +157,12 @@ public class ServerWaypointManager implements WaypointManager<WaypointTransmitte
} else {
waypoint.makeWaypointConnectionWith(player).ifPresentOrElse(connection1 -> {
connection1.connect();
- this.connections.put(player, waypoint, connection1);
+ this.connections.putOrUpdate(player, waypoint, connection1);
}, () -> {
connection.disconnect();
- this.connections.remove(player, waypoint);
+ java.util.List<WaypointTransmitter.Connection> c = this.connections.getZ(player, waypoint); // Lophine - concurrent
+ if (c.isEmpty()) return; // Lophine - concurrent
+ c.forEach((connection1) -> this.connections.remove(player, waypoint, connection1)); // Lophine - concurrent
});
}
}
@@ -0,0 +1,94 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Helvetica Volubi <suisuroru@blue-millennium.fun>
Date: Wed, 2 Jul 2025 23:36:25 +0800
Subject: [PATCH] Fix datapack command save function
diff --git a/net/minecraft/server/MinecraftServer.java b/net/minecraft/server/MinecraftServer.java
index 2f4970242c754cfba8ff5656a63af07d9d0a723c..b156b5c635f931a3d4abc0584591f90476d011a6 100644
--- a/net/minecraft/server/MinecraftServer.java
+++ b/net/minecraft/server/MinecraftServer.java
@@ -2353,6 +2353,16 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
return this.reloadResources(selectedIds, io.papermc.paper.event.server.ServerResourcesReloadedEvent.Cause.PLUGIN);
}
+ // Lophine start - region thread skip reload // TODO - add reload support
+ public void saveDatapacks(Collection<String> selectedIds) {
+ this.packRepository.setSelected(selectedIds, false); // Paper - add pendingReload flag to determine required pack loading - false as this is *after* a reload (see above)
+ WorldDataConfiguration worldDataConfiguration = new WorldDataConfiguration(
+ getSelectedPacks(this.packRepository, true), this.worldData.enabledFeatures()
+ );
+ this.worldData.setDataConfiguration(worldDataConfiguration);
+ }
+ // Lophine end - region thread skip reload // TODO - add reload support
+
public CompletableFuture<Void> reloadResources(Collection<String> selectedIds, io.papermc.paper.event.server.ServerResourcesReloadedEvent.Cause cause) {
// Paper end - Add ServerResourcesReloadedEvent
CompletableFuture<Void> completableFuture = CompletableFuture.<ImmutableList>supplyAsync(
diff --git a/net/minecraft/server/commands/DataPackCommand.java b/net/minecraft/server/commands/DataPackCommand.java
index a5032b34752061d61fd9ddb4db828d35e403f5e7..3eaa9c85d06f6ecc8ed639d8befa861439390eb7 100644
--- a/net/minecraft/server/commands/DataPackCommand.java
+++ b/net/minecraft/server/commands/DataPackCommand.java
@@ -98,7 +98,7 @@ public class DataPackCommand {
dispatcher.register(
Commands.literal("datapack")
.requires(Commands.hasPermission(2))
- /*.then( // Luminol - Add back read-only datapack command
+ .then(
Commands.literal("enable")
.then(
Commands.argument("name", StringArgumentType.string())
@@ -160,11 +160,11 @@ public class DataPackCommand {
.suggests(SELECTED_PACKS)
.executes(commandContext -> disablePack(commandContext.getSource(), getPack(commandContext, "name", false)))
)
- )*/ // Luminol - Add back read-only datapack command
+ )
.then(
Commands.literal("list")
.executes(commandContext -> listPacks(commandContext.getSource()))
- // .then(Commands.literal("available").executes(commandContext -> listAvailablePacks(commandContext.getSource()))) // Luminol - Add back read-only datapack command
+ .then(Commands.literal("available").executes(commandContext -> listAvailablePacks(commandContext.getSource())))
.then(Commands.literal("enabled").executes(commandContext -> listEnabledPacks(commandContext.getSource())))
)
.then(
@@ -252,7 +252,7 @@ public class DataPackCommand {
}
private static int listPacks(CommandSourceStack source) {
- return listEnabledPacks(source) ;// + listAvailablePacks(source); // Luminol - Add back read-only datapack command
+ return listEnabledPacks(source) + listAvailablePacks(source);
}
private static int listAvailablePacks(CommandSourceStack source) {
@@ -280,7 +280,7 @@ public class DataPackCommand {
private static int listEnabledPacks(CommandSourceStack source) {
PackRepository packRepository = source.getServer().getPackRepository();
- // packRepository.reload(); // Luminol - Add back read-only datapack command
+ packRepository.reload();
Collection<? extends Pack> selectedPacks = packRepository.getSelectedPacks();
if (selectedPacks.isEmpty()) {
source.sendSuccess(() -> Component.translatable("commands.datapack.list.enabled.none"), false);
diff --git a/net/minecraft/server/commands/ReloadCommand.java b/net/minecraft/server/commands/ReloadCommand.java
index b540a3c3a02f7242578a63477e34b9063e86f1d2..bc999de32931558d0d8dc26264653dd63c1cad19 100644
--- a/net/minecraft/server/commands/ReloadCommand.java
+++ b/net/minecraft/server/commands/ReloadCommand.java
@@ -16,11 +16,17 @@ public class ReloadCommand {
private static final Logger LOGGER = LogUtils.getLogger();
public static void reloadPacks(Collection<String> selectedIds, CommandSourceStack source) {
+ // Lophine start - region thread skip reload // TODO - add reload support
+ source.getServer().saveDatapacks(selectedIds);
+ source.getSender().sendMessage("You need to restart server to load datapacks update.");
+ if (false) {
source.getServer().reloadResources(selectedIds, io.papermc.paper.event.server.ServerResourcesReloadedEvent.Cause.COMMAND).exceptionally(throwable -> { // Paper - Add ServerResourcesReloadedEvent
LOGGER.warn("Failed to execute reload", throwable);
source.sendFailure(Component.translatable("commands.reload.failure"));
return null;
});
+ }
+ // Lophine end - region thread skip reload // TODO - add reload support
}
private static Collection<String> discoverNewPacks(PackRepository packRepository, WorldData worldData, Collection<String> selectedIds) {
@@ -8,27 +8,18 @@ As a part of : Leaves (https://github.com/LeavesMC/Leaves/blob/ea91106ae57fc4cc1
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
index 8405157ec35a443e238fa6866772c839621d73d7..67f3454e6479abb241a7d94699d4f7bec22da56b 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
+ org.leavesmc.leaves.protocol.core.LeavesProtocolManager.handleTick(); // 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
@@ -57,38 +48,8 @@ index fb263fa1f30a7dfcb7ec2656abfb38e5fe88eac9..56fd1ed7ccaf96e7eedea60fbdbf7f93
}
};
}
diff --git a/net/minecraft/server/MinecraftServer.java b/net/minecraft/server/MinecraftServer.java
index a0f1db50adf5163a671d3d9e9df7adb7de4250c6..af13707933af14171447d5ab6faacb64ddf194a7 100644
--- a/net/minecraft/server/MinecraftServer.java
+++ b/net/minecraft/server/MinecraftServer.java
@@ -305,6 +305,7 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
// Folia start - regionised ticking
public final io.papermc.paper.threadedregions.RegionizedServer regionizedServer = new io.papermc.paper.threadedregions.RegionizedServer();
+ private int tickCount;
@Override
public <V> CompletableFuture<V> submit(java.util.function.Supplier<V> task) {
@@ -2226,9 +2227,16 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
return false;
}
+ // Lophine start - reuse tick count
public int getTickCount() {
- throw new UnsupportedOperationException(); // Folia - region threading
+ if (false) throw new UnsupportedOperationException(); // Folia - region threading
+ return this.tickCount;
+ }
+
+ public void handleTickCount(int tickCount1) {
+ tickCount = tickCount1;
}
+ // Lophine end - reuse tick count
public int getSpawnProtectionRadius() {
return 16;
diff --git a/net/minecraft/server/network/ServerCommonPacketListenerImpl.java b/net/minecraft/server/network/ServerCommonPacketListenerImpl.java
index 714813cba67da4a737682fe9276cb0d96fea336d..9677a3d62de11dafc8b9debe7a18ee6d14c7a64d 100644
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
@@ -0,0 +1,47 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Helvetica Volubi <suisuroru@blue-millennium.fun>
Date: Wed, 29 Oct 2025 00:09:09 +0800
Subject: [PATCH] Leaves: Configurable trading with the void
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/server/level/ServerLevel.java b/net/minecraft/server/level/ServerLevel.java
index 7584ba257d61bf650d83a2316ff012df928d54f3..4d71cc3b643f6f594c90f2f8f9e5b820ebdff080 100644
--- a/net/minecraft/server/level/ServerLevel.java
+++ b/net/minecraft/server/level/ServerLevel.java
@@ -2901,7 +2901,7 @@ public class ServerLevel extends Level implements ServerEntityGetter, WorldGenLe
// Spigot start
if (entity.getBukkitEntity() instanceof org.bukkit.inventory.InventoryHolder && (!(entity instanceof ServerPlayer) || entity.getRemovalReason() != Entity.RemovalReason.KILLED)) { // SPIGOT-6876: closeInventory clears death message
// Paper start - Fix merchant inventory not closing on entity removal
- if (entity.getBukkitEntity() instanceof org.bukkit.inventory.Merchant merchant && merchant.getTrader() != null) {
+ if (!fun.bm.lophine.config.modules.function.OldFeatureConfig.villagerVoidTrade && entity.getBukkitEntity() instanceof org.bukkit.inventory.Merchant merchant && merchant.getTrader() != null) { // Leaves - Configurable trading with the void
merchant.getTrader().closeInventory(org.bukkit.event.inventory.InventoryCloseEvent.Reason.UNLOADED);
}
// Paper end - Fix merchant inventory not closing on entity removal
diff --git a/net/minecraft/server/players/PlayerList.java b/net/minecraft/server/players/PlayerList.java
index 593dbcda4b497291eb1ecdc64b947a8d279f8be6..6c1f4f943a5be5f2e43a82cca52ca76a98226915 100644
--- a/net/minecraft/server/players/PlayerList.java
+++ b/net/minecraft/server/players/PlayerList.java
@@ -649,7 +649,7 @@ public abstract class PlayerList {
player.stopRiding();
rootVehicle.getPassengersAndSelf().forEach(entity -> {
// Paper start - Fix villager boat exploit
- if (entity instanceof net.minecraft.world.entity.npc.AbstractVillager villager) {
+ if (!fun.bm.lophine.config.modules.function.OldFeatureConfig.villagerVoidTrade && entity instanceof net.minecraft.world.entity.npc.AbstractVillager villager) { // Leaves - Configurable trading with the void
final net.minecraft.world.entity.player.Player human = villager.getTradingPlayer();
if (human != null) {
villager.setTradingPlayer(null);
diff --git a/net/minecraft/world/inventory/MerchantMenu.java b/net/minecraft/world/inventory/MerchantMenu.java
index d59f67ffe34201c63e3d9706a4434f33b6732edb..c635c7211bff9d190b7035b284dda162334fd5c2 100644
--- a/net/minecraft/world/inventory/MerchantMenu.java
+++ b/net/minecraft/world/inventory/MerchantMenu.java
@@ -74,6 +74,7 @@ public class MerchantMenu extends AbstractContainerMenu {
@Override
public boolean stillValid(Player player) {
+ if (fun.bm.lophine.config.modules.function.OldFeatureConfig.villagerVoidTrade) return this.trader.getTradingPlayer() == player; // Leaves - Configurable trading with the void
if (!checkReachable) return true; // Paper - checkReachable
return this.trader.stillValid(player);
}
@@ -28,7 +28,7 @@ index 643797124fe5a4489d0b7419b7e600c04f283ef2..04b4100da96aad50f08e8c59200eec93
public String serialize(HolderLookup.Provider levelRegistry) {
diff --git a/net/minecraft/server/commands/GiveCommand.java b/net/minecraft/server/commands/GiveCommand.java
index f7002d6752aa03603eb06e298ba1d7130cf13826..72c87f7c0392d087440db9d267a608598f6feb6d 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 {
@@ -41,7 +41,7 @@ index f7002d6752aa03603eb06e298ba1d7130cf13826..72c87f7c0392d087440db9d267a60859
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 d74a05822c6c145971dc526a62d652d35b4f5d9d..d63d8f7523938088bf9efc1c1b8ad9f0583338bb 100644
index 74ec003486f9362d2bafbad8392b9bbdf0c15882..f06b9d8ad9512d35f3f460eb77c869690321c67f 100644
--- a/net/minecraft/server/network/ServerGamePacketListenerImpl.java
+++ b/net/minecraft/server/network/ServerGamePacketListenerImpl.java
@@ -3079,7 +3079,7 @@ public class ServerGamePacketListenerImpl
@@ -229,7 +229,7 @@ index 02d2efef2dc0f0e12eac0c71fa290af706f7694d..99f109e2653eff10c011f380694bd77a
default SlotAccess getChestVehicleSlot(final int index) {
diff --git a/net/minecraft/world/inventory/AbstractContainerMenu.java b/net/minecraft/world/inventory/AbstractContainerMenu.java
index fcb2e0a722ed94acf08da06f06b20f42652896bc..9489a50df2de3d641235153c5a0217d96c1f4fa9 100644
index 2d6acceffb2cb7e55dd504018a70b05afccd9c11..bdc0ca1f625dc264bef6e5722ec66121ba48625a 100644
--- a/net/minecraft/world/inventory/AbstractContainerMenu.java
+++ b/net/minecraft/world/inventory/AbstractContainerMenu.java
@@ -295,6 +295,13 @@ public abstract class AbstractContainerMenu {
@@ -273,7 +273,7 @@ index fcb2e0a722ed94acf08da06f06b20f42652896bc..9489a50df2de3d641235153c5a0217d9
}
} else if (clickType == ClickType.THROW && this.getCarried().isEmpty() && slotId >= 0) {
Slot slot2 = this.slots.get(slotId);
@@ -656,15 +663,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++) {
@@ -292,7 +292,7 @@ index fcb2e0a722ed94acf08da06f06b20f42652896bc..9489a50df2de3d641235153c5a0217d9
itemStack.grow(itemStack1.getCount());
}
}
@@ -766,7 +773,7 @@ public abstract class AbstractContainerMenu {
@@ -770,7 +777,7 @@ public abstract class AbstractContainerMenu {
i = endIndex - 1;
}
@@ -301,7 +301,7 @@ index fcb2e0a722ed94acf08da06f06b20f42652896bc..9489a50df2de3d641235153c5a0217d9
while (!stack.isEmpty() && (reverseDirection ? i >= startIndex : i < endIndex)) {
Slot slot = this.slots.get(i);
ItemStack item = slot.getItem();
@@ -867,7 +874,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())
@@ -310,7 +310,7 @@ index fcb2e0a722ed94acf08da06f06b20f42652896bc..9489a50df2de3d641235153c5a0217d9
: flag;
}
@@ -875,7 +882,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;
@@ -319,7 +319,7 @@ index fcb2e0a722ed94acf08da06f06b20f42652896bc..9489a50df2de3d641235153c5a0217d9
default -> stack.getCount();
};
}
@@ -898,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()) {
@@ -446,10 +446,10 @@ index 10b2cc20bbf3b70e4e09dbbe14b90506e6366158..1535bb8cec8692e8d26eaff090288f1b
}
diff --git a/net/minecraft/world/level/block/entity/HopperBlockEntity.java b/net/minecraft/world/level/block/entity/HopperBlockEntity.java
index 2f8128a11e316a6ee6b283f9a411096e582c03b7..5b93eeba6c03738011551ce18332cebe9e278a8e 100644
index 3fd172f82216fa35c36e5f563819a6f01ffd4ea4..419bc939e19a306b33d4d387007c6acbae73fba3 100644
--- a/net/minecraft/world/level/block/entity/HopperBlockEntity.java
+++ b/net/minecraft/world/level/block/entity/HopperBlockEntity.java
@@ -145,7 +145,7 @@ public class HopperBlockEntity extends RandomizableContainerBlockEntity implemen
@@ -144,7 +144,7 @@ public class HopperBlockEntity extends RandomizableContainerBlockEntity implemen
public void setItem(int index, ItemStack stack) {
this.unpackLootTable(null);
this.getItems().set(index, stack);
@@ -458,7 +458,7 @@ index 2f8128a11e316a6ee6b283f9a411096e582c03b7..5b93eeba6c03738011551ce18332cebe
}
@Override
@@ -742,9 +742,9 @@ public class HopperBlockEntity extends RandomizableContainerBlockEntity implemen
@@ -740,9 +740,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
@@ -469,4 +469,4 @@ index 2f8128a11e316a6ee6b283f9a411096e582c03b7..5b93eeba6c03738011551ce18332cebe
+ 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 // Luminol - Use vanilla hopper logic
//IGNORE_TILE_UPDATES.set(Boolean.TRUE); // Paper - Perf: Optimize Hoppers // Folia - region threading // Luminol - vanilla hopper
@@ -8,14 +8,14 @@ As a part of : Leaves (https://github.com/LeavesMC/Leaves/blob/3e96b237749a960f2
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;
}
@@ -8,7 +8,7 @@ As a part of : Leaves (https://github.com/LeavesMC/Leaves/blob/79d9ef74c2684eb49
Licensed under: GPL-3.0 (https://www.gnu.org/licenses/gpl-3.0.html)
diff --git a/net/minecraft/world/effect/BadOmenMobEffect.java b/net/minecraft/world/effect/BadOmenMobEffect.java
index 80f17f33f670018240c854df589cf90cdeab6e70..cbfb4ae2dc30c2ee98ef37f44a95434a7968e320 100644
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 {
@@ -16,7 +16,7 @@ index 80f17f33f670018240c854df589cf90cdeab6e70..cbfb4ae2dc30c2ee98ef37f44a95434a
&& level.getDifficulty() != Difficulty.PEACEFUL
&& level.isVillage(serverPlayer.blockPosition())) {
+ // Leaves start - Revert raid changes
+ if (fun.bm.lophine.config.modules.misc.OldFeatureConfig.oldRaidBehavior) {
+ if (fun.bm.lophine.config.modules.function.OldFeatureConfig.oldRaidBehavior) {
+ return level.getRaids().createOrExtendRaid(serverPlayer, serverPlayer.blockPosition()) == null;
+ }
+ // Leaves end - Revert raid changes
@@ -24,7 +24,7 @@ index 80f17f33f670018240c854df589cf90cdeab6e70..cbfb4ae2dc30c2ee98ef37f44a95434a
if (raidAt == null || raidAt.getRaidOmenLevel() < raidAt.getMaxRaidOmenLevel()) {
serverPlayer.addEffect(new MobEffectInstance(MobEffects.RAID_OMEN, 600, amplifier));
diff --git a/net/minecraft/world/entity/raid/Raid.java b/net/minecraft/world/entity/raid/Raid.java
index 1910bff3f08147bfd63703097e30b9f88c84e337..5188ae86aff3051d45fe5e7d2fcc51233b7074d4 100644
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 {
@@ -32,7 +32,7 @@ index 1910bff3f08147bfd63703097e30b9f88c84e337..5188ae86aff3051d45fe5e7d2fcc5123
public boolean absorbRaidOmen(ServerPlayer player) {
- MobEffectInstance effect = player.getEffect(MobEffects.RAID_OMEN);
+ MobEffectInstance effect = player.getEffect(fun.bm.lophine.config.modules.misc.OldFeatureConfig.oldRaidBehavior ? MobEffects.BAD_OMEN : MobEffects.RAID_OMEN); // Leaves - old Raid Behavior
+ 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 {
@@ -42,7 +42,7 @@ index 1910bff3f08147bfd63703097e30b9f88c84e337..5188ae86aff3051d45fe5e7d2fcc5123
if (flag1) {
- this.waveSpawnPos = this.getValidSpawnPos(level);
+ // Leaves Start - old FindSpawnPosition
+ if (fun.bm.lophine.config.modules.misc.OldFeatureConfig.oldRaidBehavior) {
+ 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);
@@ -58,7 +58,7 @@ index 1910bff3f08147bfd63703097e30b9f88c84e337..5188ae86aff3051d45fe5e7d2fcc5123
- BlockPos blockPos = this.waveSpawnPos.orElseGet(() -> this.findRandomSpawnPos(level, 20));
+ // Leaves Start - old FindSpawnPosition
+ BlockPos blockPos;
+ if (fun.bm.lophine.config.modules.misc.OldFeatureConfig.oldRaidBehavior) {
+ if (fun.bm.lophine.config.modules.function.OldFeatureConfig.oldRaidBehavior) {
+ blockPos = this.getRavagerSpawnLocation(level, i, 20);
+ } else {
+ blockPos = this.waveSpawnPos.orElseGet(() -> this.findRandomSpawnPos(level, 20));
@@ -72,7 +72,7 @@ index 1910bff3f08147bfd63703097e30b9f88c84e337..5188ae86aff3051d45fe5e7d2fcc5123
}
- if (i > 5) {
+ if (i > (fun.bm.lophine.config.modules.misc.OldFeatureConfig.oldRaidBehavior ? 3 : 5)) { // Leaves - old FindSpawnPosition
+ 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;
@@ -81,7 +81,7 @@ index 1910bff3f08147bfd63703097e30b9f88c84e337..5188ae86aff3051d45fe5e7d2fcc5123
int i3 = this.center.getZ() + Mth.floor(Mth.sin(f2) * 32.0F * f) + level.random.nextInt(3) * Mth.floor(f);
int height = level.getHeight(Heightmap.Types.WORLD_SURFACE, i2, i3);
- if (Mth.abs(height - this.center.getY()) <= 96) {
+ if (fun.bm.lophine.config.modules.misc.OldFeatureConfig.oldRaidBehavior || Mth.abs(height - this.center.getY()) <= 96) { // Leaves - skippable
+ 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;
@@ -167,7 +167,7 @@ index 1910bff3f08147bfd63703097e30b9f88c84e337..5188ae86aff3051d45fe5e7d2fcc5123
+ // Leaves end - old FindSpawnPosition
}
diff --git a/net/minecraft/world/entity/raid/Raider.java b/net/minecraft/world/entity/raid/Raider.java
index 3e30989c99cd11c8c94d6fd194d2969e3a7efda6..b1f1bcf7f5ac002550077d5e1d4efd5c67d18063 100644
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 {
@@ -177,7 +177,7 @@ index 3e30989c99cd11c8c94d6fd194d2969e3a7efda6..b1f1bcf7f5ac002550077d5e1d4efd5c
+
+ // Leaves start - Revert raid changes
+ if (this.level() instanceof ServerLevel) {
+ if (fun.bm.lophine.config.modules.misc.OldFeatureConfig.oldRaidBehavior && !this.hasRaid()) {
+ 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) {
@@ -7,25 +7,6 @@ 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
@@ -23,7 +23,7 @@ index d0d90a25a10bbecfffceee1992af88c60d14fd87..187e48e78e69c80b25710f193813335e
+ // Leaves end - servux
}
diff --git a/net/minecraft/server/level/ServerLevel.java b/net/minecraft/server/level/ServerLevel.java
index eb3aef1f5198592eda5dafe84b91271e861b4135..576dde41a4b48aac9079cca902f4371a31470af3 100644
index 4d71cc3b643f6f594c90f2f8f9e5b820ebdff080..7fb095c544294796768ce19c4409416e2848e296 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
@@ -0,0 +1,19 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Helvetica Volubi <suisuroru@blue-millennium.fun>
Date: Sun, 16 Nov 2025 17:47:48 +0800
Subject: [PATCH] LeavesHooks
diff --git a/ca/spottedleaf/moonrise/paper/PaperHooks.java b/ca/spottedleaf/moonrise/paper/PaperHooks.java
index d9fd093f59987f471627ff20cfd2eb1a3c0dea64..78c6d425589b8147c33583ac46d6f21ec5dbefaa 100644
--- a/ca/spottedleaf/moonrise/paper/PaperHooks.java
+++ b/ca/spottedleaf/moonrise/paper/PaperHooks.java
@@ -29,7 +29,7 @@ import net.minecraft.world.phys.AABB;
import java.util.List;
import java.util.function.Predicate;
-public final class PaperHooks extends BaseChunkSystemHooks implements PlatformHooks {
+public class PaperHooks extends BaseChunkSystemHooks implements PlatformHooks {
private static final org.slf4j.Logger LOGGER = com.mojang.logging.LogUtils.getLogger();
@@ -8,7 +8,7 @@ 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 593dbcda4b497291eb1ecdc64b947a8d279f8be6..661f797b39853104b16d75ca7ba0eda156a74af3 100644
index 6c1f4f943a5be5f2e43a82cca52ca76a98226915..8e2775e873c92beed6a0e4e8ce1304ab8ca6e8d1 100644
--- a/net/minecraft/server/players/PlayerList.java
+++ b/net/minecraft/server/players/PlayerList.java
@@ -1087,6 +1087,10 @@ public abstract class PlayerList {
@@ -17,7 +17,7 @@ index 593dbcda4b497291eb1ecdc64b947a8d279f8be6..661f797b39853104b16d75ca7ba0eda1
} // 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,7 +115,7 @@ 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 de99652629c7301a4bc0e9ec48bed9a26a338f0d..4383e87560bb05acf9c50758f38a3cb69ee5c5c7 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 implemen
@@ -124,7 +124,7 @@ index de99652629c7301a4bc0e9ec48bed9a26a338f0d..4383e87560bb05acf9c50758f38a3cb6
- 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;
@@ -143,7 +143,7 @@ index de99652629c7301a4bc0e9ec48bed9a26a338f0d..4383e87560bb05acf9c50758f38a3cb6
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;
@@ -161,7 +161,7 @@ index de99652629c7301a4bc0e9ec48bed9a26a338f0d..4383e87560bb05acf9c50758f38a3cb6
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);
@@ -6,7 +6,7 @@ Subject: [PATCH] Compatibility fix for Raid Revert and Cross Region Damage
diff --git a/net/minecraft/world/entity/LivingEntity.java b/net/minecraft/world/entity/LivingEntity.java
index 3f4b1c54f9207ba1854a8c8da965cbaca5ea517d..a8348a073682eec31990e9ab963b11ad32cf8bf8 100644
index e94687b3aa960f9c630f459fedc17bc01846eaf7..f4f4f886b6558d39d292a45665953f497f84be53 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
@@ -40,7 +40,7 @@ index 3f4b1c54f9207ba1854a8c8da965cbaca5ea517d..a8348a073682eec31990e9ab963b11ad
if (this.getType().is(EntityTypeTags.IMMUNE_TO_INFESTED)) {
return !effectInstance.is(MobEffects.INFESTED);
diff --git a/net/minecraft/world/entity/raid/Raider.java b/net/minecraft/world/entity/raid/Raider.java
index b1f1bcf7f5ac002550077d5e1d4efd5c67d18063..6120f7b970ff05867524ee9f1433d46843d57483 100644
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 {
@@ -5,7 +5,7 @@ Subject: [PATCH] Disable some check for operators
diff --git a/net/minecraft/server/network/ServerGamePacketListenerImpl.java b/net/minecraft/server/network/ServerGamePacketListenerImpl.java
index d63d8f7523938088bf9efc1c1b8ad9f0583338bb..b40059553089207ab1c6628beefadc674ec43d4e 100644
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
@@ -5,10 +5,18 @@ Subject: [PATCH] I18n support
diff --git a/net/minecraft/locale/Language.java b/net/minecraft/locale/Language.java
index 7b9e2a1a208b46a69c16e6afd8b502259893574f..db46a31ea8a36562e548eb07f0c4cf9b9e8c3360 100644
index 7b9e2a1a208b46a69c16e6afd8b502259893574f..c300ae4363298fecef8fa9bf739f01c0a11e145f 100644
--- a/net/minecraft/locale/Language.java
+++ b/net/minecraft/locale/Language.java
@@ -65,7 +65,7 @@ public abstract class Language {
@@ -36,6 +36,7 @@ public abstract class Language {
Map<String, String> map = new HashMap<>();
BiConsumer<String, String> biConsumer = map::put;
parseTranslations(biConsumer, "/assets/minecraft/lang/en_us.json");
+ fun.bm.lophine.utils.ServerI18nUtil.loadLophineI18nDefault(biConsumer);
deprecatedTranslationsInfo.applyToMap(map);
final Map<String, String> map1 = Map.copyOf(map);
return new Language() {
@@ -65,7 +66,7 @@ public abstract class Language {
};
}
@@ -31,7 +39,7 @@ index cf72cb8b418239b56c755486b03a321672e16bc3..b69c06607ca900beeebf4892a978442d
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 c6c6432c2efeb7b95c2894b188cd966321ae3186..1cd9af71ee66df5f277748c0a5a59a30bd57accc 100644
index 392d75548f7eb271a6320ac30eb301a61071d465..0e278f7b9ed85ce8c3cedd9154f817b30ff1e48a 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
@@ -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 6b7c92fdf562dfa46eee8e8acbc82b02fc3226c5..144b2c9396b8162f9cce8b343d998907bf7fb5aa 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
@@ -29,7 +29,7 @@ index 6b7c92fdf562dfa46eee8e8acbc82b02fc3226c5..144b2c9396b8162f9cce8b343d998907
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,819 @@
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: 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..58694e28b3ff85ef038fe2ada2672e6d3b074fa4 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;
+ public volatile PacketListener packetListener; // Leaves - private -> protected // Lophine protected -> public
@Nullable
private DisconnectionDetails disconnectionDetails;
private boolean encrypted;
diff --git a/net/minecraft/server/MinecraftServer.java b/net/minecraft/server/MinecraftServer.java
index b156b5c635f931a3d4abc0584591f90476d011a6..81726e5782d25ff35a2bea8820a62786e46148b1 100644
--- a/net/minecraft/server/MinecraftServer.java
+++ b/net/minecraft/server/MinecraftServer.java
@@ -347,6 +347,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<>();
@@ -1038,6 +1040,11 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
// Folia end - region threading
public void stopServer() {
+ // Leaves end - save or remove bot
+ if (!this.getBotList().forceShutdown && !this.getBotList().removeAll()) {
+ return;
+ }
+ // Leaves end - save or remove bot
// Folia start - region threading
// halt scheduler
// don't wait, we may be on a scheduler thread
@@ -1588,7 +1595,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;
@@ -1912,6 +1919,7 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
public void tickConnection() {
this.getConnection().tick();
+ this.botList.networkTick(); // Leaves - fakeplayer
}
private void synchronizeTime(ServerLevel level) {
@@ -2983,6 +2991,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 0e278f7b9ed85ce8c3cedd9154f817b30ff1e48a..09d4b1d68c3f046ef304016a1afe7a3038dfae3e 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..8ad6d5a2522cfbe316fec2c3b918e2e1b9af3d1b 100644
--- a/net/minecraft/server/level/ChunkMap.java
+++ b/net/minecraft/server/level/ChunkMap.java
@@ -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 7fb095c544294796768ce19c4409416e2848e296..3393fb585747aaf3fa270c4bfe06ed4e9174d519 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);
@@ -2827,6 +2835,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);
}
@@ -2914,6 +2927,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 8e2775e873c92beed6a0e4e8ce1304ab8ca6e8d1..580f114fd99b017403c61e994d0c947d00e5115d 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, true);
+ }
+ 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 65a4956ba2f0e38da7ae4f8cd7ed6e4b02c015a9..044ded5ec81905e38efe8812ff6d50259d761385 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) {
@@ -63,6 +68,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 a8b6e89035579557b59a20ddbe9b32943e6b7164..477241699752567df7695d33fa629fb1d5109e0a 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;
@@ -0,0 +1,20 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Helvetica Volubi <suisuroru@blue-millennium.fun>
Date: Fri, 3 Oct 2025 19:43:36 +0800
Subject: [PATCH] Modify merge ItemEntity logic
Add followTickSequenceMerge to modify merge items, due to Paper's modification of the merge radius, when the merge radius is large and stacks containing many items get stuck in an unexpected position, individual items may never reach their destination. This configuration option is added to fix this behavior.
diff --git a/net/minecraft/world/entity/item/ItemEntity.java b/net/minecraft/world/entity/item/ItemEntity.java
index e5e8c752129a6ebb0dd1315c9b8ec459be11ef07..dfd202a74ec7f2cdb9cdc208a6428090a110f811 100644
--- a/net/minecraft/world/entity/item/ItemEntity.java
+++ b/net/minecraft/world/entity/item/ItemEntity.java
@@ -295,7 +295,7 @@ public class ItemEntity extends Entity implements TraceableEntity, ChangePublish
ItemStack item = this.getItem();
ItemStack item1 = itemEntity.getItem();
if (Objects.equals(this.target, itemEntity.target) && areMergable(item, item1)) {
- if (item1.getCount() < item.getCount()) {
+ if (fun.bm.lophine.config.modules.misc.ItemEntityConfig.followTickSequenceMerge || item1.getCount() < item.getCount()) { // Lophine - add follow Tick Sequence Merge, see Paper#13073
merge(this, item, itemEntity, item1);
} else {
merge(itemEntity, item1, this, item);
@@ -0,0 +1,27 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: violetc <58360096+s-yh-china@users.noreply.github.com>
Date: Mon, 3 Feb 2025 16:51:01 +0800
Subject: [PATCH] Leaves: Syncmatica Protocol
This patch is Powered by Syncmatica(https://github.com/End-Tech/syncmatica)
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/server/network/ServerGamePacketListenerImpl.java b/net/minecraft/server/network/ServerGamePacketListenerImpl.java
index e17f9629369f8653b484b4cc71de4874b5faa4d6..9be4bc9e78c22c12ba594b16dac6901023f0fd47 100644
--- a/net/minecraft/server/network/ServerGamePacketListenerImpl.java
+++ b/net/minecraft/server/network/ServerGamePacketListenerImpl.java
@@ -330,9 +330,12 @@ public class ServerGamePacketListenerImpl
this.signedMessageDecoder = SignedMessageChain.Decoder.unsigned(player.getUUID(), server::enforceSecureProfile);
this.chatMessageChain = new FutureChain(server.chatExecutor); // CraftBukkit - async chat
this.tickEndEvent = new io.papermc.paper.event.packet.ClientTickEndEvent(player.getBukkitEntity()); // Paper - add client tick end event
+ this.exchangeTarget = new org.leavesmc.leaves.protocol.syncmatica.exchange.ExchangeTarget(this); // Leaves - Syncmatica Protocol
this.playerGameConnection = new io.papermc.paper.connection.PaperPlayerGameConnection(this); // Paper
}
+ public final org.leavesmc.leaves.protocol.syncmatica.exchange.ExchangeTarget exchangeTarget; // Leaves - Syncmatica Protocol
+
// Paper start - configuration phase API
@Override
public io.papermc.paper.connection.PlayerCommonConnection getApiConnection() {
@@ -0,0 +1,25 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: violetc <58360096+s-yh-china@users.noreply.github.com>
Date: Mon, 3 Feb 2025 13:03:42 +0800
Subject: [PATCH] Leaves: BBOR Protocol
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/world/level/chunk/LevelChunk.java b/net/minecraft/world/level/chunk/LevelChunk.java
index 0d906776e10605ecfe7c681dbf313cf25080d2e8..1f44a59e46f86ca055e89631d831a38f4c3e238e 100644
--- a/net/minecraft/world/level/chunk/LevelChunk.java
+++ b/net/minecraft/world/level/chunk/LevelChunk.java
@@ -754,6 +754,11 @@ public class LevelChunk extends ChunkAccess implements ca.spottedleaf.moonrise.p
public void setLoaded(boolean loaded) {
this.loaded = loaded;
+ // Leaves start - bbor
+ if (loaded) {
+ org.leavesmc.leaves.protocol.BBORProtocol.onChunkLoaded(this);
+ }
+ // Leaves end - bbor
}
public Level getLevel() {
@@ -0,0 +1,114 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: violetc <58360096+s-yh-china@users.noreply.github.com>
Date: Mon, 3 Feb 2025 13:33:19 +0800
Subject: [PATCH] Leaves: Jade Protocol
This patch is Powered by Jade(https://github.com/Snownee/Jade)
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/world/entity/animal/armadillo/Armadillo.java b/net/minecraft/world/entity/animal/armadillo/Armadillo.java
index c1798db2972c8f2a343cf6e16fd9354ff212d906..a8d617b16ab2b2c0cdb289a0aa05fa171940cd7e 100644
--- a/net/minecraft/world/entity/animal/armadillo/Armadillo.java
+++ b/net/minecraft/world/entity/animal/armadillo/Armadillo.java
@@ -63,7 +63,7 @@ public class Armadillo extends Animal {
public final AnimationState rollOutAnimationState = new AnimationState();
public final AnimationState rollUpAnimationState = new AnimationState();
public final AnimationState peekAnimationState = new AnimationState();
- private int scuteTime;
+ public int scuteTime; // Leaves - private -> public
private boolean peekReceivedClient = false;
public Armadillo(EntityType<? extends Animal> entityType, Level level) {
diff --git a/net/minecraft/world/entity/animal/frog/Tadpole.java b/net/minecraft/world/entity/animal/frog/Tadpole.java
index 17f58246849ed407821a987b200cc765eb7943f9..ac27df3ba0ce9bbdf2f32ea87171fbb9407008d6 100644
--- a/net/minecraft/world/entity/animal/frog/Tadpole.java
+++ b/net/minecraft/world/entity/animal/frog/Tadpole.java
@@ -254,7 +254,7 @@ public class Tadpole extends AbstractFish {
}
}
- private int getTicksLeftUntilAdult() {
+ public int getTicksLeftUntilAdult() { // Leaves - private -> public
return Math.max(0, ticksToBeFrog - this.age);
}
diff --git a/net/minecraft/world/level/storage/loot/LootPool.java b/net/minecraft/world/level/storage/loot/LootPool.java
index 6901e629d941e22e64d83eed4e8cfee3165a96a1..fdc26c8d8c82c20534c57af2a0281b99998cc9f6 100644
--- a/net/minecraft/world/level/storage/loot/LootPool.java
+++ b/net/minecraft/world/level/storage/loot/LootPool.java
@@ -37,7 +37,7 @@ public class LootPool {
)
.apply(instance, LootPool::new)
);
- private final List<LootPoolEntryContainer> entries;
+ public final List<LootPoolEntryContainer> entries; // Leaves - private -> public
private final List<LootItemCondition> conditions;
private final Predicate<LootContext> compositeCondition;
private final List<LootItemFunction> functions;
diff --git a/net/minecraft/world/level/storage/loot/LootTable.java b/net/minecraft/world/level/storage/loot/LootTable.java
index 8612cdf7161f8ddff60a6478cc901318b8f958ba..07a962d647baa99b0e1bf3898a07cc914e91397e 100644
--- a/net/minecraft/world/level/storage/loot/LootTable.java
+++ b/net/minecraft/world/level/storage/loot/LootTable.java
@@ -50,7 +50,7 @@ public class LootTable {
public static final LootTable EMPTY = new LootTable(LootContextParamSets.EMPTY, Optional.empty(), List.of(), List.of());
private final ContextKeySet paramSet;
private final Optional<ResourceLocation> randomSequence;
- private final List<LootPool> pools;
+ public final List<LootPool> pools; // Leaves - private -> public
private final List<LootItemFunction> functions;
private final BiFunction<ItemStack, LootContext, ItemStack> compositeFunction;
public org.bukkit.craftbukkit.CraftLootTable craftLootTable; // CraftBukkit
diff --git a/net/minecraft/world/level/storage/loot/entries/CompositeEntryBase.java b/net/minecraft/world/level/storage/loot/entries/CompositeEntryBase.java
index eeaa49e9f70a18b5d39493aeff73f31b05ac2faa..8cd0403d7873c4c37caef75935b06b056c3d951d 100644
--- a/net/minecraft/world/level/storage/loot/entries/CompositeEntryBase.java
+++ b/net/minecraft/world/level/storage/loot/entries/CompositeEntryBase.java
@@ -16,7 +16,7 @@ public abstract class CompositeEntryBase extends LootPoolEntryContainer {
return "Empty children list";
}
};
- protected final List<LootPoolEntryContainer> children;
+ public final List<LootPoolEntryContainer> children; // Leaves - private -> public
private final ComposableEntryContainer composedChildren;
protected CompositeEntryBase(List<LootPoolEntryContainer> children, List<LootItemCondition> conditions) {
diff --git a/net/minecraft/world/level/storage/loot/entries/LootPoolEntryContainer.java b/net/minecraft/world/level/storage/loot/entries/LootPoolEntryContainer.java
index 65e27bce9e59ef97bc8b914d646fba924d0f0877..a49bdcdf37b351436e0ba6d7865f10827c4e6ab4 100644
--- a/net/minecraft/world/level/storage/loot/entries/LootPoolEntryContainer.java
+++ b/net/minecraft/world/level/storage/loot/entries/LootPoolEntryContainer.java
@@ -14,7 +14,7 @@ import net.minecraft.world.level.storage.loot.predicates.ConditionUserBuilder;
import net.minecraft.world.level.storage.loot.predicates.LootItemCondition;
public abstract class LootPoolEntryContainer implements ComposableEntryContainer {
- protected final List<LootItemCondition> conditions;
+ public final List<LootItemCondition> conditions; // Leaves - private -> public
private final Predicate<LootContext> compositeCondition;
protected LootPoolEntryContainer(List<LootItemCondition> conditions) {
diff --git a/net/minecraft/world/level/storage/loot/entries/NestedLootTable.java b/net/minecraft/world/level/storage/loot/entries/NestedLootTable.java
index 141026601cd9a4561426b85fd1f8e7dc0544fbd7..a5d7ebb93c147bf0f806ac3c9b2dc4b878573944 100644
--- a/net/minecraft/world/level/storage/loot/entries/NestedLootTable.java
+++ b/net/minecraft/world/level/storage/loot/entries/NestedLootTable.java
@@ -29,7 +29,7 @@ public class NestedLootTable extends LootPoolSingletonContainer {
return "->{inline}";
}
};
- private final Either<ResourceKey<LootTable>, LootTable> contents;
+ public final Either<ResourceKey<LootTable>, LootTable> contents; // Leaves - private -> public
private NestedLootTable(
Either<ResourceKey<LootTable>, LootTable> contents, int weight, int quality, List<LootItemCondition> conditions, List<LootItemFunction> functions
diff --git a/net/minecraft/world/level/storage/loot/predicates/CompositeLootItemCondition.java b/net/minecraft/world/level/storage/loot/predicates/CompositeLootItemCondition.java
index bae72197acc929c7ed3e964f156115d728eb2176..8f3094f42f3366a1313d70c0b27fbe5632b2082a 100644
--- a/net/minecraft/world/level/storage/loot/predicates/CompositeLootItemCondition.java
+++ b/net/minecraft/world/level/storage/loot/predicates/CompositeLootItemCondition.java
@@ -12,7 +12,7 @@ import net.minecraft.world.level.storage.loot.LootContext;
import net.minecraft.world.level.storage.loot.ValidationContext;
public abstract class CompositeLootItemCondition implements LootItemCondition {
- protected final List<LootItemCondition> terms;
+ public final List<LootItemCondition> terms; // Leaves - private -> public
private final Predicate<LootContext> composedPredicate;
protected CompositeLootItemCondition(List<LootItemCondition> terms, Predicate<LootContext> composedPredicate) {
@@ -0,0 +1,21 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: violetc <58360096+s-yh-china@users.noreply.github.com>
Date: Fri, 7 Feb 2025 14:23:43 +0800
Subject: [PATCH] Leaves: Xaero Map Protocol
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/server/players/PlayerList.java b/net/minecraft/server/players/PlayerList.java
index 580f114fd99b017403c61e994d0c947d00e5115d..11c1eb6995ec3aa896815efa59477096c39089d1 100644
--- a/net/minecraft/server/players/PlayerList.java
+++ b/net/minecraft/server/players/PlayerList.java
@@ -1247,6 +1247,7 @@ public abstract class PlayerList {
player.connection.send(new ClientboundInitializeBorderPacket(worldBorder));
player.connection.send(new ClientboundSetTimePacket(level.getGameTime(), level.getDayTime(), level.getGameRules().getBoolean(GameRules.RULE_DAYLIGHT)));
player.connection.send(new ClientboundSetDefaultSpawnPositionPacket(level.getSharedSpawnPos(), level.getSharedSpawnAngle()));
+ org.leavesmc.leaves.protocol.XaeroMapProtocol.onSendWorldInfo(player); // Leaves - xaero map protocol
if (level.isRaining()) {
// CraftBukkit start - handle player weather
// player.connection.send(new ClientboundGameEventPacket(ClientboundGameEventPacket.START_RAINING, 0.0F));
@@ -0,0 +1,43 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: violetc <58360096+s-yh-china@users.noreply.github.com>
Date: Thu, 27 Mar 2025 13:04:35 +0800
Subject: [PATCH] Leaves: Support REI protocol
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/world/item/crafting/SmithingTransformRecipe.java b/net/minecraft/world/item/crafting/SmithingTransformRecipe.java
index 9bc0a9c3577d63a0ad5489bfd4c07d5006245c5f..bb2e891539b7eb49dc7925630695149aa531f672 100644
--- a/net/minecraft/world/item/crafting/SmithingTransformRecipe.java
+++ b/net/minecraft/world/item/crafting/SmithingTransformRecipe.java
@@ -83,6 +83,12 @@ public class SmithingTransformRecipe implements SmithingRecipe {
);
}
+ // Leaves start - REI
+ public SlotDisplay getResult() {
+ return this.result.display();
+ }
+ // Leaves end - REI
+
// CraftBukkit start
@Override
public org.bukkit.inventory.Recipe toBukkitRecipe(org.bukkit.NamespacedKey id) {
diff --git a/net/minecraft/world/item/crafting/SmithingTrimRecipe.java b/net/minecraft/world/item/crafting/SmithingTrimRecipe.java
index c324896afc2ee28ebb4d426ce4a469ee847ce24d..d5b26ff7be916e07e1162536cb2ddf5674c82f46 100644
--- a/net/minecraft/world/item/crafting/SmithingTrimRecipe.java
+++ b/net/minecraft/world/item/crafting/SmithingTrimRecipe.java
@@ -85,6 +85,12 @@ public class SmithingTrimRecipe implements SmithingRecipe {
return Optional.of(this.addition);
}
+ // Leaves start
+ public Holder<TrimPattern> pattern() {
+ return pattern;
+ }
+ // Leaves end
+
@Override
public RecipeSerializer<SmithingTrimRecipe> getSerializer() {
return RecipeSerializer.SMITHING_TRIM;
@@ -0,0 +1,95 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Helvetica Volubi <suisuroru@blue-millennium.fun>
Date: Mon, 27 Oct 2025 15:38:29 +0800
Subject: [PATCH] Leaves: Wool Hopper Counter
Co-authored by: violetc <58360096+s-yh-china@users.noreply.github.com>
As a part of : Leaves (https://github.com/LeavesMC/Leaves)
Licensed under: MIT
This patch is Powered by fabric-carpet(https://github.com/gnembon/fabric-carpet)
diff --git a/net/minecraft/world/level/block/entity/HopperBlockEntity.java b/net/minecraft/world/level/block/entity/HopperBlockEntity.java
index 419bc939e19a306b33d4d387007c6acbae73fba3..96445438c5b4202e3fc93bece778ad0d7312d41c 100644
--- a/net/minecraft/world/level/block/entity/HopperBlockEntity.java
+++ b/net/minecraft/world/level/block/entity/HopperBlockEntity.java
@@ -228,8 +228,30 @@ public class HopperBlockEntity extends RandomizableContainerBlockEntity implemen
flag |= validator.getAsBoolean(); // Paper - note: this is not a validator, it's what adds/sucks in items
}
+ // Leaves start - Wool hopper counter
+ if (fun.bm.lophine.config.modules.function.WoolHopperCounterConfig.unlimitedSpeed && org.leavesmc.leaves.util.HopperCounter.isEnabled()) {
+ net.minecraft.world.item.DyeColor woolColor = org.leavesmc.leaves.util.WoolUtils.getWoolColorAtPosition(level, blockEntity.getBlockPos().relative(state.getValue(HopperBlock.FACING)));
+ if (woolColor != null) {
+ for (int i = 0; i < Short.MAX_VALUE; i++) {
+ flag |= suckInItems(level, blockEntity);
+ if (!flag) {
+ break;
+ } else {
+ woolHopperCounter(level, pos, state, HopperBlockEntity.getContainerAt(level, pos));
+ }
+ }
+ }
+ }
+ // Leaves end - Wool hopper counter
+
if (flag) {
blockEntity.setCooldown(level.spigotConfig.hopperTransfer); // Spigot
+ // Leaves start - Wool hopper counter
+ if (fun.bm.lophine.config.modules.function.WoolHopperCounterConfig.unlimitedSpeed && org.leavesmc.leaves.util.HopperCounter.isEnabled() && woolHopperCounter(level, pos, state, HopperBlockEntity.getContainerAt(level, pos))) {
+ blockEntity.setCooldown(0);
+ return true;
+ }
+ // Leaves end - Wool hopper counter
setChanged(level, pos, state);
// Leaves start - Lithium Sleeping Block Entity
if (me.earthme.luminol.config.modules.optimizations.LeavesSleepingBlockEntityConfig.enabled
@@ -461,6 +483,13 @@ public class HopperBlockEntity extends RandomizableContainerBlockEntity implemen
// Paper end - Perf: Optimize Hoppers
private static boolean ejectItems(Level level, BlockPos pos, HopperBlockEntity blockEntity) {
+ // Leaves start - hopper counter
+ if (org.leavesmc.leaves.util.HopperCounter.isEnabled()) {
+ if (woolHopperCounter(level, pos, level.getBlockState(pos), HopperBlockEntity.getContainerAt(level, pos))) {
+ return true;
+ }
+ }
+ // Leaves end - hopper counter
Container attachedContainer = me.earthme.luminol.config.modules.optimizations.LeavesSleepingBlockEntityConfig.enabled ? blockEntity.getInsertInventory(level) : getAttachedContainer(level, pos, blockEntity); // Leaves - Lithium Sleeping Block Entity
if (attachedContainer == null) {
return false;
@@ -536,6 +565,26 @@ public class HopperBlockEntity extends RandomizableContainerBlockEntity implemen
}
}
+ // Leaves start - hopper counter
+ private static boolean woolHopperCounter(Level level, BlockPos blockPos, BlockState state, @Nullable Container container) {
+ if (container == null) {
+ return false;
+ }
+ net.minecraft.world.item.DyeColor woolColor = org.leavesmc.leaves.util.WoolUtils.getWoolColorAtPosition(level, blockPos.relative(state.getValue(HopperBlock.FACING)));
+ if (woolColor != null) {
+ for (int i = 0; i < container.getContainerSize(); ++i) {
+ if (!container.getItem(i).isEmpty()) {
+ ItemStack itemstack = container.getItem(i);
+ org.leavesmc.leaves.util.HopperCounter.getCounter(woolColor).add(level, itemstack);
+ container.setItem(i, ItemStack.EMPTY);
+ }
+ }
+ return true;
+ }
+ return false;
+ }
+ // Leaves end - hopper counter
+
private static int[] getSlots(Container container, Direction direction) {
if (container instanceof WorldlyContainer worldlyContainer) {
return worldlyContainer.getSlotsForFace(direction);
@@ -680,6 +729,7 @@ public class HopperBlockEntity extends RandomizableContainerBlockEntity implemen
}
public static boolean addItem(Container container, ItemEntity item) {
+ if (fun.bm.lophine.config.modules.function.WoolHopperCounterConfig.unlimitedSpeed && org.leavesmc.leaves.util.HopperCounter.isEnabled() && item.isRemoved()) return false; // Leaves - Wool hopper counter
boolean flag = false;
// CraftBukkit start
if (org.bukkit.event.inventory.InventoryPickupItemEvent.getHandlerList().getRegisteredListeners().length > 0) { // Paper - optimize hoppers
@@ -0,0 +1,99 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: violetc <58360096+s-yh-china@users.noreply.github.com>
Date: Mon, 3 Feb 2025 13:51:26 +0800
Subject: [PATCH] Leaves: Alternative block placement Protocol
Co-authored by: violetc <58360096+s-yh-china@users.noreply.github.com>
As a part of : Leaves (https://github.com/LeavesMC/Leaves)
Licensed under: MIT
This patch is Powered by
carpet-extra(https://github.com/gnembon/carpet-extra)
MasaGadget(https://github.com/plusls/MasaGadget)
litematica(https://github.com/maruohon/litematica)
diff --git a/net/minecraft/server/network/ServerGamePacketListenerImpl.java b/net/minecraft/server/network/ServerGamePacketListenerImpl.java
index 9be4bc9e78c22c12ba594b16dac6901023f0fd47..3d9c26bc8cc14e98310f557f351871b082e5023e 100644
--- a/net/minecraft/server/network/ServerGamePacketListenerImpl.java
+++ b/net/minecraft/server/network/ServerGamePacketListenerImpl.java
@@ -2087,7 +2087,7 @@ public class ServerGamePacketListenerImpl
if (ca.spottedleaf.moonrise.common.util.TickThread.isTickThreadFor(this.player.level(), blockPos.getX() >> 4, blockPos.getZ() >> 4, 8) && this.player.canInteractWithBlock(blockPos, 1.0)) { // Folia - do not allow players to interact with blocks outside the current region
Vec3 vec3 = location.subtract(Vec3.atCenterOf(blockPos));
double d = 1.0000001;
- if (Math.abs(vec3.x()) < 1.0000001 && Math.abs(vec3.y()) < 1.0000001 && Math.abs(vec3.z()) < 1.0000001) {
+ if (fun.bm.lophine.config.modules.function.protocol.AlternativeBlockPlacementProtocolConfig.needIgnoreDistance() ||Math.abs(vec3.x()) < 1.0000001 && Math.abs(vec3.y()) < 1.0000001 && Math.abs(vec3.z()) < 1.0000001) { // Leaves - disableDistanceCheckForUseItem
Direction direction = hitResult.getDirection();
this.player.resetLastActionTime();
int maxY = this.player.level().getMaxY();
diff --git a/net/minecraft/world/item/BlockItem.java b/net/minecraft/world/item/BlockItem.java
index 6db566adf2d0df1d26221eda04aa01738df6d3d2..8f112f382190d606a87f38da8bcdf8dd5f31d1c5 100644
--- a/net/minecraft/world/item/BlockItem.java
+++ b/net/minecraft/world/item/BlockItem.java
@@ -149,7 +149,7 @@ public class BlockItem extends Item {
@Nullable
protected BlockState getPlacementState(BlockPlaceContext context) {
- BlockState stateForPlacement = this.getBlock().getStateForPlacement(context);
+ BlockState stateForPlacement = this.getBlock().getRealStateForPlacement(context); // Leaves - alternativeBlockPlacement
return stateForPlacement != null && this.canPlace(context, stateForPlacement) ? stateForPlacement : null;
}
diff --git a/net/minecraft/world/item/StandingAndWallBlockItem.java b/net/minecraft/world/item/StandingAndWallBlockItem.java
index 12c6c8aeec89a0a55633c62fe98f5a3aa75fd476..1f0e7c391d02b18e2c89700025713ec3d759f2ea 100644
--- a/net/minecraft/world/item/StandingAndWallBlockItem.java
+++ b/net/minecraft/world/item/StandingAndWallBlockItem.java
@@ -27,14 +27,14 @@ public class StandingAndWallBlockItem extends BlockItem {
@Nullable
@Override
protected BlockState getPlacementState(BlockPlaceContext context) {
- BlockState stateForPlacement = this.wallBlock.getStateForPlacement(context);
+ BlockState stateForPlacement = this.wallBlock.getRealStateForPlacement(context); // Leaves - alternativeBlockPlacement
BlockState blockState = null;
LevelReader level = context.getLevel();
BlockPos clickedPos = context.getClickedPos();
for (Direction direction : context.getNearestLookingDirections()) {
if (direction != this.attachmentDirection.getOpposite()) {
- BlockState blockState1 = direction == this.attachmentDirection ? this.getBlock().getStateForPlacement(context) : stateForPlacement;
+ BlockState blockState1 = direction == this.attachmentDirection ? this.getBlock().getRealStateForPlacement(context) : stateForPlacement; // Leaves - alternativeBlockPlacement
if (blockState1 != null && this.canPlace(level, blockState1, clickedPos)) {
blockState = blockState1;
break;
diff --git a/net/minecraft/world/level/block/Block.java b/net/minecraft/world/level/block/Block.java
index 644b9fab1d6729bf3964ebee0d6e9c46f89b8c46..634bdb554dfe757c3c9f13a79c3f644d8be4e3ea 100644
--- a/net/minecraft/world/level/block/Block.java
+++ b/net/minecraft/world/level/block/Block.java
@@ -439,6 +439,33 @@ public class Block extends BlockBehaviour implements ItemLike {
public void stepOn(Level level, BlockPos pos, BlockState state, Entity entity) {
}
+ // Leaves start - alternativeBlockPlacement
+ @Nullable
+ public BlockState getRealStateForPlacement(BlockPlaceContext ctx) {
+ BlockState vanillaState = this.getStateForPlacement(ctx);
+ switch (fun.bm.lophine.config.modules.function.protocol.AlternativeBlockPlacementProtocolConfig.alternativeBlockPlacement) {
+ case fun.bm.lophine.enums.EnumAlternativePlaceType.CARPET -> {
+ BlockState tryState = org.leavesmc.leaves.protocol.CarpetAlternativeBlockPlacement.alternativeBlockPlacement(this, ctx);
+ if (tryState != null) {
+ return tryState;
+ }
+ }
+ case fun.bm.lophine.enums.EnumAlternativePlaceType.CARPET_FIX -> {
+ BlockState tryState = org.leavesmc.leaves.protocol.CarpetAlternativeBlockPlacement.alternativeBlockPlacementFix(this, ctx);
+ if (tryState != null) {
+ return tryState;
+ }
+ }
+ case fun.bm.lophine.enums.EnumAlternativePlaceType.LITEMATICA -> {
+ if (vanillaState != null) {
+ return org.leavesmc.leaves.protocol.LitematicaEasyPlaceProtocol.applyPlacementProtocol(vanillaState, ctx);
+ }
+ }
+ }
+ return vanillaState;
+ }
+ // Leaves end - alternativeBlockPlacement
+
@Nullable
public BlockState getStateForPlacement(BlockPlaceContext context) {
return this.defaultBlockState();
@@ -0,0 +1,564 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Helvetica Volubi <suisuroru@blue-millennium.fun>
Date: Sun, 16 Nov 2025 15:48:12 +0800
Subject: [PATCH] Leaves: Replay Mod API
Co-authored by: violetc <58360096+s-yh-china@users.noreply.github.com>
As a part of : Leaves (https://github.com/LeavesMC/Leaves) & ReplayMod (https://github.com/ReplayMod)
Licensed under: GPL-3.0 (https://www.gnu.org/licenses/gpl-3.0.html)
This patch is Powered by ReplayMod (https://github.com/ReplayMod)
diff --git a/net/minecraft/commands/CommandSourceStack.java b/net/minecraft/commands/CommandSourceStack.java
index 8c005a7b9480da7c124c5bd5bfccd5ca83abbcb1..a814a67f321662f05e2c5f81666f75e128bf4ea7 100644
--- a/net/minecraft/commands/CommandSourceStack.java
+++ b/net/minecraft/commands/CommandSourceStack.java
@@ -580,7 +580,7 @@ public class CommandSourceStack implements ExecutionCommandSource<CommandSourceS
@Override
public Collection<String> getOnlinePlayerNames() {
- return this.entity instanceof ServerPlayer sourcePlayer && !sourcePlayer.getBukkitEntity().hasPermission("paper.bypass-visibility.tab-completion") ? this.getServer().getPlayerList().getPlayers().stream().filter(serverPlayer -> sourcePlayer.getBukkitEntity().canSee(serverPlayer.getBukkitEntity())).map(serverPlayer -> serverPlayer.getGameProfile().getName()).toList() : Lists.newArrayList(this.server.getPlayerNames()); // Paper - Make CommandSourceStack respect hidden players
+ return this.entity instanceof ServerPlayer sourcePlayer && !(sourcePlayer instanceof org.leavesmc.leaves.replay.ServerPhotographer) && !sourcePlayer.getBukkitEntity().hasPermission("paper.bypass-visibility.tab-completion") ? this.getServer().getPlayerList().getPlayers().stream().filter(serverPlayer -> sourcePlayer.getBukkitEntity().canSee(serverPlayer.getBukkitEntity())).map(serverPlayer -> serverPlayer.getGameProfile().getName()).toList() : Lists.newArrayList(this.server.getPlayerNames()); // Paper - Make CommandSourceStack respect hidden players // Leaves - only real player
}
@Override
diff --git a/net/minecraft/commands/arguments/selector/EntitySelector.java b/net/minecraft/commands/arguments/selector/EntitySelector.java
index 514f8fbdeb776087608665c35de95294aadf5cf0..b75772897cabc3e7c59301d451685378fa55b6c3 100644
--- a/net/minecraft/commands/arguments/selector/EntitySelector.java
+++ b/net/minecraft/commands/arguments/selector/EntitySelector.java
@@ -128,11 +128,12 @@ public class EntitySelector {
return this.findPlayers(source);
} else if (this.playerName != null) {
ServerPlayer playerByName = source.getServer().getPlayerList().getPlayerByName(this.playerName);
+ playerByName = playerByName instanceof org.leavesmc.leaves.replay.ServerPhotographer ? null : playerByName; // Leaves - skip photographer
return playerByName == null ? List.of() : List.of(playerByName);
} else if (this.entityUUID != null) {
for (ServerLevel serverLevel : source.getServer().getAllLevels()) {
Entity entity = serverLevel.getEntity(this.entityUUID);
- if (entity != null) {
+ if (entity != null && !(entity instanceof org.leavesmc.leaves.replay.ServerPhotographer)) {
if (entity.getType().isEnabled(source.enabledFeatures())) {
return List.of(entity);
}
@@ -146,7 +147,7 @@ public class EntitySelector {
AABB absoluteAabb = this.getAbsoluteAabb(vec3);
if (this.currentEntity) {
Predicate<Entity> predicate = this.getPredicate(vec3, absoluteAabb, null);
- return source.getEntity() != null && predicate.test(source.getEntity()) ? List.of(source.getEntity()) : List.of();
+ return source.getEntity() != null && !(source.getEntity() instanceof org.leavesmc.leaves.replay.ServerPhotographer) && predicate.test(source.getEntity()) ? List.of(source.getEntity()) : List.of(); // Leaves - skip photographer
} else {
Predicate<Entity> predicate = this.getPredicate(vec3, absoluteAabb, source.enabledFeatures());
List<Entity> list = new ObjectArrayList<>();
@@ -157,6 +158,7 @@ public class EntitySelector {
this.addEntities(list, serverLevel1, absoluteAabb, predicate);
}
}
+ list.removeIf(entity -> entity instanceof org.leavesmc.leaves.replay.ServerPhotographer); // Leaves - skip photographer
return this.sortAndLimit(vec3, list);
}
@@ -192,9 +194,11 @@ public class EntitySelector {
this.checkPermissions(source);
if (this.playerName != null) {
ServerPlayer playerByName = source.getServer().getPlayerList().getPlayerByName(this.playerName);
+ playerByName = playerByName instanceof org.leavesmc.leaves.replay.ServerPhotographer ? null : playerByName; // Leaves - skip photographer
return playerByName == null ? List.of() : List.of(playerByName);
} else if (this.entityUUID != null) {
ServerPlayer playerByName = source.getServer().getPlayerList().getPlayer(this.entityUUID);
+ playerByName = playerByName instanceof org.leavesmc.leaves.replay.ServerPhotographer ? null : playerByName; // Leaves - skip photographer
return playerByName == null ? List.of() : List.of(playerByName);
} else {
Vec3 vec3 = this.position.apply(source.getPosition());
@@ -206,11 +210,11 @@ public class EntitySelector {
int resultLimit = this.getResultLimit();
List<ServerPlayer> players;
if (this.isWorldLimited()) {
- players = source.getLevel().getPlayers(predicate, resultLimit);
+ players = source.getLevel().getPlayers((player -> !(player instanceof org.leavesmc.leaves.replay.ServerPhotographer) && predicate.test(player)), resultLimit); // Leaves - skip photographer
} else {
players = new ObjectArrayList<>();
- for (ServerPlayer serverPlayer1 : source.getServer().getPlayerList().getPlayers()) {
+ for (ServerPlayer serverPlayer1 : source.getServer().getPlayerList().realPlayers) { // Leaves - only real players
if (predicate.test(serverPlayer1)) {
players.add(serverPlayer1);
if (players.size() >= resultLimit) {
diff --git a/net/minecraft/server/MinecraftServer.java b/net/minecraft/server/MinecraftServer.java
index 81726e5782d25ff35a2bea8820a62786e46148b1..3c94631f468f4ee1b46de9981c17171e5901d745 100644
--- a/net/minecraft/server/MinecraftServer.java
+++ b/net/minecraft/server/MinecraftServer.java
@@ -1800,7 +1800,7 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
}
private ServerStatus.Players buildPlayerStatus() {
- List<ServerPlayer> players = new java.util.ArrayList<>(this.playerList.getPlayers()); // Folia - region threading
+ List<ServerPlayer> players = new java.util.ArrayList<>(this.playerList.realPlayers); // Folia - region threading
int maxPlayers = this.getMaxPlayers();
if (this.hidesOnlinePlayers()) {
return new ServerStatus.Players(maxPlayers, players.size(), List.of());
@@ -2001,7 +2001,7 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
@Override
public int getPlayerCount() {
- return this.playerList.getPlayerCount();
+ return this.playerList.realPlayers.size(); // Leaves - only real player
}
@Override
@@ -2415,7 +2415,7 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
if (Thread.currentThread() != this.serverThread) return; // Paper
// Paper start - we don't need to save everything, just advancements
// this.getPlayerList().saveAll();
- for (final ServerPlayer player : this.getPlayerList().getPlayers()) {
+ for (final ServerPlayer player : this.getPlayerList().realPlayers) { // Leaves - only real players
player.getAdvancements().save();
}
// Paper end - we don't need to save everything, just advancements
@@ -2554,7 +2554,7 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
if (!playerList.isUsingWhitelist()) return; // Paper - whitelist not enabled
UserWhiteList whiteList = playerList.getWhiteList();
- for (ServerPlayer serverPlayer : Lists.newArrayList(playerList.getPlayers())) {
+ for (ServerPlayer serverPlayer : Lists.newArrayList(playerList.realPlayers)) { // Leaves - only real player
if (!whiteList.isWhiteListed(serverPlayer.getGameProfile()) && !this.getPlayerList().isOp(serverPlayer.getGameProfile())) { // Paper - Fix kicking ops when whitelist is reloaded (MC-171420)
serverPlayer.connection.disconnect(net.kyori.adventure.text.Component.text(org.spigotmc.SpigotConfig.whitelistMessage), org.bukkit.event.player.PlayerKickEvent.Cause.WHITELIST); // Paper - use configurable message & kick event cause
}
diff --git a/net/minecraft/server/PlayerAdvancements.java b/net/minecraft/server/PlayerAdvancements.java
index 5c0e338dc1b0eb5724d10a73d6fc7975f9d2e5e5..0e7e4246837353a5936a6388f23b6b4b2ec1d5f3 100644
--- a/net/minecraft/server/PlayerAdvancements.java
+++ b/net/minecraft/server/PlayerAdvancements.java
@@ -168,7 +168,7 @@ 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) {
+ if (player instanceof org.leavesmc.leaves.bot.ServerBot || player instanceof org.leavesmc.leaves.replay.ServerPhotographer) { // Leaves - and photographer
return false;
}
// Leaves end - bot can't get advancement
diff --git a/net/minecraft/server/ServerScoreboard.java b/net/minecraft/server/ServerScoreboard.java
index fc1c377ad05edcefacd407671801ae605f3fc31a..5635f0940b4b70f27c8dfdc1aa666fcb7e26d55d 100644
--- a/net/minecraft/server/ServerScoreboard.java
+++ b/net/minecraft/server/ServerScoreboard.java
@@ -242,7 +242,7 @@ public class ServerScoreboard extends Scoreboard {
public void startTrackingObjective(Objective objective) {
List<Packet<?>> startTrackingPackets = this.getStartTrackingPackets(objective);
- for (ServerPlayer serverPlayer : this.server.getPlayerList().getPlayers()) {
+ for (ServerPlayer serverPlayer : this.server.getPlayerList().realPlayers) { // Leaves - only real players
if (serverPlayer.getBukkitEntity().getScoreboard().getHandle() != this) continue; // CraftBukkit - Only players on this board
for (Packet<?> packet : startTrackingPackets) {
serverPlayer.connection.send(packet);
@@ -268,7 +268,7 @@ public class ServerScoreboard extends Scoreboard {
public void stopTrackingObjective(Objective objective) {
List<Packet<?>> stopTrackingPackets = this.getStopTrackingPackets(objective);
- for (ServerPlayer serverPlayer : this.server.getPlayerList().getPlayers()) {
+ for (ServerPlayer serverPlayer : this.server.getPlayerList().realPlayers) { // Leaves - only real players
if (serverPlayer.getBukkitEntity().getScoreboard().getHandle() != this) continue; // CraftBukkit - Only players on this board
for (Packet<?> packet : stopTrackingPackets) {
serverPlayer.connection.send(packet);
diff --git a/net/minecraft/server/commands/DefaultGameModeCommands.java b/net/minecraft/server/commands/DefaultGameModeCommands.java
index c2ab47bb97c4fbbbc48a8ff0538ab8fd38532a88..2b7d11195f96e68a004e0e060e19d4004bb8a154 100644
--- a/net/minecraft/server/commands/DefaultGameModeCommands.java
+++ b/net/minecraft/server/commands/DefaultGameModeCommands.java
@@ -27,7 +27,7 @@ public class DefaultGameModeCommands {
server.setDefaultGameType(gamemode);
GameType forcedGameType = server.getForcedGameType();
if (forcedGameType != null) {
- for (ServerPlayer serverPlayer : server.getPlayerList().getPlayers()) {
+ for (ServerPlayer serverPlayer : server.getPlayerList().realPlayers) { // Leaves - only real players
serverPlayer.getBukkitEntity().taskScheduler.schedule((ServerPlayer player) -> { // Folia - region threading
// Paper start - Expand PlayerGameModeChangeEvent
org.bukkit.event.player.PlayerGameModeChangeEvent event = player.setGameMode(gamemode, org.bukkit.event.player.PlayerGameModeChangeEvent.Cause.DEFAULT_GAMEMODE, net.kyori.adventure.text.Component.empty()); // Folia - region threading
diff --git a/net/minecraft/server/commands/ListPlayersCommand.java b/net/minecraft/server/commands/ListPlayersCommand.java
index c6ae34f91b3629990294fc5e69237a1e600ef038..2109b0a2d4099e64c34cd1c45b83f72654d3b615 100644
--- a/net/minecraft/server/commands/ListPlayersCommand.java
+++ b/net/minecraft/server/commands/ListPlayersCommand.java
@@ -33,7 +33,7 @@ public class ListPlayersCommand {
private static int format(CommandSourceStack source, Function<ServerPlayer, Component> nameExtractor) {
PlayerList playerList = source.getServer().getPlayerList();
// CraftBukkit start
- List<ServerPlayer> playersTemp = playerList.getPlayers();
+ List<ServerPlayer> playersTemp = playerList.realPlayers;
if (source.getBukkitSender() instanceof org.bukkit.entity.Player) {
org.bukkit.entity.Player sender = (org.bukkit.entity.Player) source.getBukkitSender();
playersTemp = playersTemp.stream().filter((ep) -> sender.canSee(ep.getBukkitEntity())).collect(java.util.stream.Collectors.toList());
diff --git a/net/minecraft/server/commands/OpCommand.java b/net/minecraft/server/commands/OpCommand.java
index e6c7bbb023000b9de90c1256274ff5aba4a6478a..98f8804d58616592332e2a968282be6ad8903ea8 100644
--- a/net/minecraft/server/commands/OpCommand.java
+++ b/net/minecraft/server/commands/OpCommand.java
@@ -25,7 +25,7 @@ public class OpCommand {
(commandContext, suggestionsBuilder) -> {
PlayerList playerList = commandContext.getSource().getServer().getPlayerList();
return SharedSuggestionProvider.suggest(
- playerList.getPlayers()
+ playerList.realPlayers // Leaves - only real player
.stream()
.filter(serverPlayer -> !playerList.isOp(serverPlayer.getGameProfile()))
.map(serverPlayer -> serverPlayer.getGameProfile().getName()),
diff --git a/net/minecraft/server/commands/ParticleCommand.java b/net/minecraft/server/commands/ParticleCommand.java
index 33d96239f4b72a5587dc70f9602847a870d6d6a5..a83ce2cd112fca02cf3545f8c38e5cafae2c7c0e 100644
--- a/net/minecraft/server/commands/ParticleCommand.java
+++ b/net/minecraft/server/commands/ParticleCommand.java
@@ -36,7 +36,7 @@ public class ParticleCommand {
0.0F,
0,
false,
- commandContext.getSource().getServer().getPlayerList().getPlayers()
+ commandContext.getSource().getServer().getPlayerList().realPlayers // Leaves - only real player
)
)
.then(
@@ -50,7 +50,7 @@ public class ParticleCommand {
0.0F,
0,
false,
- context1.getSource().getServer().getPlayerList().getPlayers()
+ context1.getSource().getServer().getPlayerList().realPlayers // Leaves - only real player
)
)
.then(
@@ -68,7 +68,7 @@ public class ParticleCommand {
FloatArgumentType.getFloat(context1, "speed"),
IntegerArgumentType.getInteger(context1, "count"),
false,
- context1.getSource().getServer().getPlayerList().getPlayers()
+ context1.getSource().getServer().getPlayerList().realPlayers // Leaves - only real player
)
)
.then(
@@ -82,7 +82,7 @@ public class ParticleCommand {
FloatArgumentType.getFloat(context1, "speed"),
IntegerArgumentType.getInteger(context1, "count"),
true,
- context1.getSource().getServer().getPlayerList().getPlayers()
+ context1.getSource().getServer().getPlayerList().realPlayers // Leaves - only real player
)
)
.then(
@@ -112,7 +112,7 @@ public class ParticleCommand {
FloatArgumentType.getFloat(context1, "speed"),
IntegerArgumentType.getInteger(context1, "count"),
false,
- context1.getSource().getServer().getPlayerList().getPlayers()
+ context1.getSource().getServer().getPlayerList().realPlayers // Leaves - only real player
)
)
.then(
diff --git a/net/minecraft/server/commands/TeamMsgCommand.java b/net/minecraft/server/commands/TeamMsgCommand.java
index 134d7b1a9d5a5a47ebf4aabff110dde914cd6fe1..894dd1d048904b8a775416ea6cb3112215c567bc 100644
--- a/net/minecraft/server/commands/TeamMsgCommand.java
+++ b/net/minecraft/server/commands/TeamMsgCommand.java
@@ -40,7 +40,7 @@ public class TeamMsgCommand {
} else {
List<ServerPlayer> list = commandSourceStack.getServer()
.getPlayerList()
- .getPlayers()
+ .realPlayers // Leaves - only real players
.stream()
.filter(player -> player == entityOrException || player.getTeam() == team)
.toList();
diff --git a/net/minecraft/server/commands/WhitelistCommand.java b/net/minecraft/server/commands/WhitelistCommand.java
index 763a51d9a0859296eb4ea52b6879d6c7703db673..25b4fcf79dbf3e227451b3321d8fd38bab55ffe9 100644
--- a/net/minecraft/server/commands/WhitelistCommand.java
+++ b/net/minecraft/server/commands/WhitelistCommand.java
@@ -43,7 +43,7 @@ public class WhitelistCommand {
(commandContext, suggestionsBuilder) -> {
PlayerList playerList = commandContext.getSource().getServer().getPlayerList();
return SharedSuggestionProvider.suggest(
- playerList.getPlayers()
+ playerList.realPlayers // Leaves - only real player
.stream()
.filter(serverPlayer -> !playerList.getWhiteList().isWhiteListed(serverPlayer.getGameProfile()))
.map(serverPlayer -> serverPlayer.getGameProfile().getName()),
diff --git a/net/minecraft/server/gui/PlayerListComponent.java b/net/minecraft/server/gui/PlayerListComponent.java
index f5ba0c9a4c3f9eaa38eeb689de915c25c7165433..24bbc32bc17802edbd9cc14310fe8141c0ad85b0 100644
--- a/net/minecraft/server/gui/PlayerListComponent.java
+++ b/net/minecraft/server/gui/PlayerListComponent.java
@@ -17,8 +17,8 @@ public class PlayerListComponent extends JList<String> {
if (this.tickCount++ % 20 == 0) {
Vector<String> list = new Vector<>();
- for (int i = 0; i < this.server.getPlayerList().getPlayers().size(); i++) {
- list.add(this.server.getPlayerList().getPlayers().get(i).getGameProfile().getName());
+ for (int i = 0; i < this.server.getPlayerList().realPlayers.size(); i++) { // Leaves - only real players
+ list.add(this.server.getPlayerList().realPlayers.get(i).getGameProfile().getName()); // Leaves - only real players
}
this.setListData(list);
diff --git a/net/minecraft/server/level/ServerLevel.java b/net/minecraft/server/level/ServerLevel.java
index 3393fb585747aaf3fa270c4bfe06ed4e9174d519..c58b88f09e6f08d178fcb17a13dd790c3cb62aae 100644
--- a/net/minecraft/server/level/ServerLevel.java
+++ b/net/minecraft/server/level/ServerLevel.java
@@ -2836,7 +2836,7 @@ public class ServerLevel extends Level implements ServerEntityGetter, WorldGenLe
if (entity instanceof ServerPlayer serverPlayer) {
ServerLevel.this.players.add(serverPlayer);
// Leaves start - skip
- if (!(serverPlayer instanceof org.leavesmc.leaves.bot.ServerBot)) {
+ if (!(serverPlayer instanceof org.leavesmc.leaves.bot.ServerBot) && !(serverPlayer instanceof org.leavesmc.leaves.replay.ServerPhotographer)) { // and photographer
ServerLevel.this.realPlayers.add(serverPlayer);
}
// Leaves end - skip
@@ -2928,7 +2928,7 @@ public class ServerLevel extends Level implements ServerEntityGetter, WorldGenLe
if (entity instanceof ServerPlayer serverPlayer) {
ServerLevel.this.players.remove(serverPlayer);
// Leaves start - skip
- if (!(serverPlayer instanceof org.leavesmc.leaves.bot.ServerBot)) {
+ if (!(serverPlayer instanceof org.leavesmc.leaves.bot.ServerBot) && !(serverPlayer instanceof org.leavesmc.leaves.replay.ServerPhotographer)) { // and photographer
ServerLevel.this.realPlayers.remove(serverPlayer);
}
// Leaves end - skip
diff --git a/net/minecraft/server/level/ServerPlayer.java b/net/minecraft/server/level/ServerPlayer.java
index 4b2dba8b30b72fb3acc00957b3e19d6fc178c99e..afd607df83fda3de6f53f55ad305c8f69fe6ddb6 100644
--- a/net/minecraft/server/level/ServerPlayer.java
+++ b/net/minecraft/server/level/ServerPlayer.java
@@ -194,7 +194,7 @@ import net.minecraft.world.scores.criteria.ObjectiveCriteria;
import org.slf4j.Logger;
public class ServerPlayer extends Player implements ca.spottedleaf.moonrise.patches.chunk_system.player.ChunkSystemServerPlayer { // Paper - rewrite chunk system
- private static final Logger LOGGER = LogUtils.getLogger();
+ protected static final Logger LOGGER = LogUtils.getLogger();
public static final long LAST_SAVE_ABSENT = Long.MIN_VALUE; public long lastSave = LAST_SAVE_ABSENT; // Paper // Folia - threaded regions - changed to nanoTime
private static final int NEUTRAL_MOB_DEATH_NOTIFICATION_RADII_XZ = 32;
private static final int NEUTRAL_MOB_DEATH_NOTIFICATION_RADII_Y = 10;
diff --git a/net/minecraft/server/players/PlayerList.java b/net/minecraft/server/players/PlayerList.java
index 11c1eb6995ec3aa896815efa59477096c39089d1..762728e490a152d159684324e200ab0d2852ac7f 100644
--- a/net/minecraft/server/players/PlayerList.java
+++ b/net/minecraft/server/players/PlayerList.java
@@ -130,6 +130,7 @@ public abstract class PlayerList {
private boolean allowCommandsForAllPlayers;
private static final boolean ALLOW_LOGOUTIVATOR = false;
private int sendAllPlayerInfoIn;
+ public final List<ServerPlayer> realPlayers = new java.util.concurrent.CopyOnWriteArrayList(); // Leaves - replay api
// CraftBukkit start
private org.bukkit.craftbukkit.CraftServer cserver;
@@ -213,6 +214,123 @@ public abstract class PlayerList {
abstract public void loadAndSaveFiles(); // Paper - fix converting txt to json file; moved from DedicatedPlayerList constructor
+ // Leaves start - replay mod api
+ public void placeNewPhotographer(Connection connection, org.leavesmc.leaves.replay.ServerPhotographer player, ServerLevel worldserver) {
+ player.isRealPlayer = true; // Paper
+ player.loginTime = System.currentTimeMillis(); // Paper
+
+ ServerLevel worldserver1 = worldserver;
+
+ player.setServerLevel(worldserver1);
+ player.spawnIn(worldserver1);
+ player.gameMode.setLevel((ServerLevel) player.level());
+
+ LevelData worlddata = worldserver1.getLevelData();
+
+ player.loadGameTypes(null);
+ ServerGamePacketListenerImpl playerconnection = new ServerGamePacketListenerImpl(this.server, connection, player, CommonListenerCookie.createInitial(player.gameProfile, false));
+ GameRules gamerules = worldserver1.getGameRules();
+ boolean flag = gamerules.getBoolean(GameRules.RULE_DO_IMMEDIATE_RESPAWN);
+ boolean flag1 = gamerules.getBoolean(GameRules.RULE_REDUCEDDEBUGINFO);
+ boolean flag2 = gamerules.getBoolean(GameRules.RULE_LIMITED_CRAFTING);
+
+ playerconnection.send(new ClientboundLoginPacket(player.getId(), worlddata.isHardcore(), this.server.levelKeys(), this.getMaxPlayers(), worldserver1.getWorld().getSendViewDistance(), worldserver1.getWorld().getSimulationDistance(), flag1, !flag, flag2, player.createCommonSpawnInfo(worldserver1), this.server.enforceSecureProfile())); // Paper - replace old player chunk management
+ player.getBukkitEntity().sendSupportedChannels(); // CraftBukkit
+ playerconnection.send(new ClientboundChangeDifficultyPacket(worlddata.getDifficulty(), worlddata.isDifficultyLocked()));
+ playerconnection.send(new ClientboundPlayerAbilitiesPacket(player.getAbilities()));
+ playerconnection.send(new ClientboundSetHeldSlotPacket(player.getInventory().getSelectedSlot()));
+ RecipeManager craftingmanager = this.server.getRecipeManager();
+ playerconnection.send(new ClientboundUpdateRecipesPacket(craftingmanager.getSynchronizedItemProperties(), craftingmanager.getSynchronizedStonecutterRecipes()));
+
+ this.sendPlayerPermissionLevel(player);
+ player.getStats().markAllDirty();
+ player.getRecipeBook().sendInitialRecipeBook(player);
+ this.updateEntireScoreboard(worldserver1.getScoreboard(), player);
+ this.server.invalidateStatus();
+
+ playerconnection.teleport(player.getX(), player.getY(), player.getZ(), player.getYRot(), player.getXRot());
+ ServerStatus serverping = this.server.getStatus();
+
+ if (serverping != null) {
+ player.sendServerStatus(serverping);
+ }
+
+ this.players.add(player);
+ this.playersByName.put(player.getScoreboardName().toLowerCase(java.util.Locale.ROOT), player); // Spigot
+ this.playersByUUID.put(player.getUUID(), player);
+
+ player.supressTrackerForLogin = true;
+ worldserver1.getCurrentWorldData().connections.add(player.connection.connection);
+ worldserver1.addNewPlayer(player);
+ this.server.getCustomBossEvents().onPlayerConnect(player);
+ org.bukkit.craftbukkit.entity.CraftPlayer bukkitPlayer = player.getBukkitEntity();
+
+ player.containerMenu.transferTo(player.containerMenu, bukkitPlayer);
+ if (!player.connection.isAcceptingMessages()) {
+ return;
+ }
+
+ // org.leavesmc.leaves.protocol.core.LeavesProtocolManager.handlePlayerJoin(player); // Leaves - protocol
+
+ // 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 List<ServerPlayer> onlinePlayers = Lists.newArrayListWithExpectedSize(this.players.size() - 1);
+ final List<ServerPlayer> playerList = List.copyOf(this.players); // Lophine - copy to avoid ConcurrentModificationException
+ for (ServerPlayer serverPlayer : playerList) {
+ if (serverPlayer == player || !bukkitPlayer.canSee(serverPlayer.getBukkitEntity())) {
+ continue;
+ }
+
+ // Leaves start - skip photographer
+ if (serverPlayer instanceof org.leavesmc.leaves.replay.ServerPhotographer) {
+ continue;
+ }
+ // Leaves end - skip photographer
+
+ onlinePlayers.add(serverPlayer);
+ }
+ if (!onlinePlayers.isEmpty()) {
+ player.connection.send(ClientboundPlayerInfoUpdatePacket.createPlayerInitializing(onlinePlayers, player));
+ }
+
+ player.sentListPacket = true;
+ player.supressTrackerForLogin = false;
+ player.level().getChunkSource().chunkMap.addEntity(player);
+
+ this.sendLevelInfo(player, worldserver1);
+
+ if (player.level() == worldserver1 && !worldserver1.players().contains(player)) {
+ worldserver1.addNewPlayer(player);
+ this.server.getCustomBossEvents().onPlayerConnect(player);
+ }
+
+ worldserver1 = player.level();
+ for (MobEffectInstance mobeffect : player.getActiveEffects()) {
+ playerconnection.send(new ClientboundUpdateMobEffectPacket(player.getId(), mobeffect, false));
+ }
+
+ if (player.isDeadOrDying()) {
+ net.minecraft.core.Holder<net.minecraft.world.level.biome.Biome> plains = worldserver1.registryAccess().lookupOrThrow(net.minecraft.core.registries.Registries.BIOME)
+ .getOrThrow(net.minecraft.world.level.biome.Biomes.PLAINS);
+ player.connection.send(new net.minecraft.network.protocol.game.ClientboundLevelChunkWithLightPacket(
+ new net.minecraft.world.level.chunk.EmptyLevelChunk(worldserver1, player.chunkPosition(), plains),
+ worldserver1.getLightEngine(), null, null, false)
+ );
+ }
+ }
+ // Leaves end - replay mod api
+
public void loadSpawnForNewPlayer(Connection connection, ServerPlayer player, CommonListenerCookie cookie, org.apache.commons.lang3.mutable.MutableObject<net.minecraft.util.ProblemReporter.ScopedCollector> scopedCollectorStore, org.apache.commons.lang3.mutable.MutableObject<ValueInput> data, org.apache.commons.lang3.mutable.MutableObject<String> lastKnownName, ca.spottedleaf.concurrentutil.completable.CallbackCompletable<org.bukkit.Location> toComplete) { // Folia - region threading - rewrite login process
player.isRealPlayer = true; // Paper
player.loginTime = System.currentTimeMillis(); // Paper - Replace OfflinePlayer#getLastPlayed
@@ -398,6 +516,7 @@ public abstract class PlayerList {
// player.connection.send(ClientboundPlayerInfoUpdatePacket.createPlayerInitializing(this.players)); // CraftBukkit - replaced with loop below
this.players.add(player);
+ this.realPlayers.add(player); // Leaves - replay api
this.playersByName.put(player.getScoreboardName().toLowerCase(java.util.Locale.ROOT), player); // Spigot
this.playersByUUID.put(player.getUUID(), player);
// this.broadcastAll(ClientboundPlayerInfoUpdatePacket.createPlayerInitializing(List.of(player))); // CraftBukkit - replaced with loop below
@@ -601,6 +720,7 @@ public abstract class PlayerList {
}
protected void save(ServerPlayer player) {
+ if (player instanceof org.leavesmc.leaves.replay.ServerPhotographer) return; // Leaves - skip photographer
if (!player.getBukkitEntity().isPersistent()) return; // CraftBukkit
player.lastSave = System.nanoTime(); // Folia - region threading - changed to nanoTime tracking
this.playerIo.save(player);
@@ -615,6 +735,44 @@ public abstract class PlayerList {
}
}
+ // Leaves start - replay mod api
+ public void removePhotographer(org.leavesmc.leaves.replay.ServerPhotographer entityplayer) {
+ ServerLevel worldserver = entityplayer.level();
+
+ entityplayer.awardStat(Stats.LEAVE_GAME);
+
+ if (entityplayer.containerMenu != entityplayer.inventoryMenu) {
+ entityplayer.closeContainer(org.bukkit.event.inventory.InventoryCloseEvent.Reason.DISCONNECT);
+ }
+
+ if (server.isSameThread()) entityplayer.doTick();
+
+ if (this.collideRuleTeamName != null) {
+ final net.minecraft.world.scores.Scoreboard scoreBoard = this.server.getLevel(Level.OVERWORLD).getScoreboard();
+ final PlayerTeam team = scoreBoard.getPlayersTeam(this.collideRuleTeamName);
+ if (entityplayer.getTeam() == team && team != null) {
+ scoreBoard.removePlayerFromTeam(entityplayer.getScoreboardName(), team);
+ }
+ }
+
+ worldserver.getCurrentWorldData().connections.remove(entityplayer.connection.connection);
+ worldserver.removePlayerImmediately(entityplayer, Entity.RemovalReason.UNLOADED_WITH_PLAYER);
+ entityplayer.retireScheduler();
+ entityplayer.getAdvancements().stopListening();
+ this.players.remove(entityplayer);
+ this.playersByName.remove(entityplayer.getScoreboardName().toLowerCase(java.util.Locale.ROOT));
+ this.server.getCustomBossEvents().onPlayerDisconnect(entityplayer);
+ UUID uuid = entityplayer.getUUID();
+ ServerPlayer entityplayer1 = this.playersByUUID.get(uuid);
+
+ if (entityplayer1 == entityplayer) {
+ this.playersByUUID.remove(uuid);
+ }
+
+ this.cserver.getScoreboardManager().removePlayer(entityplayer.getBukkitEntity());
+ }
+ // Leaves stop - replay mod api
+
public @Nullable net.kyori.adventure.text.Component remove(ServerPlayer player) { // CraftBukkit - return string // Paper - return Component
// Paper start - Fix kick event leave message not being sent
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())));
@@ -688,6 +846,7 @@ public abstract class PlayerList {
player.retireScheduler(); // Paper - Folia schedulers
player.getAdvancements().stopListening();
this.players.remove(player);
+ this.realPlayers.remove(player); // Leaves - replay api
this.playersByName.remove(player.getScoreboardName().toLowerCase(java.util.Locale.ROOT)); // Spigot
this.server.getCustomBossEvents().onPlayerDisconnect(player);
UUID uuid = player.getUUID();
@@ -1044,15 +1203,15 @@ 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() + this.server.getBotList().bots.size()]; // Leaves - fakeplayer support
+ List<ServerPlayer> players = new java.util.ArrayList<>(this.realPlayers); // Folia - region threading
+ String[] strings = new String[players.size() + this.server.getBotList().bots.size()]; // Leaves - fakeplayer support, and skip photographer
- for (int i = 0; i < players.size(); i++) { // Folia - region threading
- strings[i] = players.get(i).getGameProfile().getName(); // Folia - region threading
+ for (int i = 0; i < players.size(); i++) { // Folia - region threading // Leaves - only real players
+ strings[i] = players.get(i).getGameProfile().getName(); // Folia - region threading // Leaves - only real players
}
// 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();
+ for (int i = players.size(); i < strings.length; ++i) { // Leaves - only real players
+ strings[i] = this.server.getBotList().bots.get(i - players.size()).getGameProfile().getName(); // Leaves - only real players
}
// Leaves end - fakeplayer support
diff --git a/net/minecraft/world/entity/LivingEntity.java b/net/minecraft/world/entity/LivingEntity.java
index 12466e0294123a5922ee7694984d4d277a390ddd..205e2648569b7c18f488aa4d3d9ec944bb1babf4 100644
--- a/net/minecraft/world/entity/LivingEntity.java
+++ b/net/minecraft/world/entity/LivingEntity.java
@@ -257,7 +257,7 @@ public abstract class LivingEntity extends Entity implements Attackable, Waypoin
protected ItemStack useItem = ItemStack.EMPTY;
public int useItemRemaining;
protected int fallFlyTicks;
- private BlockPos lastPos;
+ protected BlockPos lastPos;
private Optional<BlockPos> lastClimbablePos = Optional.empty();
@Nullable
private DamageSource lastDamageSource;
@@ -0,0 +1,217 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Bacteriawa <A3167717663@hotmail.com>
Date: Sat, 15 Nov 2025 01:06:20 +0800
Subject: [PATCH] Leaves: Catch update suppression crash
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/network/protocol/PacketUtils.java b/net/minecraft/network/protocol/PacketUtils.java
index 7280ca2250ade4166a1a883b205bbc89f4e00d1a..06268ac3420fc03a798a20bb617ba89a032df527 100644
--- a/net/minecraft/network/protocol/PacketUtils.java
+++ b/net/minecraft/network/protocol/PacketUtils.java
@@ -30,7 +30,20 @@ public class PacketUtils {
final int packetTimerId = profiler.getOrCreateTimerAndStart(() -> "Packet Handler: ".concat(io.papermc.paper.util.ObfHelper.INSTANCE.deobfClassName(packet.getClass().getName()))); try { // Folia - profiler
packet.handle(processor);
} finally { profiler.stopTimer(packetTimerId); } // Folia - profiler
+ // Leaves start - update suppression crash fix
+ } catch (org.leavesmc.leaves.util.UpdateSuppressionException exception) {
+ if (processor instanceof net.minecraft.server.network.ServerGamePacketListenerImpl gamePacketListener) {
+ exception.providePlayer(gamePacketListener.player);
+ }
+ exception.consume();
} catch (Exception var4) {
+ if (var4.getCause() instanceof org.leavesmc.leaves.util.UpdateSuppressionException exception) {
+ if (processor instanceof net.minecraft.server.network.ServerGamePacketListenerImpl gamePacketListener) {
+ exception.providePlayer(gamePacketListener.player);
+ }
+ exception.consume();
+ }
+ // Leaves end - update suppression crash fix
if (var4 instanceof ReportedException reportedException && reportedException.getCause() instanceof OutOfMemoryError) {
throw makeReportedException(var4, packet, processor);
}
diff --git a/net/minecraft/server/MinecraftServer.java b/net/minecraft/server/MinecraftServer.java
index 3c94631f468f4ee1b46de9981c17171e5901d745..f7a2a19c0832ca90536d09de23f4ec9a7d4a1521 100644
--- a/net/minecraft/server/MinecraftServer.java
+++ b/net/minecraft/server/MinecraftServer.java
@@ -1878,8 +1878,17 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
try {
profiler.startTimer(serverLevel.tickTimerId); try { // Folia - profiler
serverLevel.tick(hasTimeLeft, region); // Folia - region threading
+ // Leaves start - update suppression crash fix
+ } catch (org.leavesmc.leaves.util.UpdateSuppressionException exception) {
+ exception.provideLevel(serverLevel);
+ exception.consume();
} finally { profiler.stopTimer(serverLevel.tickTimerId); } // Folia - profiler
} catch (Throwable var7) {
+ if (var7.getCause() instanceof org.leavesmc.leaves.util.UpdateSuppressionException exception) {
+ exception.provideLevel(serverLevel);
+ exception.consume();
+ }
+ // Leaves end - update suppression crash fix
CrashReport crashReport = CrashReport.forThrowable(var7, "Exception ticking world");
serverLevel.fillReportDetails(crashReport);
throw new ReportedException(crashReport);
diff --git a/net/minecraft/server/level/ServerLevel.java b/net/minecraft/server/level/ServerLevel.java
index c58b88f09e6f08d178fcb17a13dd790c3cb62aae..c0ba36849da0983117901f0cd04b2c785df0010b 100644
--- a/net/minecraft/server/level/ServerLevel.java
+++ b/net/minecraft/server/level/ServerLevel.java
@@ -840,7 +840,20 @@ public class ServerLevel extends Level implements ServerEntityGetter, WorldGenLe
// Kaiiju end
profilerFiller.push("tick");
- this.guardEntityTick(this::tickNonPassenger, entity);
+ // Lophine start - update suppression crash fix
+ if (fun.bm.lophine.config.modules.fixes.UpdateSuppressionCrashFixConfig.enabled) {
+ try {
+ this.tickNonPassenger(entity); // Lophine changed
+ // Leaves start - update suppression crash fix - for dragon dupe
+ } catch (org.leavesmc.leaves.util.UpdateSuppressionException exception) {
+ exception.provideLevel(this);
+ exception.consume();
+ // Leaves end - update suppression crash fix - for dragon dupe
+ }
+ } else {
+ this.guardEntityTick(this::tickNonPassenger, entity);
+ }
+ // Lophine end - update suppression crash fix
profilerFiller.pop();
}
}
diff --git a/net/minecraft/server/level/ServerPlayer.java b/net/minecraft/server/level/ServerPlayer.java
index afd607df83fda3de6f53f55ad305c8f69fe6ddb6..e2f4b35c88c64a68cf7ef9fadf977e7b99e17c6e 100644
--- a/net/minecraft/server/level/ServerPlayer.java
+++ b/net/minecraft/server/level/ServerPlayer.java
@@ -1123,6 +1123,12 @@ public class ServerPlayer extends Player implements ca.spottedleaf.moonrise.patc
((org.bukkit.craftbukkit.CraftWorldBorder) this.getBukkitEntity().getWorldBorder()).getHandle().tick();
}
// CraftBukkit end
+ // Leaves start - update suppression crash fix
+ } catch (org.leavesmc.leaves.util.UpdateSuppressionException exception) {
+ exception.providePlayer(this);
+ exception.provideLevel(this.level());
+ exception.consume();
+ // Leaves start - update suppression crash fix
} catch (Throwable var4) {
CrashReport crashReport = CrashReport.forThrowable(var4, "Ticking player");
CrashReportCategory crashReportCategory = crashReport.addCategory("Player being ticked");
diff --git a/net/minecraft/world/entity/Entity.java b/net/minecraft/world/entity/Entity.java
index 477241699752567df7695d33fa629fb1d5109e0a..f63252c580c313ccf89304866cfc6503f4b26052 100644
--- a/net/minecraft/world/entity/Entity.java
+++ b/net/minecraft/world/entity/Entity.java
@@ -1403,9 +1403,19 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess
this.walkingStepSound(pos, state);
}
+ // Leaves start - update suppression crash fix
if (broadcastGameEvent) {
- this.level().gameEvent(GameEvent.STEP, this.position(), GameEvent.Context.of(this, state));
+ try {
+ this.level().gameEvent(net.minecraft.world.level.gameevent.GameEvent.STEP, this.position(), net.minecraft.world.level.gameevent.GameEvent.Context.of(this, state));
+ } catch (org.leavesmc.leaves.util.UpdateSuppressionException exception) {
+ exception.provideBlock(level, pos, state.getBlock());
+ if (this instanceof net.minecraft.server.level.ServerPlayer player) {
+ exception.providePlayer(player);
+ }
+ exception.consume();
+ }
}
+ // Leaves end - update suppression crash fix
return true;
} else {
diff --git a/net/minecraft/world/level/block/ShulkerBoxBlock.java b/net/minecraft/world/level/block/ShulkerBoxBlock.java
index 49bac7af90b0a7c490141be6357563447783c6ca..a26cd86aedcaf7ab606f8543e08360cbe9f2d1e6 100644
--- a/net/minecraft/world/level/block/ShulkerBoxBlock.java
+++ b/net/minecraft/world/level/block/ShulkerBoxBlock.java
@@ -183,7 +183,17 @@ public class ShulkerBoxBlock extends BaseEntityBlock {
@Override
protected int getAnalogOutputSignal(BlockState blockState, Level level, BlockPos pos) {
- return AbstractContainerMenu.getRedstoneSignalFromBlockEntity(level.getBlockEntity(pos));
+ // Leaves start - update suppression crash fix
+ try {
+ return AbstractContainerMenu.getRedstoneSignalFromBlockEntity(level.getBlockEntity(pos));
+ } catch (ClassCastException ex) {
+ if (fun.bm.lophine.config.modules.fixes.UpdateSuppressionCrashFixConfig.enabled) {
+ throw new org.leavesmc.leaves.util.UpdateSuppressionException(pos, null, this, null, ex);
+ } else {
+ throw ex;
+ }
+ }
+ // Leaves end - update suppression crash fix
}
public static Block getBlockByColor(@Nullable DyeColor color) {
diff --git a/net/minecraft/world/level/block/state/StateHolder.java b/net/minecraft/world/level/block/state/StateHolder.java
index 9b9842462b210b0407279ee8a8e9ccecf3ead005..1057f3bd32ff4c8f93f09251341c243915593e4d 100644
--- a/net/minecraft/world/level/block/state/StateHolder.java
+++ b/net/minecraft/world/level/block/state/StateHolder.java
@@ -104,7 +104,16 @@ public abstract class StateHolder<O, S> implements ca.spottedleaf.moonrise.patch
if (ret != null) {
return ret;
}
- throw new IllegalArgumentException("Cannot get property " + property + " as it does not exist in " + this.owner);
+ // Leaves start - update suppression crash fix
+ IllegalArgumentException iae = new IllegalArgumentException("Cannot get property " + property + " as it does not exist in " + this.owner);
+ if (fun.bm.lophine.config.modules.fixes.UpdateSuppressionCrashFixConfig.enabled) {
+ org.leavesmc.leaves.util.UpdateSuppressionException exception = new org.leavesmc.leaves.util.UpdateSuppressionException(null, null, null, null, iae);
+ if (exception.getStackTrace()[1].getClassName().startsWith("net.minecraft")) {
+ throw exception;
+ }
+ }
+ throw iae;
+ // Leaves end - update suppression crash fix
// Paper end - optimise blockstate property access
}
diff --git a/net/minecraft/world/level/chunk/LevelChunk.java b/net/minecraft/world/level/chunk/LevelChunk.java
index 1f44a59e46f86ca055e89631d831a38f4c3e238e..39c075173b77b5197af5d4ad3fe235ea0d2df756 100644
--- a/net/minecraft/world/level/chunk/LevelChunk.java
+++ b/net/minecraft/world/level/chunk/LevelChunk.java
@@ -378,7 +378,7 @@ public class LevelChunk extends ChunkAccess implements ca.spottedleaf.moonrise.p
if (blockState == state) {
return null;
} else {
- Block block = state.getBlock();
+ Block block = state.getBlock(); try { // Leaves start - update suppression crash fix
this.heightmaps.get(Heightmap.Types.MOTION_BLOCKING).update(i, y, i2, state);
this.heightmaps.get(Heightmap.Types.MOTION_BLOCKING_NO_LEAVES).update(i, y, i2, state);
this.heightmaps.get(Heightmap.Types.OCEAN_FLOOR).update(i, y, i2, state);
@@ -450,6 +450,7 @@ public class LevelChunk extends ChunkAccess implements ca.spottedleaf.moonrise.p
this.markUnsaved();
return blockState;
}
+ } catch (org.leavesmc.leaves.util.UpdateSuppressionException e) { e.provideBlock(level, pos, block); throw e; } // Leaves - update suppression crash fix
}
}
}
diff --git a/net/minecraft/world/level/redstone/NeighborUpdater.java b/net/minecraft/world/level/redstone/NeighborUpdater.java
index 714e291b17856aacdac7db992fc18e6ae662f659..01285691a2e0bf72b94969e9045f7ab42a9161bf 100644
--- a/net/minecraft/world/level/redstone/NeighborUpdater.java
+++ b/net/minecraft/world/level/redstone/NeighborUpdater.java
@@ -63,9 +63,22 @@ public interface NeighborUpdater {
} finally { if (levelChunk != null) levelChunk.getChunkHot().stopTickingAndCount(); } // KioCG
// Spigot start
} catch (StackOverflowError ex) {
+ // Leaves start - update suppression crash fix
+ if (fun.bm.lophine.config.modules.fixes.UpdateSuppressionCrashFixConfig.enabled) {
+ throw new org.leavesmc.leaves.util.UpdateSuppressionException(pos, level, neighborBlock, null, ex);
+ }
level.lastPhysicsProblem = pos.immutable();
// Spigot end
} catch (Throwable var9) {
+ if (fun.bm.lophine.config.modules.fixes.UpdateSuppressionCrashFixConfig.enabled) {
+ if (var9 instanceof org.leavesmc.leaves.util.UpdateSuppressionException ue) {
+ ue.provideBlock(level, pos, neighborBlock);
+ throw ue;
+ } else {
+ throw new org.leavesmc.leaves.util.UpdateSuppressionException(pos, level, neighborBlock, null, var9);
+ }
+ }
+ // Leaves end - update suppression crash fix
CrashReport crashReport = CrashReport.forThrowable(var9, "Exception while updating neighbours");
CrashReportCategory crashReportCategory = crashReport.addCategory("Block being updated");
crashReportCategory.setDetail(
@@ -0,0 +1,24 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Bacteriawa <A3167717663@hotmail.com>
Date: Sat, 15 Nov 2025 01:12:39 +0800
Subject: [PATCH] Leaves: CCE update suppression
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/world/level/block/ShulkerBoxBlock.java b/net/minecraft/world/level/block/ShulkerBoxBlock.java
index a26cd86aedcaf7ab606f8543e08360cbe9f2d1e6..27ccce4b4d8b4cb8be48bfe5d55366abe4ea3a38 100644
--- a/net/minecraft/world/level/block/ShulkerBoxBlock.java
+++ b/net/minecraft/world/level/block/ShulkerBoxBlock.java
@@ -185,7 +185,9 @@ public class ShulkerBoxBlock extends BaseEntityBlock {
protected int getAnalogOutputSignal(BlockState blockState, Level level, BlockPos pos) {
// Leaves start - update suppression crash fix
try {
- return AbstractContainerMenu.getRedstoneSignalFromBlockEntity(level.getBlockEntity(pos));
+ return fun.bm.lophine.config.modules.experiment.RedStoneConfig.cce ?
+ AbstractContainerMenu.getRedstoneSignalFromContainer((net.minecraft.world.Container) level.getBlockEntity(pos)) : // Leaves - make cce happy(?)
+ AbstractContainerMenu.getRedstoneSignalFromBlockEntity(level.getBlockEntity(pos));
} catch (ClassCastException ex) {
if (fun.bm.lophine.config.modules.fixes.UpdateSuppressionCrashFixConfig.enabled) {
throw new org.leavesmc.leaves.util.UpdateSuppressionException(pos, null, this, null, ex);
@@ -0,0 +1,55 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: violetc <58360096+s-yh-china@users.noreply.github.com>
Date: Wed, 14 Jun 2023 12:07:07 +0800
Subject: [PATCH] Leaves: Redstone ignore upwards update
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/world/level/block/ComparatorBlock.java b/net/minecraft/world/level/block/ComparatorBlock.java
index afecf6bb6152b4d9dfee4cd8c5b1d798cbf723b3..a5bbe83b079276a3ab497e8a725713721db83aa1 100644
--- a/net/minecraft/world/level/block/ComparatorBlock.java
+++ b/net/minecraft/world/level/block/ComparatorBlock.java
@@ -58,6 +58,7 @@ public class ComparatorBlock extends DiodeBlock implements EntityBlock {
BlockState neighborState,
RandomSource random
) {
+ if (fun.bm.lophine.config.modules.experiment.RedStoneConfig.redstoneIgnoreUpwardsUpdate && direction == Direction.DOWN) return state; // Leaves - behavior to 1.20.1
return direction == Direction.DOWN && !this.canSurviveOn(level, neighborPos, neighborState)
? Blocks.AIR.defaultBlockState()
: super.updateShape(state, level, scheduledTickAccess, pos, direction, neighborPos, neighborState, random);
diff --git a/net/minecraft/world/level/block/RedStoneWireBlock.java b/net/minecraft/world/level/block/RedStoneWireBlock.java
index 4b37335e91a51b9a58ce0bce94b61ab5afbc6486..555a00e402da3d9126925135d030b7bccd8e5327 100644
--- a/net/minecraft/world/level/block/RedStoneWireBlock.java
+++ b/net/minecraft/world/level/block/RedStoneWireBlock.java
@@ -179,7 +179,7 @@ public class RedStoneWireBlock extends Block {
RandomSource random
) {
if (direction == Direction.DOWN) {
- return !this.canSurviveOn(level, neighborPos, neighborState) ? Blocks.AIR.defaultBlockState() : state;
+ return fun.bm.lophine.config.modules.experiment.RedStoneConfig.redstoneIgnoreUpwardsUpdate ? state : !this.canSurviveOn(level, neighborPos, neighborState) ? Blocks.AIR.defaultBlockState() : state; // Leaves - behavior to 1.19
} else if (direction == Direction.UP) {
return this.getConnectionState(level, state, pos);
} else {
@@ -239,7 +239,7 @@ public class RedStoneWireBlock extends Block {
BlockPos blockPos = pos.relative(direction);
BlockState blockState = level.getBlockState(blockPos);
if (nonNormalCubeAbove) {
- boolean flag = blockState.getBlock() instanceof TrapDoorBlock || this.canSurviveOn(level, blockPos, blockState);
+ boolean flag = (!fun.bm.lophine.config.modules.experiment.RedStoneConfig.redstoneIgnoreUpwardsUpdate && blockState.getBlock() instanceof TrapDoorBlock) || this.canSurviveOn(level, blockPos, blockState); // Leaves - behavior to 1.19
if (flag && shouldConnectTo(level.getBlockState(blockPos.above()))) {
if (blockState.isFaceSturdy(level, blockPos, direction.getOpposite())) {
return RedstoneSide.UP;
diff --git a/net/minecraft/world/level/block/RepeaterBlock.java b/net/minecraft/world/level/block/RepeaterBlock.java
index 1d45564253258eebdf10470dd7eefa644a4151ae..4b8a0f7353984d8997ab3b702227ae8d46863d7e 100644
--- a/net/minecraft/world/level/block/RepeaterBlock.java
+++ b/net/minecraft/world/level/block/RepeaterBlock.java
@@ -68,6 +68,7 @@ public class RepeaterBlock extends DiodeBlock {
BlockState neighborState,
RandomSource random
) {
+ if (fun.bm.lophine.config.modules.experiment.RedStoneConfig.redstoneIgnoreUpwardsUpdate && direction == Direction.DOWN) return state; // Leaves - behavior to 1.20.1
if (direction == Direction.DOWN && !this.canSurviveOn(level, neighborPos, neighborState)) {
return Blocks.AIR.defaultBlockState();
} else {
@@ -0,0 +1,19 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Helvetica Volubi <suisuroru@blue-millennium.fun>
Date: Mon, 17 Nov 2025 18:41:08 +0800
Subject: [PATCH] Instant Block Updater
diff --git a/io/papermc/paper/threadedregions/RegionizedWorldData.java b/io/papermc/paper/threadedregions/RegionizedWorldData.java
index d0de3b22f6cef6eb213b7ff688a514a67f84fbcf..3b39b8f7ccb278cf910e3cc4e00da29790465a81 100644
--- a/io/papermc/paper/threadedregions/RegionizedWorldData.java
+++ b/io/papermc/paper/threadedregions/RegionizedWorldData.java
@@ -524,7 +524,7 @@ public final class RegionizedWorldData {
this.world = world;
this.blockLevelTicks = new LevelTicks<>(world::isPositionTickingWithEntitiesLoaded, world, true);
this.fluidLevelTicks = new LevelTicks<>(world::isPositionTickingWithEntitiesLoaded, world, false);
- this.neighborUpdater = new CollectingNeighborUpdater(world, world.neighbourUpdateMax);
+ this.neighborUpdater = fun.bm.lophine.config.modules.experiment.RedStoneConfig.instantBlockUpdater ? new net.minecraft.world.level.redstone.InstantNeighborUpdater(world) : new CollectingNeighborUpdater(world, world.neighbourUpdateMax); // Lophine - Instant Block Updater
this.nearbyPlayers = new NearbyPlayers(world);
this.wireHandler = new alternate.current.wire.WireHandler(world);
this.turbo = new io.papermc.paper.redstone.RedstoneWireTurbo((RedStoneWireBlock)Blocks.REDSTONE_WIRE);
@@ -0,0 +1,49 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Helvetica Volubi <suisuroru@blue-millennium.fun>
Date: Tue, 18 Nov 2025 00:24:32 +0800
Subject: [PATCH] Revert TrapDoorBlock changes form paperMC
diff --git a/net/minecraft/world/level/block/TrapDoorBlock.java b/net/minecraft/world/level/block/TrapDoorBlock.java
index a2140d22dc58aa2df1afbeb5170d04fd65739d55..ea79c471c03b6908710d5943f2ff378eaba4099e 100644
--- a/net/minecraft/world/level/block/TrapDoorBlock.java
+++ b/net/minecraft/world/level/block/TrapDoorBlock.java
@@ -128,37 +128,7 @@ public class TrapDoorBlock extends HorizontalDirectionalBlock implements SimpleW
if (!level.isClientSide) {
boolean hasNeighborSignal = level.hasNeighborSignal(pos);
if (hasNeighborSignal != state.getValue(POWERED)) {
- // CraftBukkit start
- org.bukkit.block.Block bblock = org.bukkit.craftbukkit.block.CraftBlock.at(level, pos);
- int power = bblock.getBlockPower();
- int oldPower = state.getValue(TrapDoorBlock.OPEN) ? 15 : 0;
-
- if (oldPower == 0 ^ power == 0 || neighborBlock.defaultBlockState().isSignalSource()) {
- org.bukkit.event.block.BlockRedstoneEvent eventRedstone = new org.bukkit.event.block.BlockRedstoneEvent(bblock, oldPower, power);
- level.getCraftServer().getPluginManager().callEvent(eventRedstone);
- hasNeighborSignal = eventRedstone.getNewCurrent() > 0;
- }
- // CraftBukkit end
- // Paper start - break redstone on trapdoors early
- boolean open = state.getValue(TrapDoorBlock.OPEN) != hasNeighborSignal;
- // note: this must run before any state for this block/its neighbours are written to the world
- // we allow the redstone event to fire so that plugins can block
- if (hasNeighborSignal && open) { // if we are now powered and it caused the trap door to open
- // in this case, first check for the redstone on top first
- BlockPos abovePos = pos.above();
- BlockState above = level.getBlockState(abovePos);
- if (above.getBlock() instanceof RedStoneWireBlock) {
- level.setBlock(abovePos, Blocks.AIR.defaultBlockState(), Block.UPDATE_CLIENTS | Block.UPDATE_NEIGHBORS);
- Block.popResource(level, abovePos, new net.minecraft.world.item.ItemStack(net.minecraft.world.item.Items.REDSTONE));
- // now check that this didn't change our state
- if (level.getBlockState(pos) != state) {
- // our state was changed, so we cannot propagate this update
- return;
- }
- }
- }
- if (open) {
- // Paper end - break redstone on trapdoors early
+ if (state.getValue(OPEN) != hasNeighborSignal) {
state = state.setValue(OPEN, hasNeighborSignal);
this.playSound(null, level, pos, hasNeighborSignal);
}
@@ -0,0 +1,42 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Helvetica Volubi <suisuroru@blue-millennium.fun>
Date: Tue, 18 Nov 2025 00:35:13 +0800
Subject: [PATCH] Leaves: Prevent loss of item drops due to update suppression
when breaking blocks
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/level/ServerPlayerGameMode.java b/net/minecraft/server/level/ServerPlayerGameMode.java
index ee7bdc3adbeb04cf6ca630282f31a21e52bdb215..58f6c3e83c80b4b419ba22da81a7880be5b5d551 100644
--- a/net/minecraft/server/level/ServerPlayerGameMode.java
+++ b/net/minecraft/server/level/ServerPlayerGameMode.java
@@ -369,7 +369,18 @@ public class ServerPlayerGameMode {
this.level.getCurrentWorldData().captureDrops = new java.util.ArrayList<>(); // Folia - region threading
// CraftBukkit end
BlockState blockState1 = block.playerWillDestroy(this.level, pos, blockState, this.player);
- boolean flag = this.level.removeBlock(pos, false);
+ // Leaves start - Prevent loss of item drops due to update suppression when breaking blocks
+ boolean flag;
+ org.leavesmc.leaves.util.UpdateSuppressionException ex = null;
+ try {
+ flag = this.level.removeBlock(pos, false);
+ } catch (org.leavesmc.leaves.util.UpdateSuppressionException e) {
+ ex = e;
+ ex.provideBlock(level, pos, block);
+ ex.providePlayer(this.player);
+ flag = false;
+ }
+ // Leaves end - Prevent loss of item drops due to update suppression when breaking blocks
if (flag) {
block.destroy(this.level, pos, blockState1);
}
@@ -397,6 +408,7 @@ public class ServerPlayerGameMode {
if (event.isDropItems()) {
org.bukkit.craftbukkit.event.CraftEventFactory.handleBlockDropItemEvent(bblock, state, this.player, itemsToDrop); // Paper - capture all item additions to the world
}
+ if (ex != null) throw ex; // Leaves - Prevent loss of item drops due to update suppression when breaking blocks
// Drop event experience
if (flag) {

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