Compare commits

...

34 Commits

Author SHA1 Message Date
Helvetica Volubi 311fd32acb fix some bugs in lagFree (https://github.com/LuminolMC/Lophine/issues/145) 2026-06-03 20:13:13 +08:00
Helvetica Volubi 06dc3306f9 Update Luminol 2026-06-03 15:06:44 +08:00
Helvetica Volubi 896aed0f2d fix: reproduce rewritten concurrent table to fix a bug in hud data protocol 2026-05-07 19:29:12 +08:00
ChunghwaMC daa146c8b5 fix ServuxHudDataProtocol ConcurrentModificationException (#144)
* fix ServuxHudDataProtocol ConcurrentModificationException

* fix build error
2026-05-04 00:44:01 +08:00
Helvetica Volubi e0d42cb1d3 Update Luminol 2026-05-01 23:12:36 +08:00
xiaoyueyoqwq 559b2e089a Fix Syncmatica modifier cleanup (#141)
Co-authored-by: xiaoyueyoqwq <xiaoyueyoqwq@users.noreply.github.com>
2026-05-01 23:07:35 +08:00
Helvetica Volubi a07649d14d [ci skip]update code from Leaves to fix some bug 2026-05-01 23:06:47 +08:00
Helvetica Volubi 6aa709cf75 [ci skip] fixup ci 2026-04-22 18:30:09 +08:00
Helvetica Volubi 6a452799cc feat: add full blocking load option for localized language support 2026-04-22 13:26:54 +08:00
Helvetica Volubi e17a3f2033 perf: some perform update && update Gradle wrapper to version 9.4.1 2026-04-22 01:40:07 +08:00
Helvetica Volubi 74ac0bccf5 fix: fix some function is not covered by core config 2026-04-14 00:41:54 +08:00
xiaoxijun f65b7f176b Carpet features (#134)
* Carpet features

* refactor(config): move up config

* refactor file

diff in file system

* Update Luminol

* move up - stage 1

* Revert "Update Luminol"

This reverts commit 6f02993bfe.

* move up - stage 2

* new carpet features

* upload docs

* stage - pre update

* [ci skip]Update Luminol

only update for dev branch, so skip ci

* remove blank

* feat:add carpet core config & move some function to config load stage

* remove some unused value & logic

* fix up

* Update Luminol

* fix up

* remove imports update

* add warning to carpet system if it is enabled

* Update Luminol

* use new function to sync config

* oops!

* Update Luminol

* fix up a bug

* refactor

* Update Luminol

* move up directory

* Update Luminol

* oops!

* refactor need

* feat: add CoreConfig to enable general rules in CarpetCompatSync

* modify some default value

* fix: enhance HUD logger subscriptions and improve player data handling

---------

Co-authored-by: Helvetica Volubi <suisuroru@blue-millennium.fun>
Co-authored-by: Helvetica Volubi <88063803+Suisuroru@users.noreply.github.com>
2026-04-13 19:45:35 +08:00
Helvetica Volubi 97a17dbf78 fix: update Luminol plugin versions in build configuration 2026-04-13 18:35:25 +08:00
Bacteriawa c5194eeb8f Update Luminol 2026-04-11 03:40:59 +08:00
Helvetica Volubi 58c5573421 fix: fix some crash problem 2026-04-08 09:49:45 +08:00
Helvetica Volubi 1907a41329 perf: some perform update for global entities counter 2026-04-08 09:15:55 +08:00
Helvetica Volubi 21cce73216 fix GlobalEntitiesCounter again 2026-04-07 20:25:57 +08:00
Helvetica Volubi 163a9902cc fix: fix mob despawn when player is away from all the player 2026-04-02 18:38:19 +08:00
Helvetica Volubi ba1216d4d3 Update Luminol 2026-03-30 00:08:27 +08:00
dependabot[bot] beb5cbeb52 [ci skip]Bump gradle/actions from 5 to 6 (#135)
Bumps [gradle/actions](https://github.com/gradle/actions) from 5 to 6.
- [Release notes](https://github.com/gradle/actions/releases)
- [Commits](https://github.com/gradle/actions/compare/v5...v6)

---
updated-dependencies:
- dependency-name: gradle/actions
  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>
2026-03-28 18:41:16 +08:00
Helvetica Volubi 5cc4371833 Update Luminol 2026-03-26 08:50:42 +08:00
Helvetica Volubi 35f1e58d4d [ci skip]Update Luminol
only update for dev branch, so skip ci
2026-03-22 15:14:15 +08:00
Helvetica Volubi 690d7c2785 Update Luminol 2026-03-21 21:56:12 +08:00
Helvetica Volubi 115c9f8ee8 fix: try to fix a bug in syncmatica protocol 2026-03-19 19:00:17 +08:00
Helvetica Volubi faa3529943 feat: split timeout options to config file 2026-03-19 18:39:37 +08:00
Helvetica Volubi 3c3a2a6ff4 [ci skip] fixup 2026-03-16 18:09:49 +08:00
Helvetica Volubi 25776c7200 Update Luminol
我去,这个怎么没提交上
2026-03-16 16:39:26 +08:00
Helvetica Volubi 74e9d146e3 Update Luminol & adapt auto update system 2026-03-16 16:08:58 +08:00
Helvetica Volubi a8b2972e0e [ci skip] fix up a config transform flag 2026-03-14 01:38:42 +08:00
xiaoxijun ed3063a477 [ci skip]Update gradle.properties 2026-03-10 23:40:16 +08:00
Helvetica Volubi 85d2fa3372 feat: add Leaves' Creative Fly No Clip feature
fix #16
2026-03-09 00:13:34 +08:00
Helvetica Volubi b22e9e588b Update Luminol 2026-03-08 00:20:08 +08:00
Helvetica Volubi 90b751da5a Update Luminol 2026-03-03 20:55:18 +08:00
dependabot[bot] 7efeb48a7e [ci skip]Bump actions/upload-artifact from 6 to 7 (#133)
Bumps [actions/upload-artifact](https://github.com/actions/upload-artifact) from 6 to 7.
- [Release notes](https://github.com/actions/upload-artifact/releases)
- [Commits](https://github.com/actions/upload-artifact/compare/v6...v7)

---
updated-dependencies:
- dependency-name: actions/upload-artifact
  dependency-version: '7'
  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>
2026-02-28 16:29:34 +08:00
70 changed files with 3838 additions and 185 deletions
+3 -3
View File
@@ -39,7 +39,7 @@ jobs:
java-version: 21
- name: Setup Gradle
uses: gradle/actions/setup-gradle@v5
uses: gradle/actions/setup-gradle@v6
- name: Grant execute permission for gradlew
run: chmod +x gradlew
@@ -54,7 +54,7 @@ jobs:
run: ./gradlew --refresh-dependencies createMojmapPaperclipJar
- name: Upload Artifact
uses: actions/upload-artifact@v6
uses: actions/upload-artifact@v7
with:
name: ${{ env.project_id_b }} CI Artifacts
path: lophine-server/build/libs/*-paperclip-*-mojmap.jar
@@ -85,7 +85,7 @@ jobs:
echo "release_exists=${release_exists}" >> $GITHUB_ENV
- name: Check If Branch In Expectations
if: github.event_name != 'pull_request'
if: github.event_name != 'pull_request' && env.flag_release == 'true'
run: |
if [[ "$GITHUB_REF_NAME" == ver/* ]] || [[ "$GITHUB_REF_NAME" == exp/* ]] || [[ "$GITHUB_REF_NAME" == dev/* ]]; then
echo "Branch is in expected prefixes"
+1 -1
View File
@@ -3,7 +3,7 @@ import org.gradle.api.tasks.testing.logging.TestLogEvent
plugins {
java // TODO java launcher tasks
id("moe.luminolmc.hyacinthusweight.patcher") version "+" // Automatically pulls the latest
id("moe.luminolmc.hyacinthusweight.patcher")
}
paperweight {
+89
View File
@@ -0,0 +1,89 @@
### Carpet 原版
| 规则 | 状态 | 说明 |
| --- | --- | --- |
| `language` | 已映射 | 转发到 `lophine.function.language.lang` |
| `commandTick` | 已映射 | 转发到现有的 tick 指令补丁 |
| `creativeNoClip` | 已映射 | 转发到 Lophine 的创造飞行穿墙实现 |
| `ctrlQCraftingFix` | 结构等价 | 当前的合成与切石机菜单已经自带修复后的 Ctrl+Q 结果槽丢弃行为 |
| `placementRotationFix` | 已移植 | 放置朝向判断可使用玩家身体朝向而不是头部旋转 |
| `tntDoNotUpdate` | 已移植 | 新放置的 TNT 不会再因放置时更新而自动点燃 |
| `explosionNoBlockDamage` | 已移植 | 爆炸仍会伤害实体,但不会破坏方块 |
| `interactionUpdates` | 已移植 | 玩家交互和破坏方块时可以在不触发邻居更新与形状更新的情况下运行 |
| `xpNoCooldown` | 已移植 | 经验球可以在同一 tick 内被连续吸收,不再有拾取冷却 |
| `viewDistance` | 已移植 | 专用服务器启动视距会被兼容配置覆盖 |
| `fastRedstoneDust` | 已移植 | 规则启用时,红石粉更新会统一走 Alternate Current 快速更新后端 |
| `lagFreeSpawning` | 已移植 | 自然生成会使用轻量碰撞检查与预构造实体路径 |
| `defaultLoggers` | 已移植 | 按 Carpet 默认订阅语义为玩家启用 HUD logger,当前支持 `tps``mobcaps``counter` |
| `commandPlayer` | 已映射 | 当前由同一套 `/bot` 假人能力承接 |
| `hopperCounters` | 已映射 | 转发到 Lophine 的羊毛漏斗计数器 |
### Carpet TIS Addition
| 规则 | 状态 | 说明 |
| --- | --- | --- |
| `yeetUpdateSuppressionCrash` | 已映射 | 转发到同一套 Lophine 崩溃修复逻辑 |
| `dustTrapdoorReintroduced` | 已映射 | 转发到 `lophine.experiment.redstone.redstone-ignore-upwards-update` |
| `shulkerBoxCCEReintroduced` | 已映射 | 转发到 `lophine.experiment.redstone.cce-update-suppression` |
| `instantBlockUpdaterReintroduced` | 已映射 | 转发到 `lophine.experiment.redstone.instant-block-updater` |
| `optimizedDragonRespawn` | 已映射 | 转发到 Luminol 的末影龙重生优化 |
| `antiSpamDisabled` | 已移植 | 在 `ServerGamePacketListenerImpl` 中关闭聊天和创造丢物防刷屏限制 |
| `blockPlacementIgnoreEntity` | 已移植 | 创造模式放置方块时忽略实体碰撞检查 |
| `creativeOpenContainerForcibly` | 已移植 | 创造玩家可以强制打开被阻挡的箱子、末影箱和潜影盒 |
| `observerNoDetection` | 已移植 | 观察者的检测触发会在发出脉冲前被取消 |
| `creativeNoItemCooldown` | 已移植 | 创造玩家不会应用物品冷却 |
| `totallyNoBlockUpdate` | 已移植 | 邻居更新和形状更新会在 `NeighborUpdater` 中被统一短路 |
| `tiscmNetworkProtocol` | 已移植 | 原生 `tiscm:network/v1` 握手与数据包协商已经通过专用 Leaves 协议接入 |
| `optimizedTNTHighPriority` | 结构等价 | 当前 `ServerExplosion` 路径已经运行在优化过的服务端爆炸实现上 |
| `tntIgnoreRedstoneSignal` | 已移植 | TNT 在自动点燃判断时忽略红石信号 |
| `tntDupingFix` | 已移植 | 活塞复制 TNT 的路径现在由 `PistonBaseBlock` 中的兼容规则直接控制 |
| `clientSettingsLostOnRespawnFix` | 已移植 | 玩家上一次的客户端设置会在 `ServerPlayer.restoreFrom` 中恢复 |
| `entityInstantDeathRemoval` | 已移植 | 已死亡生物不会再保留原版的 20gt 延迟,而是立即移除 |
| `farmlandTrampledDisabled` | 已移植 | 耕地不会再因实体踩踏而变回泥土 |
| `yeetOutOfOrderChatKick` | 已移植 | 乱序的签名聊天不会再破坏安全聊天链 |
| `tickCommandPermission` | 已移植 | `/tick` 的权限等级可通过兼容配置调整 |
| `tickFreezeCommandToggleable` | 已移植 | 已冻结时再次执行 `/tick freeze` 会切换回运行状态 |
| `syncServerMsptMetricsData` | 已移植 | 实时 MSPT 样本会通过原生 TISCM 协议通道广播 |
| `microTiming` | 结构等价 | 当前 Folia 的区块级性能分析与计时钩子已经提供对应的服务端观测能力 |
| `optimizedFastEntityMovement` | 结构等价 | Moonrise/Paper 的快速实体移动碰撞优化已经是基础运行时的一部分 |
| `optimizedHardHitBoxEntityCollision` | 结构等价 | Moonrise/Paper 的硬碰撞箱实体碰撞优化已经是基础运行时的一部分 |
| `tntFuseDuration` | 已移植 | 可配置的 TNT 引信时长已接入 NMS TNT 逻辑 |
| `fakePlayerTicksLikeRealPlayer` | 已映射 | 转发到 Leaves 的假人网络阶段 tick 行为 |
| `hopperCountersUnlimitedSpeed` | 已映射 | 转发到不限速计数器模式 |
### Carpet Org Addition
| 规则 | 状态 | 说明 |
| --- | --- | --- |
| `hopperNoItemCost` | 已移植 | 漏斗上方放置羊毛时,可在传输后恢复被推出的物品堆,实现零消耗供给线 |
| `simpleInGameCalculator` | 已移植 | 以 `=` 开头的聊天内容会作为简单计算表达式私聊回复 |
### Carpet AMS Addition
| 规则 | 状态 | 说明 |
| --- | --- | --- |
| `amsUpdateSuppressionCrashFix` | 已映射 | 转发到 Lophine 现有的更新抑制崩溃修复 |
| `creativeOneHitKill` | 已移植 | 创造玩家可通过 `Player.attack` 瞬间击杀附近符合条件的目标 |
| `bambooModelNoOffset` | 已移植 | 竹子和竹笋始终返回零模型偏移 |
| `carpetAlwaysSetDefault` | 结构等价 | 兼容配置默认值已经会在 `ConfigsInstance.preLoadConfig` 中被写入 |
| `powerfulExpMending` | 已移植 | 经验拾取会修复玩家整个背包内所有受损的经验修补物品 |
| `sensibleEnderman` | 已移植 | 末影人仅会在规则开启时拾取南瓜和西瓜 |
| `shulkerGolem` | 已移植 | 潜影盒上方放置雕刻南瓜时可以召唤潜影贝 |
| `preventEndSpikeRespawn` | 已移植 | 末影龙重生时会跳过黑曜石柱重建和柱顶水晶重建 |
| `betterCraftableBoneBlock` | 已移植 | 向配方管理器注入 AMS 的替代骨块配方 |
| `betterCraftableDispenser` | 已移植 | 向配方管理器注入 AMS 的替代发射器配方 |
| `fakePlayerDefaultSurvivalMode` | 已移植 | 新创建的假人可以被强制设为生存模式,而不是沿用服务器默认游戏模式 |
| `fakePlayerInteractLikeClient` | 已移植 | 假人与盔甲架交互时会先按真实客户端方式回退,再进入普通实体交互 |
| `fakePlayerAutoReplenishmentFormShulkerBox` | 已移植 | 假人自动补货现在可以从背包里的潜影盒中抽取匹配物品 |
### Lophine 扩展承接
| 规则 | 状态 | 说明 |
| --- | --- |----------------------------------|
| `commandBot` | 已映射 | 启用 Lophine `/bot` |
| `fakePlayerResident` | 已映射 | 转发到 Lophine 的假人常驻模式 |
| `openFakePlayerInventory` | 已映射 | 转发到 Lophine 的假人背包打开功能 |
| `fakePlayerAutoReplaceTool` | 已映射 | 为现有自动换工具逻辑增加了新开关 |
| `fakePlayerAutoReplenishment` | 已映射 | 为现有自动补货逻辑增加了新开关 |
| `fakePlayerReloadAction` | 已映射 | 为假人动作持久化增加了新开关 |
| `fakePlayerAutoFish` | 已移植 | 手持鱼竿的假人会自动抛竿和收杆,除非显式执行 `fish` 动作 |
+3 -3
View File
@@ -1,13 +1,13 @@
group=fun.bm.lophine
version=1.21.11-R0.1-SNAPSHOT
mcVersion=1.21.11
# This is the current API version for use in (paper-)plugin.yml files
# During snapshot cycles this should be the anticipated version of the release target
apiVersion=1.21.11
clipVersion=3.0.15-20260413.051550-1
weightVersion=2.0.7
release=2
# 0 for skip release, 1 for pre-release, 2 for release
luminolRef=78a3b89f5850f71ddb5b1d1adeccda47f5762a1b
luminolRef=274f341465b3143c421de2c3110a69da4b68b75b
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-9.3.1-bin.zip
distributionUrl=https\://services.gradle.org/distributions/gradle-9.4.1-bin.zip
networkTimeout=10000
validateDistributionUrl=true
zipStoreBase=GRADLE_USER_HOME
+3 -3
View File
@@ -49,9 +49,9 @@
- implementation(project(":luminol-api")) // Luminol
+ implementation(project(":lophine-api")) // Lophine
// Luminol start - Dependenices insert
implementation("net.objecthunter:exp4j:0.4.8")
implementation("io.netty:netty-all:4.2.9.Final") // used for io_uring
implementation("com.electronwill.night-config:toml:3.8.3") // Night config
@@ -273,14 +_,14 @@
@@ -274,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,
@@ -432,7 +_,7 @@
@@ -433,7 +_,7 @@
}
fill {
@@ -5,14 +5,14 @@ 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 0bebc2cc87fb9c1c526e4de4f3bc157552307a72..cb1657939b86c039fcfe6403d3f202c2c5879e88 100644
index 490316c7d5cbb77e988039cfa7a7cc0aa3849730..f5fb4e210c07f32e52eb8c1ba166d1571a6778c7 100644
--- a/src/main/java/me/earthme/luminol/config/ConfigManager.java
+++ b/src/main/java/me/earthme/luminol/config/ConfigManager.java
@@ -23,6 +23,7 @@ public class ConfigManager {
@@ -24,6 +24,7 @@ public class ConfigManager {
public static void initConfigs() {
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
+ configfiles.put("lophine", ConfigsInstance.of("lophine", "fun.bm.lophine.config.modules")); // add lophine global config
preLoad();
}
@@ -5,7 +5,7 @@ Subject: [PATCH] Transformed Configs
diff --git a/src/main/java/me/earthme/luminol/config/modules/experiment/CommandConfig.java b/src/main/java/me/earthme/luminol/config/modules/experiment/CommandConfig.java
index ad2ba680bedb2336de24146676e70a1f64fd9c5a..f81581782bf684bbcf4719e9b268efa3c6385325 100644
index ad2ba680bedb2336de24146676e70a1f64fd9c5a..505f7fe2ac40451e56967c90df9c52fb35f80fdf 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
@@ -13,6 +13,7 @@ public class CommandConfig implements IConfigModule {
@@ -20,18 +20,18 @@ index ad2ba680bedb2336de24146676e70a1f64fd9c5a..f81581782bf684bbcf4719e9b268efa3
DO NOT ENABLE UNLESS YOU KNOW WHAT YOU ARE DOING!!!
""")
public static boolean commandBlock = false;
+ @TransformedConfig(name = "enable-waypoint", directory = {"experiment", "waypoint bar"})
+ @TransformedConfig(name = "enable-waypoint", directory = {"experiment", "waypoint_bar"})
+ @TransformedConfig(name = "waypoint_command_enabled", directory = {"experiment", "command"})
+ @TransformedConfig(name = "enable-waypoint", directory = {"experiment", "waypoint bar"}, originInstance = "lophine")
+ @TransformedConfig(name = "enable-waypoint", directory = {"experiment", "waypoint_bar"}, originInstance = "lophine")
+ @TransformedConfig(name = "waypoint_command_enabled", directory = {"experiment", "command"}, originInstance = "lophine")
@ConfigInfo(name = "enable_waypoints_and_waypoint_command", comments = """
Enable waypoint and waypoint command.
WARN: Still under testing
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 ce190737de22438d5de86b0ee0e8158954fdca18..0a175e87f3cb5b23480abefd70ee2e84e876b0d4 100644
index aece37eb6406f8ada56428e68cf198c43f8ae309..d5f99fd672e33c16fc3c3e2031ce2e5a5001ab55 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
@@ -11,6 +11,7 @@ import me.earthme.luminol.enums.EnumConfigCategory;
"has negative impacts on security"
"has negative impacts on security"
)
public class PaperPacketLimiterConfig implements IConfigModule {
+ @TransformedConfig(name = "unlimit-packet", directory = {"misc", "network"}, originInstance = "lophine")
@@ -8,10 +8,10 @@ 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/me/earthme/luminol/utils/FoliaServerWaypointManager.java b/src/main/java/me/earthme/luminol/utils/FoliaServerWaypointManager.java
index 14cf379decfd46c4719bc192a1467bbbd6835417..20970844de2e64ca213384e53003e80020165770 100644
index 948730740e151352b4a12a90862f1e16925e52b0..0e6c9edbceef525bf102ad8f1fc0cb08e789d0e6 100644
--- a/src/main/java/me/earthme/luminol/utils/FoliaServerWaypointManager.java
+++ b/src/main/java/me/earthme/luminol/utils/FoliaServerWaypointManager.java
@@ -38,6 +38,12 @@ public class FoliaServerWaypointManager implements WaypointManager<@NotNull Wayp
@@ -39,6 +39,12 @@ public class FoliaServerWaypointManager implements WaypointManager<@NotNull Wayp
return;
}
@@ -24,7 +24,7 @@ index 14cf379decfd46c4719bc192a1467bbbd6835417..20970844de2e64ca213384e53003e800
this.waypoints.add(waypoint);
for (ServerPlayer toCreateFor : this.trackingPlayers) {
@@ -91,6 +97,12 @@ public class FoliaServerWaypointManager implements WaypointManager<@NotNull Wayp
@@ -92,6 +98,12 @@ public class FoliaServerWaypointManager implements WaypointManager<@NotNull Wayp
public void addPlayer(ServerPlayer player) {
this.trackingPlayers.add(player);
@@ -0,0 +1,150 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Helvetica Volubi <suisuroru@blue-millennium.fun>
Date: Mon, 16 Mar 2026 15:51:40 +0800
Subject: [PATCH] Diff in auto-update patch
diff --git a/src/main/java/me/earthme/luminol/config/modules/misc/AutoUpdateConfig.java b/src/main/java/me/earthme/luminol/config/modules/misc/AutoUpdateConfig.java
index ce01352b637547c1cf8308d56d4b3f9c48fcca70..d57c04d839401e8ba88e56d92be2798bb312949f 100644
--- a/src/main/java/me/earthme/luminol/config/modules/misc/AutoUpdateConfig.java
+++ b/src/main/java/me/earthme/luminol/config/modules/misc/AutoUpdateConfig.java
@@ -16,13 +16,13 @@ import java.util.Set;
category = EnumConfigCategory.MISC,
name = "auto_update",
comments = """
- Checks GitHub Releases for newer Luminol jars on a schedule.
- Downloads are staged under auto_update/luminol and written to auto_update/core.path,
+ Checks GitHub Releases for newer version's jars on a schedule.
+ Downloads are staged under auto_update/lophine and written to auto_update/core.path,
which Hyacinthusclip can consume on the next restart.
- If target_jar_path is set, Luminol will also try to replace that launcher jar directly."""
+ If target_jar_path is set, server will also try to replace that launcher jar directly."""
)
public class AutoUpdateConfig implements IConfigModule {
- @ConfigInfo(name = "enabled", comments = "Whether Luminol should check for updates automatically.")
+ @ConfigInfo(name = "enabled", comments = "Whether the server should check for updates automatically.")
public static boolean enabled = false;
@ConfigInfo(name = "check_times", comments = "List of daily check times in HH:mm, based on the server's local time zone.")
@@ -33,7 +33,7 @@ public class AutoUpdateConfig implements IConfigModule {
@ConfigInfo(name = "target_jar_path", comments = """
Optional launcher jar path to replace after a successful download.
- Leave this blank to keep the downloaded jar staged in auto_update/luminol
+ Leave this blank to keep the downloaded jar staged in auto_update/lophine
and let Hyacinthusclip switch to it through auto_update/core.path on restart.""")
public static String targetJarPath = "";
@@ -42,7 +42,7 @@ public class AutoUpdateConfig implements IConfigModule {
@Override
public void onLoaded(CommentedFileConfig configInstance, @Nullable Set<Exception> exs) {
- if (enabled) {
+ if (enabled || fun.bm.lophine.config.modules.misc.AutoUpdateConfig.enabled) {
if (instance == null) {
instance = new AutoUpdateHelper();
}
diff --git a/src/main/java/me/earthme/luminol/utils/AutoUpdateHelper.java b/src/main/java/me/earthme/luminol/utils/AutoUpdateHelper.java
index 94a25cd681c9352d6931edafd9ec67fd7ba8a777..d0d0cf5e5ff8d4b8efa4d9778c27267b41cf8cf8 100644
--- a/src/main/java/me/earthme/luminol/utils/AutoUpdateHelper.java
+++ b/src/main/java/me/earthme/luminol/utils/AutoUpdateHelper.java
@@ -35,10 +35,10 @@ public final class AutoUpdateHelper {
private final Logger LOGGER = LogUtils.getClassLogger();
private final Gson GSON = new Gson();
- private final String GITHUB_API_BASE = "https://api.github.com/repos/LuminolMC/Luminol";
+ private final String GITHUB_API_BASE = "https://api.github.com/repos/lophineMC/lophine";
private final Path AUTO_UPDATE_DIR = Path.of("auto_update");
private final Path CORE_PATH_FILE = AUTO_UPDATE_DIR.resolve("core.path");
- private final Path LUMINOL_UPDATE_DIR = AUTO_UPDATE_DIR.resolve("luminol");
+ private final Path LUMINOL_UPDATE_DIR = AUTO_UPDATE_DIR.resolve("lophine");
private final Path LATEST_PATH_FILE = LUMINOL_UPDATE_DIR.resolve("latest.path");
private final long DAILY_TASK_PERIOD_MILLIS = TimeUnit.DAYS.toMillis(1);
@@ -51,13 +51,13 @@ public final class AutoUpdateHelper {
ensureWorkingDirectories();
scheduler = Executors.newSingleThreadScheduledExecutor(task -> {
- final Thread thread = new Thread(task, "Luminol Auto Update Scheduler");
+ final Thread thread = new Thread(task, "Lophine Auto Update Scheduler");
thread.setDaemon(true);
return thread;
});
if (AutoUpdateConfig.checkTimes.isEmpty()) {
- LOGGER.warn("Luminol auto-update is enabled, but misc.auto_update.check_times is empty.");
+ LOGGER.warn("Lophine auto-update is enabled, but misc.auto_update.check_times is empty.");
return;
}
@@ -95,14 +95,14 @@ public final class AutoUpdateHelper {
private void checkForUpdatesSafely() {
if (!UPDATE_RUNNING.compareAndSet(false, true)) {
- LOGGER.info("A Luminol auto-update check is already running, skipping this schedule.");
+ LOGGER.info("A Lophine auto-update check is already running, skipping this schedule.");
return;
}
try {
checkForUpdates();
} catch (Exception e) {
- LOGGER.warn("Luminol auto-update failed.", e);
+ LOGGER.warn("Lophine auto-update failed.", e);
} finally {
UPDATE_RUNNING.set(false);
}
@@ -123,7 +123,7 @@ public final class AutoUpdateHelper {
final ReleaseAssetInfo latestRelease = getLatestCompatibleRelease(mcVersion, gitBranch);
if (latestRelease == null) {
- LOGGER.warn("No compatible Luminol release was found for Minecraft {}{}.",
+ LOGGER.warn("No compatible Lophine release was found for Minecraft {}{}.",
mcVersion,
gitBranch == null ? "" : " on branch " + gitBranch
);
@@ -132,7 +132,7 @@ public final class AutoUpdateHelper {
final UpdateStatus updateStatus = compareReleaseWithCurrentBuild(latestRelease.releaseCommitHash(), currentGitHash);
if (updateStatus == UpdateStatus.UP_TO_DATE) {
- LOGGER.info("You are already running the latest compatible Luminol release: {}", latestRelease.tagName());
+ LOGGER.info("You are already running the latest compatible Lophine release: {}", latestRelease.tagName());
return;
}
@@ -154,7 +154,7 @@ public final class AutoUpdateHelper {
return;
}
- LOGGER.info("Found a newer Luminol release: {}", latestRelease.tagName());
+ LOGGER.info("Found a newer Lophine release: {}", latestRelease.tagName());
final Path stagedJar = downloadAndStageRelease(latestRelease);
final Path finalJarPath = tryApplyTargetJar(stagedJar);
@@ -163,7 +163,7 @@ public final class AutoUpdateHelper {
if (finalJarPath.equals(stagedJar)) {
LOGGER.info(
- "Downloaded the latest Luminol jar to {} and refreshed auto_update/core.path for Hyacinthusclip. Please restart your server.",
+ "Downloaded the latest Lophine jar to {} and refreshed auto_update/core.path for Hyacinthusclip. Please restart your server.",
finalJarPath.toAbsolutePath()
);
} else {
@@ -179,7 +179,7 @@ public final class AutoUpdateHelper {
try {
Files.createDirectories(LUMINOL_UPDATE_DIR);
} catch (IOException e) {
- throw new RuntimeException("Failed to create Luminol auto update directory", e);
+ throw new RuntimeException("Failed to create Lophine auto update directory", e);
}
}
@@ -440,7 +440,7 @@ public final class AutoUpdateHelper {
private @NotNull HttpURLConnection openConnection(String url) throws IOException, URISyntaxException {
final HttpURLConnection connection = (HttpURLConnection) new URI(url).toURL().openConnection();
connection.setRequestProperty("Accept", "application/vnd.github+json");
- connection.setRequestProperty("User-Agent", "Luminol Auto Update");
+ connection.setRequestProperty("User-Agent", "Lophine Auto Update");
connection.setConnectTimeout(10000);
connection.setReadTimeout(10000);
return connection;
@@ -0,0 +1,24 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Bacteriawa <A3167717663@hotmail.com>
Date: Sun, 22 Mar 2026 01:39:17 +0800
Subject: [PATCH] Carpet features
diff --git a/src/main/java/me/earthme/luminol/config/ConfigManager.java b/src/main/java/me/earthme/luminol/config/ConfigManager.java
index f5fb4e210c07f32e52eb8c1ba166d1571a6778c7..e990c147e1b632ef5a33e4f3c37b5cd608040ac7 100644
--- a/src/main/java/me/earthme/luminol/config/ConfigManager.java
+++ b/src/main/java/me/earthme/luminol/config/ConfigManager.java
@@ -25,6 +25,13 @@ public class ConfigManager {
public static void initConfigs() {
configfiles.put("luminol", ConfigsInstance.of("luminol", "me.earthme.luminol.config.modules"));
configfiles.put("lophine", ConfigsInstance.of("lophine", "fun.bm.lophine.config.modules")); // add lophine global config
+ configfiles.put("lophine_carpet", ConfigsInstance.of(
+ new java.io.File("lophine_config"),
+ "lophine_carpet",
+ "lophine_carpet_config.toml",
+ "lophinecarpetconfig",
+ "fun.bm.lophine.carpet.config.modules"
+ )); // add lophine carpet config
preLoad();
}
@@ -5,7 +5,7 @@ Subject: [PATCH] Add config to disable some check for operators
diff --git a/net/minecraft/server/network/ServerGamePacketListenerImpl.java b/net/minecraft/server/network/ServerGamePacketListenerImpl.java
index bd344bbebc867f89061694edc0feeab9d679c90c..6d0bb9868e2c2ef516de7630442808c04ba47b39 100644
index 90e044ab3476d91819dddf14054d4626358da363..5c5dfcefd9ce6c9d7f69fb6d03216805b9e63822 100644
--- a/net/minecraft/server/network/ServerGamePacketListenerImpl.java
+++ b/net/minecraft/server/network/ServerGamePacketListenerImpl.java
@@ -392,7 +392,7 @@ public class ServerGamePacketListenerImpl
@@ -30,8 +30,8 @@ index bd344bbebc867f89061694edc0feeab9d679c90c..6d0bb9868e2c2ef516de7630442808c0
d5 = d2 - rootVehicle.getZ();
d7 = d3 * d3 + d4 * d4 + d5 * d5;
boolean flag1 = false;
- if (!me.earthme.luminol.config.modules.misc.DisableMovedWronglyThreshold.enabled && d7 > org.spigotmc.SpigotConfig.movedWronglyThreshold) { // Spigot
+ if (!me.earthme.luminol.config.modules.misc.DisableMovedWronglyThreshold.enabled && d7 > org.spigotmc.SpigotConfig.movedWronglyThreshold && !(fun.bm.lophine.config.modules.misc.DisableCheckConfig.disableOpMoveCheck && MinecraftServer.getServer().getPlayerList().isOp(this.player.nameAndId()))) { // Spigot // Lophine - disable move check for operator
- if (!me.earthme.luminol.config.modules.misc.DisableWarningConfig.disableMovedWronglyThresholdWarning && d7 > org.spigotmc.SpigotConfig.movedWronglyThreshold) { // Spigot
+ if (!me.earthme.luminol.config.modules.misc.DisableWarningConfig.disableMovedWronglyThresholdWarning && d7 > org.spigotmc.SpigotConfig.movedWronglyThreshold && !(fun.bm.lophine.config.modules.misc.DisableCheckConfig.disableOpMoveCheck && MinecraftServer.getServer().getPlayerList().isOp(this.player.nameAndId()))) { // Spigot // Lophine - disable move check for operator
flag1 = true; // Paper - diff on change, this should be moved wrongly
LOGGER.warn("{} (vehicle of {}) moved wrongly! {}", rootVehicle.getPlainTextName(), this.player.getPlainTextName(), Math.sqrt(d7));
}
@@ -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 08e0dde0aa5ff94b72127abcd7da2a63d5701db8..e311502c156076842c5dcd91c70a94e414312100 100644
index 607ce65a719e333ce2275346a356032327d00ccf..dcdb01fe5333d407a10f9872d09992c7b438ddeb 100644
--- a/net/minecraft/server/level/ServerPlayer.java
+++ b/net/minecraft/server/level/ServerPlayer.java
@@ -1445,6 +1445,13 @@ public class ServerPlayer extends Player implements ca.spottedleaf.moonrise.patc
@@ -48,7 +48,7 @@ index 08e0dde0aa5ff94b72127abcd7da2a63d5701db8..e311502c156076842c5dcd91c70a94e4
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 c67c29278ea63ab8b936d53bf654089f2dba2eef..88d532d11c29bd8cfe5b6b02847d960b07cf0a38 100644
index 7a63f52efca133d9d1a773da2443333009d87113..5bae274f5ee869e540e4330e03af671b6b18313d 100644
--- a/net/minecraft/world/entity/LivingEntity.java
+++ b/net/minecraft/world/entity/LivingEntity.java
@@ -1975,6 +1975,13 @@ public abstract class LivingEntity extends Entity implements Attackable, Waypoin
@@ -5,7 +5,7 @@ Subject: [PATCH] Add config to enable function command
diff --git a/net/minecraft/commands/Commands.java b/net/minecraft/commands/Commands.java
index 9c0c3835776bda3bab08f0abaffc89a72bba0bfc..b95d144dd479d339cc4b0f92cc4c3bade9af4927 100644
index 4fa276ba05a550cdf7ce48617056c5a4d4a18006..6cb0d4daf0f275b95d5ca09275e40ab32258eb39 100644
--- a/net/minecraft/commands/Commands.java
+++ b/net/minecraft/commands/Commands.java
@@ -215,7 +215,9 @@ public class Commands {
@@ -48,7 +48,7 @@ index 6cb0d4daf0f275b95d5ca09275e40ab32258eb39..48628174299f8f18170198df1834d617
SaveOnCommand.register(this.dispatcher);
SetPlayerIdleTimeoutCommand.register(this.dispatcher);
diff --git a/net/minecraft/server/MinecraftServer.java b/net/minecraft/server/MinecraftServer.java
index 4be383909addfebc0ca3b007d4ef2ec30ed445f7..fa60388376e9dafc5e1b71b6e78550bb6cd4009b 100644
index 8ce5cdda24d63b33b39e32f9712fafa3917f27c5..25324d81bbe876dbe47b33bd5749b72e4283bf80 100644
--- a/net/minecraft/server/MinecraftServer.java
+++ b/net/minecraft/server/MinecraftServer.java
@@ -1656,6 +1656,11 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
@@ -5,7 +5,7 @@ Subject: [PATCH] Add config to enable scoreboard command
diff --git a/net/minecraft/commands/Commands.java b/net/minecraft/commands/Commands.java
index 097e5b6a14fd148c93ca3894295a4a6b1e9aa870..bd8d98aa813b575e83138e79726bd3143268913c 100644
index 48628174299f8f18170198df1834d6174bd1fafa..91e2f3e9e85622adbd3799473634e716383bbe94 100644
--- a/net/minecraft/commands/Commands.java
+++ b/net/minecraft/commands/Commands.java
@@ -241,7 +241,11 @@ public class Commands {
@@ -97,7 +97,7 @@ index 0f4e8ee9438135e24e0f61a1390c59573b9e9c3f..65699d2ad66c686a8b7d1b951787ddd7
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 bd8d98aa813b575e83138e79726bd3143268913c..65f200381f486146ce8d893378f0591a82f6076b 100644
index 91e2f3e9e85622adbd3799473634e716383bbe94..21466c225db02a5be74bf60b5ea831c5bea8a7e8 100644
--- a/net/minecraft/commands/Commands.java
+++ b/net/minecraft/commands/Commands.java
@@ -262,7 +262,11 @@ public class Commands {
@@ -114,7 +114,7 @@ index bd8d98aa813b575e83138e79726bd3143268913c..65f200381f486146ce8d893378f0591a
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 76098a4f44f8d82bebf832fa5e240adf25fbe80a..644c49730fc08a7c680f8114136db00ee8f81378 100644
index 25324d81bbe876dbe47b33bd5749b72e4283bf80..504f21c5f1e3101e7164b97059aecce630d18cee 100644
--- a/net/minecraft/server/MinecraftServer.java
+++ b/net/minecraft/server/MinecraftServer.java
@@ -270,7 +270,7 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
@@ -5,10 +5,10 @@ Subject: [PATCH] Fix datapack command save function
diff --git a/net/minecraft/server/MinecraftServer.java b/net/minecraft/server/MinecraftServer.java
index d7d22806c010636d8c1471f0fd1d64ef54e92c97..8d96d2130cf3383427047fee97fa49155164e337 100644
index 504f21c5f1e3101e7164b97059aecce630d18cee..d5b680fc6e13ec26d0db2448009e4cbe59933e22 100644
--- a/net/minecraft/server/MinecraftServer.java
+++ b/net/minecraft/server/MinecraftServer.java
@@ -2431,6 +2431,16 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
@@ -2437,6 +2437,16 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
return this.reloadResources(selectedIds, io.papermc.paper.event.server.ServerResourcesReloadedEvent.Cause.PLUGIN);
}
@@ -8,10 +8,10 @@ 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 d0b5bc9da450d881534abdbd8ff786e1fbc7d418..674450fb2e600236f99550febf276d093308b8d8 100644
index b0546cb4af7291013399e67d98bced98a5fffc0f..bf340425dc2a1545b9069badcf054d4c2f6a405f 100644
--- a/net/minecraft/server/level/ServerLevel.java
+++ b/net/minecraft/server/level/ServerLevel.java
@@ -3030,7 +3030,7 @@ public class ServerLevel extends Level implements ServerEntityGetter, WorldGenLe
@@ -3058,7 +3058,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
@@ -41,10 +41,10 @@ index 1281eadb2d0c5b4ce5e3afd51d8205f9a4ef011c..b1446dfb4689174ee9f55cec6080622e
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 6d0bb9868e2c2ef516de7630442808c04ba47b39..1a749d9bee139975fa249839df93c3a1e48bf102 100644
index 5c5dfcefd9ce6c9d7f69fb6d03216805b9e63822..d35ef52a6adb9a560138d1a2af32969c3a73f42b 100644
--- a/net/minecraft/server/network/ServerGamePacketListenerImpl.java
+++ b/net/minecraft/server/network/ServerGamePacketListenerImpl.java
@@ -3162,7 +3162,7 @@ public class ServerGamePacketListenerImpl
@@ -3170,7 +3170,7 @@ public class ServerGamePacketListenerImpl
} else if (slot.mayPlace(cursor)) {
if (ItemStack.isSameItemSameComponents(clickedItem, cursor)) {
int toPlace = packet.buttonNum() == 0 ? cursor.getCount() : 1;
@@ -53,7 +53,7 @@ index 6d0bb9868e2c2ef516de7630442808c04ba47b39..1a749d9bee139975fa249839df93c3a1
toPlace = Math.min(toPlace, slot.container.getMaxStackSize() - clickedItem.getCount());
if (toPlace == 1) {
action = InventoryAction.PLACE_ONE;
@@ -3198,7 +3198,7 @@ public class ServerGamePacketListenerImpl
@@ -3206,7 +3206,7 @@ public class ServerGamePacketListenerImpl
}
} else if (ItemStack.isSameItemSameComponents(cursor, clickedItem)) {
if (clickedItem.getCount() >= 0) {
@@ -62,7 +62,7 @@ index 6d0bb9868e2c2ef516de7630442808c04ba47b39..1a749d9bee139975fa249839df93c3a1
// As of 1.5, this is result slots only
action = InventoryAction.PICKUP_ALL;
}
@@ -3415,6 +3415,7 @@ public class ServerGamePacketListenerImpl
@@ -3423,6 +3423,7 @@ public class ServerGamePacketListenerImpl
this.player.containerMenu.broadcastFullState();
} else {
this.player.containerMenu.broadcastChanges();
@@ -70,7 +70,7 @@ index 6d0bb9868e2c2ef516de7630442808c04ba47b39..1a749d9bee139975fa249839df93c3a1
}
if (packet.buttonNum() == Inventory.SLOT_OFFHAND && this.player.containerMenu != this.player.inventoryMenu) this.player.containerSynchronizer.sendOffHandSlotChange(); // Paper - update offhand data when the player is clicking in an inventory not their own as the sychronizer does not include offhand slots
if (io.papermc.paper.configuration.GlobalConfiguration.get().unsupportedSettings.updateEquipmentOnPlayerActions) this.player.detectEquipmentUpdates(); // Paper - Force update attributes.
@@ -3526,7 +3527,7 @@ public class ServerGamePacketListenerImpl
@@ -3534,7 +3535,7 @@ public class ServerGamePacketListenerImpl
}
boolean flag1 = packet.slotNum() >= 1 && packet.slotNum() <= 45;
@@ -79,7 +79,7 @@ index 6d0bb9868e2c2ef516de7630442808c04ba47b39..1a749d9bee139975fa249839df93c3a1
if (flag || (flag1 && !ItemStack.matches(this.player.inventoryMenu.getSlot(packet.slotNum()).getItem(), packet.itemStack()))) { // Insist on valid slot
// CraftBukkit start - Call click event
org.bukkit.inventory.InventoryView inventory = this.player.inventoryMenu.getBukkitView();
@@ -3568,6 +3569,7 @@ public class ServerGamePacketListenerImpl
@@ -3576,6 +3577,7 @@ public class ServerGamePacketListenerImpl
this.player.inventoryMenu.getSlot(packet.slotNum()).setByPlayer(itemStack);
this.player.inventoryMenu.setRemoteSlot(packet.slotNum(), itemStack);
this.player.inventoryMenu.broadcastChanges();
@@ -446,10 +446,10 @@ index 8f80c469c309bf61328c4f98037495f47a38c3c1..b4127d4b4386b98205275a9a0ef8067c
}
diff --git a/net/minecraft/world/level/block/entity/HopperBlockEntity.java b/net/minecraft/world/level/block/entity/HopperBlockEntity.java
index f851e0f7d727cb9dca70c578777d871af424388c..6a51bace3151646afae9de820d9c8977a0221df4 100644
index 4ae14236535d04713990f5abb3777001071f591c..3d2bf6371d39946576745aa7977cdc0f2ff07036 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
@@ -155,7 +155,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 f851e0f7d727cb9dca70c578777d871af424388c..6a51bace3151646afae9de820d9c8977
}
@Override
@@ -741,9 +741,9 @@ public class HopperBlockEntity extends RandomizableContainerBlockEntity implemen
@@ -751,9 +751,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
@@ -23,10 +23,10 @@ index 95f6b1cbd5e33151c326689bc72292ff38a48803..e1974bbebbd10b75ea02955d9ced198d
+ // Leaves end - servux
}
diff --git a/net/minecraft/server/level/ServerLevel.java b/net/minecraft/server/level/ServerLevel.java
index 674450fb2e600236f99550febf276d093308b8d8..f5bf8ed6e741823078ab93eac47636fcebcdfe2d 100644
index bf340425dc2a1545b9069badcf054d4c2f6a405f..eb5722848d1c77044807510cd6a46a46c5763509 100644
--- a/net/minecraft/server/level/ServerLevel.java
+++ b/net/minecraft/server/level/ServerLevel.java
@@ -2463,6 +2463,8 @@ public class ServerLevel extends Level implements ServerEntityGetter, WorldGenLe
@@ -2491,6 +2491,8 @@ public class ServerLevel extends Level implements ServerEntityGetter, WorldGenLe
this.server.updateEffectiveRespawnData();
}
// Paper end
@@ -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 ec34282744c71c881392ff278a8b03af90de8151..f0d45cb70af62b747c9235941f3fc45221b3b8e6 100644
index dcdb01fe5333d407a10f9872d09992c7b438ddeb..6df8f98afa7810815b8d7c86b3dac57cd5915682 100644
--- a/net/minecraft/server/level/ServerPlayer.java
+++ b/net/minecraft/server/level/ServerPlayer.java
@@ -240,6 +240,7 @@ public class ServerPlayer extends Player implements ca.spottedleaf.moonrise.patc
@@ -33,7 +33,7 @@ index 9f444988d40208a1bfa9f147ef80489f81115e93..ab2fa0b86564622501238305714fff01
private boolean encrypted;
private final java.util.concurrent.atomic.AtomicBoolean disconnectionHandled = new java.util.concurrent.atomic.AtomicBoolean(false); // Folia - region threading - may be called concurrently during configuration stage
diff --git a/net/minecraft/server/MinecraftServer.java b/net/minecraft/server/MinecraftServer.java
index 8d96d2130cf3383427047fee97fa49155164e337..dfcf8eb7dc422a55d94d6eb9eb1451c7c14a1d56 100644
index d5b680fc6e13ec26d0db2448009e4cbe59933e22..385c71d70ed9e5a3ae25fbecaefd6773c467d31d 100644
--- a/net/minecraft/server/MinecraftServer.java
+++ b/net/minecraft/server/MinecraftServer.java
@@ -425,6 +425,8 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
@@ -74,7 +74,7 @@ index 8d96d2130cf3383427047fee97fa49155164e337..dfcf8eb7dc422a55d94d6eb9eb1451c7
}
for (final ServerLevel level : (region == null ? this.getAllLevels() : Arrays.asList(region.world))) { // Folia - region threading
if (level.paperConfig().chunks.autoSaveInterval.value() > 0) {
@@ -2021,6 +2029,7 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
@@ -2027,6 +2035,7 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
public void tickConnection() {
this.getConnection().tick();
@@ -82,7 +82,7 @@ index 8d96d2130cf3383427047fee97fa49155164e337..dfcf8eb7dc422a55d94d6eb9eb1451c7
}
private void synchronizeTime(ServerLevel level) {
@@ -3155,6 +3164,16 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
@@ -3161,6 +3170,16 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
return this.debugSubscribers;
}
@@ -255,7 +255,7 @@ index fdccb06c918d387e46c29ede7337159db8c5d9ca..4cd5c5659ac098c3b7ff95efb94b1987
}
}
diff --git a/net/minecraft/server/level/ServerLevel.java b/net/minecraft/server/level/ServerLevel.java
index f5bf8ed6e741823078ab93eac47636fcebcdfe2d..14516c3ebd092e6f230804145a60dc851f34230b 100644
index eb5722848d1c77044807510cd6a46a46c5763509..88efbe2bcb5621432b67366116150a3dab52e510 100644
--- a/net/minecraft/server/level/ServerLevel.java
+++ b/net/minecraft/server/level/ServerLevel.java
@@ -220,6 +220,7 @@ public class ServerLevel extends Level implements ServerEntityGetter, WorldGenLe
@@ -274,7 +274,7 @@ index f5bf8ed6e741823078ab93eac47636fcebcdfe2d..14516c3ebd092e6f230804145a60dc85
}
// Folia start - region threading
@@ -2490,6 +2492,12 @@ public class ServerLevel extends Level implements ServerEntityGetter, WorldGenLe
@@ -2518,6 +2520,12 @@ public class ServerLevel extends Level implements ServerEntityGetter, WorldGenLe
return this.players;
}
@@ -287,7 +287,7 @@ index f5bf8ed6e741823078ab93eac47636fcebcdfe2d..14516c3ebd092e6f230804145a60dc85
@Override
public void updatePOIOnBlockStateChange(BlockPos pos, BlockState oldState, BlockState newState) {
Optional<Holder<PoiType>> optional = PoiTypes.forState(oldState);
@@ -2963,6 +2971,11 @@ public class ServerLevel extends Level implements ServerEntityGetter, WorldGenLe
@@ -2991,6 +2999,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);
@@ -299,7 +299,7 @@ index f5bf8ed6e741823078ab93eac47636fcebcdfe2d..14516c3ebd092e6f230804145a60dc85
if (serverPlayer.isReceivingWaypoints()) {
ServerLevel.this.getWaypointManager().addPlayer(serverPlayer);
}
@@ -3044,6 +3057,11 @@ public class ServerLevel extends Level implements ServerEntityGetter, WorldGenLe
@@ -3072,6 +3085,11 @@ public class ServerLevel extends Level implements ServerEntityGetter, WorldGenLe
ServerLevel.this.getChunkSource().removeEntity(entity);
if (entity instanceof ServerPlayer serverPlayer) {
ServerLevel.this.players.remove(serverPlayer);
@@ -312,7 +312,7 @@ index f5bf8ed6e741823078ab93eac47636fcebcdfe2d..14516c3ebd092e6f230804145a60dc85
ServerLevel.this.updateSleepingPlayerList();
}
diff --git a/net/minecraft/server/level/ServerPlayer.java b/net/minecraft/server/level/ServerPlayer.java
index f0d45cb70af62b747c9235941f3fc45221b3b8e6..132645758827ee4d332909e701dde24e69699beb 100644
index 6df8f98afa7810815b8d7c86b3dac57cd5915682..7c2d53ca863c2e50c6871938efc426ddefff9b5c 100644
--- a/net/minecraft/server/level/ServerPlayer.java
+++ b/net/minecraft/server/level/ServerPlayer.java
@@ -227,7 +227,7 @@ public class ServerPlayer extends Player implements ca.spottedleaf.moonrise.patc
@@ -506,7 +506,7 @@ index 0d322ef4aa3e70c9edbb9fa8ba368bef916eb93d..3a5319739f0687fc6f09b2ac8ee31716
public @Nullable ServerPlayer getPlayer(String name) {
diff --git a/net/minecraft/world/entity/Entity.java b/net/minecraft/world/entity/Entity.java
index 32299faedd89a848ca00288672ce85cad707dca0..3e44a61bd982dc822437f647a02a95affd988cdb 100644
index 22e68cabb4554c3e21e6f4ef316186f60ec77511..5ac53fcdca5fa6aae0452840e78d67653ceececb 100644
--- a/net/minecraft/world/entity/Entity.java
+++ b/net/minecraft/world/entity/Entity.java
@@ -1235,7 +1235,7 @@ public abstract class Entity implements SyncedDataHolder, DebugValueSource, Name
@@ -9,7 +9,7 @@ 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 1a749d9bee139975fa249839df93c3a1e48bf102..d81e22fe65cded98c201a7bbae8a51b22c6a20b4 100644
index d35ef52a6adb9a560138d1a2af32969c3a73f42b..df66e24c61af9d57d619c91849e28f1bec64a036 100644
--- a/net/minecraft/server/network/ServerGamePacketListenerImpl.java
+++ b/net/minecraft/server/network/ServerGamePacketListenerImpl.java
@@ -338,9 +338,12 @@ public class ServerGamePacketListenerImpl
@@ -8,10 +8,10 @@ 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 435acf0fe5e80ca371d1145befc2cfaa917a89e3..77577d0df9b14a7ad55b2866c2ac31863226458b 100644
index 5a72b032150209b6ab053934b05e39eae4bceda5..6b281405e05931396a17d7e48c034d9205574cae 100644
--- a/net/minecraft/world/level/chunk/LevelChunk.java
+++ b/net/minecraft/world/level/chunk/LevelChunk.java
@@ -758,6 +758,11 @@ public class LevelChunk extends ChunkAccess implements DebugValueSource, ca.spot
@@ -759,6 +759,11 @@ public class LevelChunk extends ChunkAccess implements DebugValueSource, ca.spot
public void setLoaded(boolean loaded) {
this.loaded = loaded;
@@ -9,7 +9,7 @@ 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/ServerPlayer.java b/net/minecraft/server/level/ServerPlayer.java
index 132645758827ee4d332909e701dde24e69699beb..3efce8ae3cba85019aea7453f97a72939baef7f6 100644
index 7c2d53ca863c2e50c6871938efc426ddefff9b5c..8c1e6837b0328c66fdda322c3c7ceba31c4f017f 100644
--- a/net/minecraft/server/level/ServerPlayer.java
+++ b/net/minecraft/server/level/ServerPlayer.java
@@ -1451,7 +1451,7 @@ public class ServerPlayer extends Player implements ca.spottedleaf.moonrise.patc
@@ -22,10 +22,10 @@ index 132645758827ee4d332909e701dde24e69699beb..3efce8ae3cba85019aea7453f97a7293
// Luminol End - Cross Region Damage trace
}
diff --git a/net/minecraft/server/network/ServerGamePacketListenerImpl.java b/net/minecraft/server/network/ServerGamePacketListenerImpl.java
index d81e22fe65cded98c201a7bbae8a51b22c6a20b4..43e9fd5684d15f8643524132e60266071c6dc454 100644
index df66e24c61af9d57d619c91849e28f1bec64a036..8386757c5fd3d8df3ae77a4eeeb25a2f28840cda 100644
--- a/net/minecraft/server/network/ServerGamePacketListenerImpl.java
+++ b/net/minecraft/server/network/ServerGamePacketListenerImpl.java
@@ -3835,7 +3835,7 @@ public class ServerGamePacketListenerImpl
@@ -3843,7 +3843,7 @@ public class ServerGamePacketListenerImpl
}
@Deprecated @io.papermc.paper.annotation.DoNotUse // Paper - Add PlayerLoadedWorldEvent
@@ -10,10 +10,10 @@ 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 6a51bace3151646afae9de820d9c8977a0221df4..474ca0ce0d3095fc586f8f9e9a584ed4f7c91471 100644
index 3d2bf6371d39946576745aa7977cdc0f2ff07036..ed906ec726fa0eb12c0108dc751af2dcadf2bfb1 100644
--- a/net/minecraft/world/level/block/entity/HopperBlockEntity.java
+++ b/net/minecraft/world/level/block/entity/HopperBlockEntity.java
@@ -229,8 +229,30 @@ public class HopperBlockEntity extends RandomizableContainerBlockEntity implemen
@@ -239,8 +239,30 @@ public class HopperBlockEntity extends RandomizableContainerBlockEntity implemen
flag |= validator.getAsBoolean(); // Paper - note: this is not a validator, it's what adds/sucks in items
}
@@ -44,7 +44,7 @@ index 6a51bace3151646afae9de820d9c8977a0221df4..474ca0ce0d3095fc586f8f9e9a584ed4
setChanged(level, pos, state);
// Leaves start - Lithium Sleeping Block Entity
if (me.earthme.luminol.config.modules.optimizations.LeavesSleepingBlockEntityConfig.enabled
@@ -462,6 +484,13 @@ public class HopperBlockEntity extends RandomizableContainerBlockEntity implemen
@@ -472,6 +494,13 @@ public class HopperBlockEntity extends RandomizableContainerBlockEntity implemen
// Paper end - Perf: Optimize Hoppers
private static boolean ejectItems(Level level, BlockPos pos, HopperBlockEntity blockEntity) {
@@ -58,7 +58,7 @@ index 6a51bace3151646afae9de820d9c8977a0221df4..474ca0ce0d3095fc586f8f9e9a584ed4
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;
@@ -537,6 +566,26 @@ public class HopperBlockEntity extends RandomizableContainerBlockEntity implemen
@@ -547,6 +576,26 @@ public class HopperBlockEntity extends RandomizableContainerBlockEntity implemen
}
}
@@ -85,7 +85,7 @@ index 6a51bace3151646afae9de820d9c8977a0221df4..474ca0ce0d3095fc586f8f9e9a584ed4
private static int[] getSlots(Container container, Direction direction) {
if (container instanceof WorldlyContainer worldlyContainer) {
return worldlyContainer.getSlotsForFace(direction);
@@ -681,6 +730,7 @@ public class HopperBlockEntity extends RandomizableContainerBlockEntity implemen
@@ -691,6 +740,7 @@ public class HopperBlockEntity extends RandomizableContainerBlockEntity implemen
}
public static boolean addItem(Container container, ItemEntity item) {
@@ -13,7 +13,7 @@ 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 43e9fd5684d15f8643524132e60266071c6dc454..62db0ba9c824959f953d452ab7aafcde01b10bcd 100644
index 8386757c5fd3d8df3ae77a4eeeb25a2f28840cda..fd74636d911596daae8cfe563c2dd5e3d37a1549 100644
--- a/net/minecraft/server/network/ServerGamePacketListenerImpl.java
+++ b/net/minecraft/server/network/ServerGamePacketListenerImpl.java
@@ -2134,7 +2134,7 @@ public class ServerGamePacketListenerImpl
@@ -84,7 +84,7 @@ index af71e0e1eb1c93745f3e4954ecc8fbd2ad4808a3..f3829564681faff3ae8971ed90b947b4
players.add(serverPlayer1);
if (players.size() >= resultLimit) {
diff --git a/net/minecraft/server/MinecraftServer.java b/net/minecraft/server/MinecraftServer.java
index dfcf8eb7dc422a55d94d6eb9eb1451c7c14a1d56..9130f1fbf15500f2149418b73ae2a374bba22eee 100644
index 385c71d70ed9e5a3ae25fbecaefd6773c467d31d..24f83a6875219aaf4b29e2461afcd0478a391014 100644
--- a/net/minecraft/server/MinecraftServer.java
+++ b/net/minecraft/server/MinecraftServer.java
@@ -1896,7 +1896,7 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
@@ -96,7 +96,7 @@ index dfcf8eb7dc422a55d94d6eb9eb1451c7c14a1d56..9130f1fbf15500f2149418b73ae2a374
int maxPlayers = this.getMaxPlayers();
if (this.hidesOnlinePlayers()) {
return new ServerStatus.Players(maxPlayers, players.size(), List.of());
@@ -2105,7 +2105,7 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
@@ -2111,7 +2111,7 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
@Override
public int getPlayerCount() {
@@ -105,7 +105,7 @@ index dfcf8eb7dc422a55d94d6eb9eb1451c7c14a1d56..9130f1fbf15500f2149418b73ae2a374
}
public String[] getPlayerNames() {
@@ -2309,7 +2309,7 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
@@ -2315,7 +2315,7 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
} else {
int i = 0;
@@ -114,7 +114,7 @@ index dfcf8eb7dc422a55d94d6eb9eb1451c7c14a1d56..9130f1fbf15500f2149418b73ae2a374
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, null); // Folia - region threading
@@ -2494,7 +2494,7 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
@@ -2500,7 +2500,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();
@@ -123,7 +123,7 @@ index dfcf8eb7dc422a55d94d6eb9eb1451c7c14a1d56..9130f1fbf15500f2149418b73ae2a374
player.getAdvancements().save();
}
// Paper end - we don't need to save everything, just advancements
@@ -2632,7 +2632,7 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
@@ -2638,7 +2638,7 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
PlayerList playerList = this.getPlayerList();
UserWhiteList whiteList = playerList.getWhiteList();
@@ -284,10 +284,10 @@ index 5a8cd3e6b448a4472092690cf589bca10b142126..158593b1a6384db635a1f224919b24c1
this.setListData(list);
diff --git a/net/minecraft/server/level/ServerLevel.java b/net/minecraft/server/level/ServerLevel.java
index 14516c3ebd092e6f230804145a60dc851f34230b..bd33a381490ec3f03af9d22c9337f5d686014d85 100644
index 88efbe2bcb5621432b67366116150a3dab52e510..9345bdb2770e7c27488c547012020337fc651edc 100644
--- a/net/minecraft/server/level/ServerLevel.java
+++ b/net/minecraft/server/level/ServerLevel.java
@@ -2972,7 +2972,7 @@ public class ServerLevel extends Level implements ServerEntityGetter, WorldGenLe
@@ -3000,7 +3000,7 @@ public class ServerLevel extends Level implements ServerEntityGetter, WorldGenLe
if (entity instanceof ServerPlayer serverPlayer) {
ServerLevel.this.players.add(serverPlayer);
// Leaves start - skip
@@ -296,7 +296,7 @@ index 14516c3ebd092e6f230804145a60dc851f34230b..bd33a381490ec3f03af9d22c9337f5d6
ServerLevel.this.realPlayers.add(serverPlayer);
}
// Leaves end - skip
@@ -3058,7 +3058,7 @@ public class ServerLevel extends Level implements ServerEntityGetter, WorldGenLe
@@ -3086,7 +3086,7 @@ public class ServerLevel extends Level implements ServerEntityGetter, WorldGenLe
if (entity instanceof ServerPlayer serverPlayer) {
ServerLevel.this.players.remove(serverPlayer);
// Leaves start - skip
@@ -306,7 +306,7 @@ index 14516c3ebd092e6f230804145a60dc851f34230b..bd33a381490ec3f03af9d22c9337f5d6
}
// Leaves end - skip
diff --git a/net/minecraft/server/level/ServerPlayer.java b/net/minecraft/server/level/ServerPlayer.java
index 3efce8ae3cba85019aea7453f97a72939baef7f6..0741dc695c0b22a6edd9a51a9dcdec0cdd545326 100644
index 8c1e6837b0328c66fdda322c3c7ceba31c4f017f..ca751564d9189e873d4a53be9a2c68b2a04a8624 100644
--- a/net/minecraft/server/level/ServerPlayer.java
+++ b/net/minecraft/server/level/ServerPlayer.java
@@ -202,7 +202,7 @@ import org.jspecify.annotations.Nullable;
@@ -46,10 +46,10 @@ index ab80a66646a7ebe2862221ac342b06c14fcfc734..7834f3da4d0ff4a5cdec46b023d73483
fillCrashReport(reportedException.getReport(), packetListener, packet);
return reportedException;
diff --git a/net/minecraft/server/MinecraftServer.java b/net/minecraft/server/MinecraftServer.java
index 9130f1fbf15500f2149418b73ae2a374bba22eee..eef40103bcec3b836ffb50154fb05926b9757723 100644
index 24f83a6875219aaf4b29e2461afcd0478a391014..9457d86461b032ff2dac338620c3d4a54e6c7071 100644
--- a/net/minecraft/server/MinecraftServer.java
+++ b/net/minecraft/server/MinecraftServer.java
@@ -1975,11 +1975,27 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
@@ -1978,11 +1978,27 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop<TickTa
try {
profiler.startTimer(serverLevel.tickTimerId); try { // Folia - profiler
serverLevel.tick(hasTimeLeft, region); // Folia - region threading
@@ -81,7 +81,7 @@ index 9130f1fbf15500f2149418b73ae2a374bba22eee..eef40103bcec3b836ffb50154fb05926
profilerFiller.pop();
diff --git a/net/minecraft/server/level/ServerLevel.java b/net/minecraft/server/level/ServerLevel.java
index bd33a381490ec3f03af9d22c9337f5d686014d85..284bd9703b11804f51273ecfaa5cd44925726f5f 100644
index 9345bdb2770e7c27488c547012020337fc651edc..447b3e6149716a3ca4b9721746ea471c94c56e67 100644
--- a/net/minecraft/server/level/ServerLevel.java
+++ b/net/minecraft/server/level/ServerLevel.java
@@ -894,7 +894,20 @@ public class ServerLevel extends Level implements ServerEntityGetter, WorldGenLe
@@ -107,7 +107,7 @@ index bd33a381490ec3f03af9d22c9337f5d686014d85..284bd9703b11804f51273ecfaa5cd449
}
}
diff --git a/net/minecraft/server/level/ServerPlayer.java b/net/minecraft/server/level/ServerPlayer.java
index 0741dc695c0b22a6edd9a51a9dcdec0cdd545326..3fc09529159d202ce6201626be535d791818fa4a 100644
index ca751564d9189e873d4a53be9a2c68b2a04a8624..e6ec0788b88d3431c06134c879b4bae9c769dac1 100644
--- a/net/minecraft/server/level/ServerPlayer.java
+++ b/net/minecraft/server/level/ServerPlayer.java
@@ -1108,6 +1108,12 @@ public class ServerPlayer extends Player implements ca.spottedleaf.moonrise.patc
@@ -124,7 +124,7 @@ index 0741dc695c0b22a6edd9a51a9dcdec0cdd545326..3fc09529159d202ce6201626be535d79
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 3e44a61bd982dc822437f647a02a95affd988cdb..28ae21b08e42edcc031ace2404b9d213589c28e8 100644
index 5ac53fcdca5fa6aae0452840e78d67653ceececb..26de0d418e0dcbb4ecd88630d75c677fdc7ff93d 100644
--- a/net/minecraft/world/entity/Entity.java
+++ b/net/minecraft/world/entity/Entity.java
@@ -1427,9 +1427,19 @@ public abstract class Entity implements SyncedDataHolder, DebugValueSource, Name
@@ -5,10 +5,10 @@ Subject: [PATCH] Instant Block Updater
diff --git a/io/papermc/paper/threadedregions/RegionizedWorldData.java b/io/papermc/paper/threadedregions/RegionizedWorldData.java
index c7f578c117baccb10c0c15b05a6c04e073f15da8..aee3f98173de1b2c5a5278919a5d21b4bca03b7b 100644
index 111fd492351601a22cd29391bc4e3602709adb54..166089c786210d645c22f96569f266cc50fe7d6c 100644
--- a/io/papermc/paper/threadedregions/RegionizedWorldData.java
+++ b/io/papermc/paper/threadedregions/RegionizedWorldData.java
@@ -472,7 +472,7 @@ public final class RegionizedWorldData {
@@ -484,7 +484,7 @@ public final class RegionizedWorldData {
public long lastMidTickExecuteFailure;
// From Level
public boolean populating;
@@ -17,7 +17,7 @@ index c7f578c117baccb10c0c15b05a6c04e073f15da8..aee3f98173de1b2c5a5278919a5d21b4
public boolean captureBlockStates = false;
public boolean captureTreeGeneration = false;
public final Map<BlockPos, CraftBlockState> capturedBlockStates = new java.util.LinkedHashMap<>(); // Paper
@@ -531,7 +531,7 @@ public final class RegionizedWorldData {
@@ -572,7 +572,7 @@ public final class RegionizedWorldData {
this.world = world;
this.blockLevelTicks = new LevelTicks<>(world::isPositionTickingWithEntitiesLoaded, world, true);
this.fluidLevelTicks = new LevelTicks<>(world::isPositionTickingWithEntitiesLoaded, world, false);
@@ -27,7 +27,7 @@ index c7f578c117baccb10c0c15b05a6c04e073f15da8..aee3f98173de1b2c5a5278919a5d21b4
this.wireHandler = new alternate.current.wire.WireHandler(world);
this.turbo = new io.papermc.paper.redstone.RedstoneWireTurbo((RedStoneWireBlock)Blocks.REDSTONE_WIRE);
diff --git a/net/minecraft/server/level/ServerLevel.java b/net/minecraft/server/level/ServerLevel.java
index 284bd9703b11804f51273ecfaa5cd44925726f5f..9756caff0b7403c212987e58c08a2da09bdeb962 100644
index 447b3e6149716a3ca4b9721746ea471c94c56e67..d8a2f8a1fbf0189e86ccbce70a84641a143cdb91 100644
--- a/net/minecraft/server/level/ServerLevel.java
+++ b/net/minecraft/server/level/ServerLevel.java
@@ -927,12 +927,16 @@ public class ServerLevel extends Level implements ServerEntityGetter, WorldGenLe
@@ -46,10 +46,10 @@ index a5d3caab2072b2c9bf8fdcbdb7c52c2dee16cfaf..4bb36d2965785fadf628a9bfac4fa2aa
// CraftBukkit start
if (bukkitState != null) {
diff --git a/net/minecraft/world/level/chunk/LevelChunk.java b/net/minecraft/world/level/chunk/LevelChunk.java
index 73dd8e67938fbfff745b189c06a93174976ba1bf..8e56c2237f9d49d0b09b62c0933283f77aaae679 100644
index 318725a0c9b2bdece7e6287cd521844af81aaf69..b1829ac08ba90e8e1522ee790b7f96515da2b9c4 100644
--- a/net/minecraft/world/level/chunk/LevelChunk.java
+++ b/net/minecraft/world/level/chunk/LevelChunk.java
@@ -1001,12 +1001,14 @@ public class LevelChunk extends ChunkAccess implements DebugValueSource, ca.spot
@@ -1005,12 +1005,14 @@ public class LevelChunk extends ChunkAccess implements DebugValueSource, ca.spot
profilerFiller.pop();
} catch (Throwable var5) {
@@ -5,11 +5,11 @@ Subject: [PATCH] Global Entities Counter
diff --git a/io/papermc/paper/threadedregions/RegionizedWorldData.java b/io/papermc/paper/threadedregions/RegionizedWorldData.java
index aee3f98173de1b2c5a5278919a5d21b4bca03b7b..24a8ea9a709ed9637a8e3814c6f8fe9f9f9dedff 100644
index 166089c786210d645c22f96569f266cc50fe7d6c..c7de740dd9babfc1373a5663cd552214b75a0674 100644
--- a/io/papermc/paper/threadedregions/RegionizedWorldData.java
+++ b/io/papermc/paper/threadedregions/RegionizedWorldData.java
@@ -525,6 +525,9 @@ public final class RegionizedWorldData {
public final Map<Long, ChunkSectionItemEntityMovementTracker> itemEntityMovementTrackerMap = new it.unimi.dsi.fastutil.objects.Object2ObjectLinkedOpenHashMap<>();
@@ -566,6 +566,9 @@ public final class RegionizedWorldData {
}
// Luminol end
+ public int uniqueId = -1; // Lophine - Global Entities Counter
@@ -18,7 +18,7 @@ index aee3f98173de1b2c5a5278919a5d21b4bca03b7b..24a8ea9a709ed9637a8e3814c6f8fe9f
public final TickRegions.TickRegionData regionData;
public RegionizedWorldData(final ServerLevel world, final TickRegions.TickRegionData regionData) {
@@ -537,6 +540,15 @@ public final class RegionizedWorldData {
@@ -578,6 +581,15 @@ public final class RegionizedWorldData {
this.turbo = new io.papermc.paper.redstone.RedstoneWireTurbo((RedStoneWireBlock)Blocks.REDSTONE_WIRE);
this.regionData = regionData;
@@ -97,7 +97,7 @@ index f4c9c551fc39bae38e283ccbbc3cd116f0536a64..db13211b24208d8addd3c210155bc244
NaturalSpawner.spawnForChunk(this.level, chunk, spawnState, spawnCategories);
}
diff --git a/net/minecraft/server/level/ServerLevel.java b/net/minecraft/server/level/ServerLevel.java
index 9756caff0b7403c212987e58c08a2da09bdeb962..4acd4718628b160c482b04da6ba905687b61bbad 100644
index d8a2f8a1fbf0189e86ccbce70a84641a143cdb91..76930a603a5bf07e62e8ef3a41bbb5213dcc8aea 100644
--- a/net/minecraft/server/level/ServerLevel.java
+++ b/net/minecraft/server/level/ServerLevel.java
@@ -765,6 +765,11 @@ public class ServerLevel extends Level implements ServerEntityGetter, WorldGenLe
@@ -112,6 +112,55 @@ index 9756caff0b7403c212987e58c08a2da09bdeb962..4acd4718628b160c482b04da6ba90568
final io.papermc.paper.threadedregions.RegionizedWorldData regionizedWorldData = this.getCurrentWorldData(); // Folia - regionised ticking
final ca.spottedleaf.leafprofiler.RegionizedProfiler.Handle profiler = io.papermc.paper.threadedregions.TickRegionScheduler.getProfiler(); // Folia - profiler
ProfilerFiller profilerFiller = Profiler.get();
diff --git a/net/minecraft/world/entity/Mob.java b/net/minecraft/world/entity/Mob.java
index c6985d2a9d1093943d754d77089bd4c3d5baf3ee..a8807f80f1b11d7f5dae3b96a0e215e3e105fdd0 100644
--- a/net/minecraft/world/entity/Mob.java
+++ b/net/minecraft/world/entity/Mob.java
@@ -806,6 +806,44 @@ public abstract class Mob extends LivingEntity implements EquipmentUser, Leashab
}
}
} else {
+ // Lophine start - fix some bug if player is cross scheduler >> to remove monster if the region don't have any valid player
+ if (fun.bm.lophine.config.modules.experiment.GlobalEntitiesCounter.enabled) {
+ List<net.minecraft.server.level.ServerPlayer> playerList = this.level().getServer().getPlayerList().getPlayers();
+ if (!playerList.isEmpty()) {
+ double d = -1.0;
+ Vec3 vec3 = null;
+ for (net.minecraft.server.level.ServerPlayer player1 : playerList) {
+ if (player1.level() != this.level()) continue;
+ double d1 = player1.distanceToSqr(this.position());
+ if (d == -1.0 || d1 < d) {
+ d = d1;
+ vec3 = player1.position();
+ }
+ }
+ if (vec3 != null) {
+ // copy from PaperMC
+ // Paper start - Configurable despawn distances
+ final io.papermc.paper.configuration.WorldConfiguration.Entities.Spawning.DespawnRangePair despawnRangePair = this.level().paperConfig().entities.spawning.despawnRanges.get(this.getType().getCategory());
+ final io.papermc.paper.configuration.type.DespawnRange.Shape shape = this.level().paperConfig().entities.spawning.despawnRangeShape;
+ final double dy = Math.abs(vec3.y - this.getY());
+ final double dySqr = Mth.square(dy);
+ final double dxSqr = Mth.square(vec3.x - this.getX());
+ final double dzSqr = Mth.square(vec3.z - this.getZ());
+ final double distanceSquared = dxSqr + dzSqr + dySqr;
+ // Despawn if hard/soft limit is exceeded
+ if (despawnRangePair.hard().shouldDespawn(shape, dxSqr, dySqr, dzSqr, dy) && this.removeWhenFarAway(distanceSquared)) {
+ this.discard(EntityRemoveEvent.Cause.DESPAWN); // CraftBukkit - add Bukkit remove cause
+ }
+
+ if (despawnRangePair.soft().shouldDespawn(shape, dxSqr, dySqr, dzSqr, dy)) {
+ if (this.noActionTime > 600 && this.random.nextInt(800) == 0 && this.removeWhenFarAway(distanceSquared)) {
+ this.discard(EntityRemoveEvent.Cause.DESPAWN); // CraftBukkit - add Bukkit remove cause
+ }
+ }
+ }
+ }
+ }
+ // Lophine end
this.noActionTime = 0;
}
}
diff --git a/net/minecraft/world/level/NaturalSpawner.java b/net/minecraft/world/level/NaturalSpawner.java
index 91f0fbd75bc93355dc872390d91af83c76dc1dce..e7223540a7cef20b661bdb857ffba962d0f5d348 100644
--- a/net/minecraft/world/level/NaturalSpawner.java
@@ -6,10 +6,10 @@ Subject: [PATCH] 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.
diff --git a/io/papermc/paper/threadedregions/RegionizedWorldData.java b/io/papermc/paper/threadedregions/RegionizedWorldData.java
index 24a8ea9a709ed9637a8e3814c6f8fe9f9f9dedff..35f8bcb5569987e4096ea06bab4e6693dfb97695 100644
index c7de740dd9babfc1373a5663cd552214b75a0674..61deaf0184c984a71d104a495b4dee34b27766f1 100644
--- a/io/papermc/paper/threadedregions/RegionizedWorldData.java
+++ b/io/papermc/paper/threadedregions/RegionizedWorldData.java
@@ -229,14 +229,17 @@ public final class RegionizedWorldData {
@@ -241,14 +241,17 @@ public final class RegionizedWorldData {
regionToData.get(CoordinateUtils.getChunkKey(pos.x >> chunkToRegionShift, pos.z >> chunkToRegionShift)).itemEntityMovementTrackerMap.put(key, tracker);
}
// Luminol end
@@ -27,7 +27,7 @@ index 24a8ea9a709ed9637a8e3814c6f8fe9f9f9dedff..35f8bcb5569987e4096ea06bab4e6693
}
// entities
for (final ServerPlayer player : from.localPlayers) {
@@ -247,6 +250,13 @@ public final class RegionizedWorldData {
@@ -259,6 +262,13 @@ public final class RegionizedWorldData {
into.localPlayers.add(player);
player.getBukkitEntity().updateRegion(into);
into.nearbyPlayers.addPlayer(player);
@@ -41,7 +41,7 @@ index 24a8ea9a709ed9637a8e3814c6f8fe9f9f9dedff..35f8bcb5569987e4096ea06bab4e6693
}
for (final Entity entity : from.allEntities) {
final ChunkPos pos = entity.chunkPosition();
@@ -585,6 +595,7 @@ public final class RegionizedWorldData {
@@ -626,6 +636,7 @@ public final class RegionizedWorldData {
private static void cleanUpConnection(final Connection conn) {
// note: ALL connections HERE have a player
final ServerPlayer player = conn.getPlayer();
@@ -0,0 +1,148 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Helvetica Volubi <suisuroru@blue-millennium.fun>
Date: Sun, 8 Mar 2026 23:56:06 +0800
Subject: [PATCH] Leaves: Creative fly no clip
Co-authored by: violetc <58360096+s-yh-china@users.noreply.github.com>
As a part of : Leaves (https://github.com/LeavesMC/Leaves/blob/da12a58e8d3bd3cd13b52a60676d5a1e03acd30a/leaves-server/minecraft-patches/features/0049-Creative-fly-no-clip.patch)
Licensed under: GPL-3.0 (https://www.gnu.org/licenses/gpl-3.0.html)
diff --git a/net/minecraft/world/entity/ExperienceOrb.java b/net/minecraft/world/entity/ExperienceOrb.java
index f2ec0bd8cca3c08d558790537e17a8a2f95f1953..29d904b02a2c7b3d25261ca26c7c0479b00999ef 100644
--- a/net/minecraft/world/entity/ExperienceOrb.java
+++ b/net/minecraft/world/entity/ExperienceOrb.java
@@ -199,7 +199,7 @@ public class ExperienceOrb extends Entity {
Player prevTarget = this.followingPlayer; // CraftBukkit - store old target
if (this.followingPlayer == null || this.followingPlayer.isSpectator() || this.followingPlayer.distanceToSqr(this) > 64.0) {
Player nearestPlayer = this.level().getNearestPlayer(this, 8.0);
- if (nearestPlayer != null && !nearestPlayer.isSpectator() && !nearestPlayer.isDeadOrDying()) {
+ if (nearestPlayer != null && !nearestPlayer.isCreativeFlyOrSpectator() && !nearestPlayer.isDeadOrDying()) { // Leaves - creative no clip
this.followingPlayer = nearestPlayer;
} else {
this.followingPlayer = null;
diff --git a/net/minecraft/world/entity/player/Player.java b/net/minecraft/world/entity/player/Player.java
index 29ca31b05ec5636243b13fee8b3399d5e21808b4..8966953ee80a1527c9c4cc4d0a7a3bdb2197a2da 100644
--- a/net/minecraft/world/entity/player/Player.java
+++ b/net/minecraft/world/entity/player/Player.java
@@ -285,8 +285,8 @@ public abstract class Player extends Avatar implements ContainerUser {
if (this.cullTask != null) this.cullTask.requestCullSignal(); // Luminol - Ray tracing entity tracker
// Luminol end
- this.noPhysics = this.isSpectator();
- if (this.isSpectator() || this.isPassenger()) {
+ this.noPhysics = this.isCreativeFlyOrSpectator(); // Leaves - creative no clip
+ if (this.isCreativeFlyOrSpectator() || this.isPassenger()) { // Leaves - creative no clip
this.setOnGround(false);
}
@@ -415,7 +415,7 @@ public abstract class Player extends Avatar implements ContainerUser {
if (this.canPlayerFitWithinBlocksAndEntitiesWhen(Pose.SWIMMING)) {
Pose desiredPose = this.getDesiredPose();
Pose pose;
- if (this.isSpectator() || this.isPassenger() || this.canPlayerFitWithinBlocksAndEntitiesWhen(desiredPose)) {
+ if (this.isCreativeFlyOrSpectator() || this.isPassenger() || this.canPlayerFitWithinBlocksAndEntitiesWhen(desiredPose)) { // Leaves - creative no clip
pose = desiredPose;
} else if (this.canPlayerFitWithinBlocksAndEntitiesWhen(Pose.CROUCHING)) {
pose = Pose.CROUCHING;
@@ -544,7 +544,7 @@ public abstract class Player extends Avatar implements ContainerUser {
this.updateSwingTime();
this.yHeadRot = this.getYRot();
this.setSpeed((float)this.getAttributeValue(Attributes.MOVEMENT_SPEED));
- if (this.getHealth() > 0.0F && !this.isSpectator()) {
+ if (this.getHealth() > 0.0F && !this.isCreativeFlyOrSpectator()) { // Leaves - creative no clip
AABB aabb;
if (this.isPassenger() && !this.getVehicle().isRemoved()) {
aabb = this.getBoundingBox().minmax(this.getVehicle().getBoundingBox()).inflate(1.0, 0.0, 1.0);
@@ -1877,9 +1877,29 @@ public abstract class Player extends Avatar implements ContainerUser {
return this.gameMode() == GameType.SPECTATOR;
}
+ // Leaves start - creative no clip
+ public boolean isCreativeFlyOrSpectator() {
+ return isSpectator() || (fun.bm.lophine.config.modules.function.CreativeFlyNoClipConfig.enabled && isCreative() && getAbilities().flying);
+ }
+
+ public boolean canSpectatingPlace(net.minecraft.world.level.LevelReader world, BlockState state, BlockPos pos, net.minecraft.world.phys.shapes.CollisionContext context) {
+ if (this.isCreativeFlyOrSpectator()) {
+ net.minecraft.world.phys.shapes.VoxelShape voxelShape = state.getCollisionShape(world, pos, context);
+ return voxelShape.isEmpty() || world.isUnobstructed(this, voxelShape.move(pos.getX(), pos.getY(), pos.getZ()));
+ } else {
+ return world.isUnobstructed(state, pos, context);
+ }
+ }
+
+ @Override
+ public boolean isCollidable(boolean ignoreClimbing) {
+ return !isCreativeFlyOrSpectator() && super.isCollidable(ignoreClimbing);
+ }
+ // Leaves end - creative no clip
+
@Override
public boolean canBeHitByProjectile() {
- return !this.isSpectator() && super.canBeHitByProjectile();
+ return !this.isCreativeFlyOrSpectator() && super.canBeHitByProjectile(); // Leaves - creative no clip
}
@Override
diff --git a/net/minecraft/world/item/BlockItem.java b/net/minecraft/world/item/BlockItem.java
index 4bb36d2965785fadf628a9bfac4fa2aa305aaf65..44786e4f77285f1fbd4643f560105e410035afeb 100644
--- a/net/minecraft/world/item/BlockItem.java
+++ b/net/minecraft/world/item/BlockItem.java
@@ -170,8 +170,9 @@ public class BlockItem extends Item {
protected boolean canPlace(BlockPlaceContext context, BlockState state) {
Player player = context.getPlayer();
// CraftBukkit start
+ CollisionContext collisionContext = player == null ? CollisionContext.empty() : CollisionContext.placementContext(player);
Level world = context.getLevel(); // Paper - Cancel hit for vanished players
- boolean canBuild = (!this.mustSurvive() || state.canSurvive(world, context.getClickedPos())) && world.checkEntityCollision(state, player, CollisionContext.placementContext(player), context.getClickedPos(), true); // Paper - Cancel hit for vanished players
+ boolean canBuild = (!this.mustSurvive() || state.canSurvive(world, context.getClickedPos())) && ((fun.bm.lophine.config.modules.function.CreativeFlyNoClipConfig.enabled && context.getPlayer() != null) ? context.getPlayer().canSpectatingPlace(world, state, context.getClickedPos(), collisionContext) : world.checkEntityCollision(state, player, collisionContext, context.getClickedPos(), true)); // Paper - Cancel hit for vanished players // Leaves - creative no clip
org.bukkit.entity.Player bukkitPlayer = (context.getPlayer() instanceof ServerPlayer) ? (org.bukkit.entity.Player) context.getPlayer().getBukkitEntity() : null;
org.bukkit.event.block.BlockCanBuildEvent event = new org.bukkit.event.block.BlockCanBuildEvent(
diff --git a/net/minecraft/world/item/StandingAndWallBlockItem.java b/net/minecraft/world/item/StandingAndWallBlockItem.java
index be685383b8e7c8111e976fc560f16d43d6545a34..6a2219b45eae0ff6fc48de9a6e6cabf2ec60cb07 100644
--- a/net/minecraft/world/item/StandingAndWallBlockItem.java
+++ b/net/minecraft/world/item/StandingAndWallBlockItem.java
@@ -44,7 +44,7 @@ public class StandingAndWallBlockItem extends BlockItem {
// return blockState != null && level.isUnobstructed(blockState, clickedPos, CollisionContext.empty()) ? blockState : null;
// CraftBukkit start
if (blockState != null) {
- boolean defaultReturn = level.isUnobstructed(blockState, clickedPos, CollisionContext.empty());
+ boolean defaultReturn = (fun.bm.lophine.config.modules.function.CreativeFlyNoClipConfig.enabled && context.getPlayer() != null) ? context.getPlayer().canSpectatingPlace(level, blockState, clickedPos, CollisionContext.empty()) : level.isUnobstructed(blockState, clickedPos, CollisionContext.empty()); // Leaves - creative no clip
org.bukkit.entity.Player player = (context.getPlayer() instanceof net.minecraft.server.level.ServerPlayer serverPlayer) ? serverPlayer.getBukkitEntity() : null;
org.bukkit.event.block.BlockCanBuildEvent event = new org.bukkit.event.block.BlockCanBuildEvent(org.bukkit.craftbukkit.block.CraftBlock.at(context.getLevel(), clickedPos), player, org.bukkit.craftbukkit.block.data.CraftBlockData.fromData(blockState), defaultReturn, org.bukkit.craftbukkit.CraftEquipmentSlot.getHand(context.getHand())); // Paper - Expose hand in BlockCanBuildEvent
diff --git a/net/minecraft/world/level/block/entity/ShulkerBoxBlockEntity.java b/net/minecraft/world/level/block/entity/ShulkerBoxBlockEntity.java
index 17110035f4b0f144c29b8ff8bf7d0d39efc8103a..1b7f0bfa66e632678164ac64e03855e75bab1305 100644
--- a/net/minecraft/world/level/block/entity/ShulkerBoxBlockEntity.java
+++ b/net/minecraft/world/level/block/entity/ShulkerBoxBlockEntity.java
@@ -153,7 +153,7 @@ public class ShulkerBoxBlockEntity extends RandomizableContainerBlockEntity impl
List<Entity> entities = level.getEntities(null, progressDeltaAabb);
if (!entities.isEmpty()) {
for (Entity entity : entities) {
- if (entity.getPistonPushReaction() != PushReaction.IGNORE) {
+ if (entity.getPistonPushReaction() != PushReaction.IGNORE && !(entity instanceof net.minecraft.world.entity.player.Player player && player.isCreativeFlyOrSpectator())) { // Leaves - creative no clip
entity.move(
MoverType.SHULKER_BOX,
new Vec3(
diff --git a/net/minecraft/world/level/block/piston/PistonMovingBlockEntity.java b/net/minecraft/world/level/block/piston/PistonMovingBlockEntity.java
index 1d60054146f970ec24054d2c76a27e2d8620ec67..a5469971734a3f4f08d676426821fbf059c526dc 100644
--- a/net/minecraft/world/level/block/piston/PistonMovingBlockEntity.java
+++ b/net/minecraft/world/level/block/piston/PistonMovingBlockEntity.java
@@ -161,7 +161,7 @@ public class PistonMovingBlockEntity extends BlockEntity {
d3 = movementDirection.getStepZ();
}
- entity.setDeltaMovement(d1, d2, d3);
+ if (!(entity instanceof net.minecraft.world.entity.player.Player player) || !player.isCreativeFlyOrSpectator()) entity.setDeltaMovement(d1, d2, d3); // Leaves - creative no clip
// Paper - EAR items stuck in slime pushed by a piston
entity.activatedTick = Math.max(entity.activatedTick, io.papermc.paper.threadedregions.RegionizedServer.getCurrentTick() + 10); // Folia - region threading
entity.activatedImmunityTick = Math.max(entity.activatedImmunityTick, io.papermc.paper.threadedregions.RegionizedServer.getCurrentTick() + 10); // Folia - region threading
@@ -197,6 +197,7 @@ public class PistonMovingBlockEntity extends BlockEntity {
}
private static void moveEntityByPiston(Direction noClipDirection, Entity entity, double progress, Direction direction) {
+ if (entity instanceof net.minecraft.world.entity.player.Player player && player.isCreativeFlyOrSpectator()) return; // Leaves - creative no clip
NOCLIP.set(noClipDirection);
Vec3 vec3 = entity.position();
entity.move(MoverType.PISTON, new Vec3(progress * direction.getStepX(), progress * direction.getStepY(), progress * direction.getStepZ()));
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,42 @@
package fun.bm.lophine.carpet;
import fun.bm.lophine.carpet.config.modules.GeneralCompatConfig;
import net.minecraft.ChatFormatting;
import net.minecraft.network.chat.Component;
import net.minecraft.server.level.ServerPlayer;
import net.objecthunter.exp4j.ExpressionBuilder;
import java.math.BigDecimal;
public final class CarpetCalculatorCompatHelper {
public static boolean handleChat(ServerPlayer player, String rawMessage) {
if (!GeneralCompatConfig.simpleInGameCalculator || !rawMessage.startsWith("=")) {
return false;
}
String expression = rawMessage.substring(1).trim();
if (expression.isEmpty()) {
return false;
}
Component response;
try {
double result = new ExpressionBuilder(expression).build().evaluate();
response = Component.literal(expression + " = " + formatNumber(result)).withStyle(ChatFormatting.YELLOW);
} catch (RuntimeException exception) {
response = Component.literal("Calculator error: " + exception.getMessage()).withStyle(ChatFormatting.RED);
}
Component scheduledResponse = response;
player.getBukkitEntity().taskScheduler.schedule((ServerPlayer scheduledPlayer) -> scheduledPlayer.sendSystemMessage(scheduledResponse), null, 1L);
return true;
}
private static String formatNumber(double value) {
if (Double.isFinite(value) && Math.rint(value) == value && value >= Long.MIN_VALUE && value <= Long.MAX_VALUE) {
return Long.toString((long) value);
}
return BigDecimal.valueOf(value).stripTrailingZeros().toPlainString();
}
}
@@ -0,0 +1,138 @@
package fun.bm.lophine.carpet;
import fun.bm.lophine.carpet.config.modules.CoreConfig;
import fun.bm.lophine.carpet.config.modules.CounterCompatConfig;
import fun.bm.lophine.carpet.config.modules.FakePlayerCompatConfig;
import fun.bm.lophine.carpet.config.modules.GeneralCompatConfig;
import fun.bm.lophine.config.modules.experiment.CommandConfig;
import fun.bm.lophine.config.modules.experiment.RedStoneConfig;
import fun.bm.lophine.config.modules.fixes.UpdateSuppressionCrashFixConfig;
import fun.bm.lophine.config.modules.function.CreativeFlyNoClipConfig;
import fun.bm.lophine.config.modules.function.FakeplayerConfig;
import fun.bm.lophine.config.modules.function.LanguageConfig;
import fun.bm.lophine.config.modules.function.WoolHopperCounterConfig;
import fun.bm.lophine.protocol.CarpetLoggerProtocol;
import me.earthme.luminol.config.modules.optimizations.OptimizedDragonRespawnConfig;
import org.leavesmc.leaves.bot.ServerBot;
import org.leavesmc.leaves.protocol.CarpetServerProtocol;
import java.util.List;
// WARNING: THIS FILE NEED TO FULLY REWRITTEN
public final class CarpetCompatSync {
private static boolean init = false;
public static void apply() {
if (init) return;
if (CoreConfig.enabled) {
applyGeneralRules();
applyFakePlayerRules();
applyCounterRules();
}
CarpetLoggerProtocol.refreshConfiguredDefaults(!init);
registerProtocolRules();
init = true;
}
private static void applyGeneralRules() {
LanguageConfig.lang = GeneralCompatConfig.language;
UpdateSuppressionCrashFixConfig.enabled = GeneralCompatConfig.amsUpdateSuppressionCrashFix || GeneralCompatConfig.yeetUpdateSuppressionCrash;
RedStoneConfig.redstoneIgnoreUpwardsUpdate = GeneralCompatConfig.dustTrapdoorReintroduced;
RedStoneConfig.cce = GeneralCompatConfig.shulkerBoxCCEReintroduced;
RedStoneConfig.instantBlockUpdater = GeneralCompatConfig.instantBlockUpdaterReintroduced;
CommandConfig.tick = GeneralCompatConfig.commandTick;
CreativeFlyNoClipConfig.enabled = GeneralCompatConfig.creativeNoClip;
OptimizedDragonRespawnConfig.optimizedRespawn = GeneralCompatConfig.optimizedDragonRespawn;
}
private static void applyFakePlayerRules() {
FakeplayerConfig.enable = FakePlayerCompatConfig.commandBot || FakePlayerCompatConfig.commandPlayer;
FakeplayerConfig.canResident = FakePlayerCompatConfig.fakePlayerResident;
FakeplayerConfig.canOpenInventory = FakePlayerCompatConfig.openFakePlayerInventory;
FakeplayerConfig.tickType = FakePlayerCompatConfig.fakePlayerTicksLikeRealPlayer
? ServerBot.TickType.NETWORK
: ServerBot.TickType.ENTITY_LIST;
}
private static void applyCounterRules() {
WoolHopperCounterConfig.enabled = CounterCompatConfig.hopperCounters;
WoolHopperCounterConfig.unlimitedSpeed = CounterCompatConfig.hopperCountersUnlimitedSpeed;
}
private static List<String> sanitizeDefaultLoggers(List<String> configuredLoggers) {
return configuredLoggers == null ? List.of() : List.copyOf(configuredLoggers);
}
private static void registerProtocolRules() {
CarpetServerProtocol.CarpetRules.clear();
CarpetServerProtocol.CarpetRules.register(CarpetServerProtocol.CarpetRule.of("carpet", "language", GeneralCompatConfig.language));
CarpetServerProtocol.CarpetRules.register(CarpetServerProtocol.CarpetRule.of("carpetamsaddition", "amsUpdateSuppressionCrashFix", GeneralCompatConfig.amsUpdateSuppressionCrashFix));
CarpetServerProtocol.CarpetRules.register(CarpetServerProtocol.CarpetRule.of("carpettisaddition", "yeetUpdateSuppressionCrash", GeneralCompatConfig.yeetUpdateSuppressionCrash));
CarpetServerProtocol.CarpetRules.register(CarpetServerProtocol.CarpetRule.of("carpettisaddition", "dustTrapdoorReintroduced", GeneralCompatConfig.dustTrapdoorReintroduced));
CarpetServerProtocol.CarpetRules.register(CarpetServerProtocol.CarpetRule.of("carpettisaddition", "shulkerBoxCCEReintroduced", GeneralCompatConfig.shulkerBoxCCEReintroduced));
CarpetServerProtocol.CarpetRules.register(CarpetServerProtocol.CarpetRule.of("carpettisaddition", "instantBlockUpdaterReintroduced", GeneralCompatConfig.instantBlockUpdaterReintroduced));
CarpetServerProtocol.CarpetRules.register(CarpetServerProtocol.CarpetRule.of("carpet", "commandTick", GeneralCompatConfig.commandTick));
CarpetServerProtocol.CarpetRules.register(CarpetServerProtocol.CarpetRule.of("carpet", "creativeNoClip", GeneralCompatConfig.creativeNoClip));
CarpetServerProtocol.CarpetRules.register(CarpetServerProtocol.CarpetRule.of("carpettisaddition", "optimizedDragonRespawn", GeneralCompatConfig.optimizedDragonRespawn));
CarpetServerProtocol.CarpetRules.register(CarpetServerProtocol.CarpetRule.of("carpettisaddition", "antiSpamDisabled", GeneralCompatConfig.antiSpamDisabled));
CarpetServerProtocol.CarpetRules.register(CarpetServerProtocol.CarpetRule.of("carpettisaddition", "blockPlacementIgnoreEntity", GeneralCompatConfig.blockPlacementIgnoreEntity));
CarpetServerProtocol.CarpetRules.register(CarpetServerProtocol.CarpetRule.of("carpettisaddition", "creativeOpenContainerForcibly", GeneralCompatConfig.creativeOpenContainerForcibly));
CarpetServerProtocol.CarpetRules.register(CarpetServerProtocol.CarpetRule.of("carpetamsaddition", "creativeOneHitKill", GeneralCompatConfig.creativeOneHitKill));
CarpetServerProtocol.CarpetRules.register(CarpetServerProtocol.CarpetRule.of("carpettisaddition", "observerNoDetection", GeneralCompatConfig.observerNoDetection));
CarpetServerProtocol.CarpetRules.register(CarpetServerProtocol.CarpetRule.of("carpetamsaddition", "bambooModelNoOffset", GeneralCompatConfig.bambooModelNoOffset));
CarpetServerProtocol.CarpetRules.register(CarpetServerProtocol.CarpetRule.of("carpettisaddition", "creativeNoItemCooldown", GeneralCompatConfig.creativeNoItemCooldown));
CarpetServerProtocol.CarpetRules.register(CarpetServerProtocol.CarpetRule.of("carpet", "ctrlQCraftingFix", GeneralCompatConfig.ctrlQCraftingFix));
CarpetServerProtocol.CarpetRules.register(CarpetServerProtocol.CarpetRule.of("carpetamsaddition", "carpetAlwaysSetDefault", GeneralCompatConfig.carpetAlwaysSetDefault));
CarpetServerProtocol.CarpetRules.register(CarpetServerProtocol.CarpetRule.of("carpet", "placementRotationFix", GeneralCompatConfig.placementRotationFix));
CarpetServerProtocol.CarpetRules.register(CarpetServerProtocol.CarpetRule.of("carpet", "tntDoNotUpdate", GeneralCompatConfig.tntDoNotUpdate));
CarpetServerProtocol.CarpetRules.register(CarpetServerProtocol.CarpetRule.of("carpettisaddition", "totallyNoBlockUpdate", GeneralCompatConfig.totallyNoBlockUpdate));
CarpetServerProtocol.CarpetRules.register(CarpetServerProtocol.CarpetRule.of("carpettisaddition", "tiscmNetworkProtocol", GeneralCompatConfig.tiscmNetworkProtocol));
CarpetServerProtocol.CarpetRules.register(CarpetServerProtocol.CarpetRule.of("carpetorgaddition", "hopperNoItemCost", GeneralCompatConfig.hopperNoItemCost));
CarpetServerProtocol.CarpetRules.register(CarpetServerProtocol.CarpetRule.of("carpet", "explosionNoBlockDamage", GeneralCompatConfig.explosionNoBlockDamage));
CarpetServerProtocol.CarpetRules.register(CarpetServerProtocol.CarpetRule.of("carpettisaddition", "optimizedTNTHighPriority", GeneralCompatConfig.optimizedTNTHighPriority));
CarpetServerProtocol.CarpetRules.register(CarpetServerProtocol.CarpetRule.of("carpet", "tntPrimerMomentumRemoved", GeneralCompatConfig.tntPrimerMomentumRemoved));
CarpetServerProtocol.CarpetRules.register(CarpetServerProtocol.CarpetRule.of("carpettisaddition", "tntIgnoreRedstoneSignal", GeneralCompatConfig.tntIgnoreRedstoneSignal));
CarpetServerProtocol.CarpetRules.register(CarpetServerProtocol.CarpetRule.of("carpettisaddition", "tntDupingFix", GeneralCompatConfig.tntDupingFix));
CarpetServerProtocol.CarpetRules.register(CarpetServerProtocol.CarpetRule.of("carpet", "interactionUpdates", GeneralCompatConfig.interactionUpdates));
CarpetServerProtocol.CarpetRules.register(CarpetServerProtocol.CarpetRule.of("carpet", "xpNoCooldown", GeneralCompatConfig.xpNoCooldown));
CarpetServerProtocol.CarpetRules.register(CarpetServerProtocol.CarpetRule.of("carpetamsaddition", "powerfulExpMending", GeneralCompatConfig.powerfulExpMending));
CarpetServerProtocol.CarpetRules.register(CarpetServerProtocol.CarpetRule.of("carpettisaddition", "clientSettingsLostOnRespawnFix", GeneralCompatConfig.clientSettingsLostOnRespawnFix));
CarpetServerProtocol.CarpetRules.register(CarpetServerProtocol.CarpetRule.of("carpetamsaddition", "sensibleEnderman", GeneralCompatConfig.sensibleEnderman));
CarpetServerProtocol.CarpetRules.register(CarpetServerProtocol.CarpetRule.of("carpettisaddition", "entityInstantDeathRemoval", GeneralCompatConfig.entityInstantDeathRemoval));
CarpetServerProtocol.CarpetRules.register(CarpetServerProtocol.CarpetRule.of("carpettisaddition", "farmlandTrampledDisabled", GeneralCompatConfig.farmlandTrampledDisabled));
CarpetServerProtocol.CarpetRules.register(CarpetServerProtocol.CarpetRule.of("carpetamsaddition", "shulkerGolem", GeneralCompatConfig.shulkerGolem));
CarpetServerProtocol.CarpetRules.register(CarpetServerProtocol.CarpetRule.of("carpetamsaddition", "preventEndSpikeRespawn", GeneralCompatConfig.preventEndSpikeRespawn));
CarpetServerProtocol.CarpetRules.register(CarpetServerProtocol.CarpetRule.of("carpettisaddition", "yeetOutOfOrderChatKick", GeneralCompatConfig.yeetOutOfOrderChatKick));
CarpetServerProtocol.CarpetRules.register(CarpetServerProtocol.CarpetRule.of("carpetamsaddition", "betterCraftableBoneBlock", GeneralCompatConfig.betterCraftableBoneBlock));
CarpetServerProtocol.CarpetRules.register(CarpetServerProtocol.CarpetRule.of("carpetamsaddition", "betterCraftableDispenser", GeneralCompatConfig.betterCraftableDispenser));
CarpetServerProtocol.CarpetRules.register(CarpetServerProtocol.CarpetRule.of("carpet", "viewDistance", GeneralCompatConfig.viewDistance));
CarpetServerProtocol.CarpetRules.register(CarpetServerProtocol.CarpetRule.of("carpettisaddition", "tickCommandPermission", GeneralCompatConfig.normalizedTickCommandPermission()));
CarpetServerProtocol.CarpetRules.register(CarpetServerProtocol.CarpetRule.of("carpettisaddition", "tickFreezeCommandToggleable", GeneralCompatConfig.tickFreezeCommandToggleable));
CarpetServerProtocol.CarpetRules.register(CarpetServerProtocol.CarpetRule.of("carpettisaddition", "syncServerMsptMetricsData", GeneralCompatConfig.syncServerMsptMetricsData));
CarpetServerProtocol.CarpetRules.register(CarpetServerProtocol.CarpetRule.of("carpetorgaddition", "simpleInGameCalculator", GeneralCompatConfig.simpleInGameCalculator));
CarpetServerProtocol.CarpetRules.register(CarpetServerProtocol.CarpetRule.of("carpettisaddition", "microTiming", GeneralCompatConfig.microTiming));
CarpetServerProtocol.CarpetRules.register(CarpetServerProtocol.CarpetRule.of("carpet", "fastRedstoneDust", GeneralCompatConfig.fastRedstoneDust));
CarpetServerProtocol.CarpetRules.register(CarpetServerProtocol.CarpetRule.of("carpet", "lagFreeSpawning", GeneralCompatConfig.lagFreeSpawning));
CarpetServerProtocol.CarpetRules.register(CarpetServerProtocol.CarpetRule.of("carpettisaddition", "optimizedFastEntityMovement", GeneralCompatConfig.optimizedFastEntityMovement));
CarpetServerProtocol.CarpetRules.register(CarpetServerProtocol.CarpetRule.of("carpettisaddition", "optimizedHardHitBoxEntityCollision", GeneralCompatConfig.optimizedHardHitBoxEntityCollision));
CarpetServerProtocol.CarpetRules.register(CarpetServerProtocol.CarpetRule.of("carpettisaddition", "tntFuseDuration", GeneralCompatConfig.normalizedTntFuseDuration()));
CarpetServerProtocol.CarpetRules.register(CarpetServerProtocol.CarpetRule.of("carpet", "defaultLoggers", CarpetLoggerProtocol.serializeConfiguredDefaults(sanitizeDefaultLoggers(GeneralCompatConfig.defaultLoggers))));
CarpetServerProtocol.CarpetRules.register(CarpetServerProtocol.CarpetRule.of("lophine", "commandBot", FakePlayerCompatConfig.commandBot));
CarpetServerProtocol.CarpetRules.register(CarpetServerProtocol.CarpetRule.of("carpet", "commandPlayer", FakePlayerCompatConfig.commandPlayer));
CarpetServerProtocol.CarpetRules.register(CarpetServerProtocol.CarpetRule.of("lophine", "fakePlayerResident", FakePlayerCompatConfig.fakePlayerResident));
CarpetServerProtocol.CarpetRules.register(CarpetServerProtocol.CarpetRule.of("lophine", "openFakePlayerInventory", FakePlayerCompatConfig.openFakePlayerInventory));
CarpetServerProtocol.CarpetRules.register(CarpetServerProtocol.CarpetRule.of("carpettisaddition", "fakePlayerTicksLikeRealPlayer", FakePlayerCompatConfig.fakePlayerTicksLikeRealPlayer));
CarpetServerProtocol.CarpetRules.register(CarpetServerProtocol.CarpetRule.of("carpetamsaddition", "fakePlayerDefaultSurvivalMode", FakePlayerCompatConfig.fakePlayerDefaultSurvivalMode));
CarpetServerProtocol.CarpetRules.register(CarpetServerProtocol.CarpetRule.of("carpetamsaddition", "fakePlayerInteractLikeClient", FakePlayerCompatConfig.fakePlayerInteractLikeClient));
CarpetServerProtocol.CarpetRules.register(CarpetServerProtocol.CarpetRule.of("lophine", "fakePlayerAutoReplaceTool", FakePlayerCompatConfig.fakePlayerAutoReplaceTool));
CarpetServerProtocol.CarpetRules.register(CarpetServerProtocol.CarpetRule.of("lophine", "fakePlayerAutoReplenishment", FakePlayerCompatConfig.fakePlayerAutoReplenishment));
CarpetServerProtocol.CarpetRules.register(CarpetServerProtocol.CarpetRule.of("carpetamsaddition", "fakePlayerAutoReplenishmentFormShulkerBox", FakePlayerCompatConfig.fakePlayerAutoReplenishmentFormShulkerBox));
CarpetServerProtocol.CarpetRules.register(CarpetServerProtocol.CarpetRule.of("lophine", "fakePlayerAutoFish", FakePlayerCompatConfig.fakePlayerAutoFish));
CarpetServerProtocol.CarpetRules.register(CarpetServerProtocol.CarpetRule.of("lophine", "fakePlayerReloadAction", FakePlayerCompatConfig.fakePlayerReloadAction));
CarpetServerProtocol.CarpetRules.register(CarpetServerProtocol.CarpetRule.of("carpet", "hopperCounters", CounterCompatConfig.hopperCounters));
CarpetServerProtocol.CarpetRules.register(CarpetServerProtocol.CarpetRule.of("carpettisaddition", "hopperCountersUnlimitedSpeed", CounterCompatConfig.hopperCountersUnlimitedSpeed));
}
}
@@ -0,0 +1,42 @@
package fun.bm.lophine.carpet;
import java.util.function.Supplier;
public final class InteractionUpdateCompatHelper {
private static final ThreadLocal<Integer> SUPPRESSED_DEPTH = ThreadLocal.withInitial(() -> 0);
public static boolean shouldSkipUpdates() {
return SUPPRESSED_DEPTH.get() > 0;
}
public static void runWithSuppressedUpdates(Runnable action) {
push();
try {
action.run();
} finally {
pop();
}
}
public static <T> T supplyWithSuppressedUpdates(Supplier<T> action) {
push();
try {
return action.get();
} finally {
pop();
}
}
private static void push() {
SUPPRESSED_DEPTH.set(SUPPRESSED_DEPTH.get() + 1);
}
private static void pop() {
int depth = SUPPRESSED_DEPTH.get();
if (depth <= 1) {
SUPPRESSED_DEPTH.remove();
} else {
SUPPRESSED_DEPTH.set(depth - 1);
}
}
}
@@ -0,0 +1,122 @@
package fun.bm.lophine.carpet;
import ca.spottedleaf.moonrise.common.util.TickThread;
import com.mojang.logging.LogUtils;
import io.papermc.paper.threadedregions.RegionizedWorldData;
import net.minecraft.core.BlockPos;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.tags.BlockTags;
import net.minecraft.util.Mth;
import net.minecraft.world.entity.EntitySpawnReason;
import net.minecraft.world.entity.EntityType;
import net.minecraft.world.entity.Mob;
import net.minecraft.world.entity.MobCategory;
import net.minecraft.world.level.block.Block;
import net.minecraft.world.level.block.FenceGateBlock;
import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.world.phys.AABB;
import net.minecraft.world.phys.shapes.Shapes;
import net.minecraft.world.phys.shapes.VoxelShape;
import org.jetbrains.annotations.Nullable;
import org.slf4j.Logger;
import java.util.HashMap;
import java.util.Map;
import java.util.WeakHashMap;
public final class LagFreeSpawningCompatHelper {
private static final Logger LOGGER = LogUtils.getLogger();
private static final Map<RegionizedWorldData, Map<EntityType<?>, Mob>> PRECOOKED_MOBS = new WeakHashMap<>();
public static boolean hasNoCollision(ServerLevel world, AABB bb) {
if (!TickThread.isTickThreadFor(world, bb)) return world.noCollision(bb);
int minX = Mth.floor(bb.minX);
int minY = Mth.floor(bb.minY);
int minZ = Mth.floor(bb.minZ);
int maxY = Mth.ceil(bb.maxY) - 1;
BlockPos.MutableBlockPos blockPos = new BlockPos.MutableBlockPos();
if (bb.getXsize() <= 1.0 && bb.getZsize() <= 1.0) {
for (int y = minY; y <= maxY; y++) {
blockPos.set(minX, y, minZ);
VoxelShape shape = world.getBlockState(blockPos).getCollisionShape(world, blockPos);
if (shape != Shapes.empty()) {
return shape != Shapes.block() && world.noCollision(bb);
}
}
return true;
}
int maxX = Mth.ceil(bb.maxX) - 1;
int maxZ = Mth.ceil(bb.maxZ) - 1;
for (int y = minY; y <= maxY; y++) {
for (int x = minX; x <= maxX; x++) {
for (int z = minZ; z <= maxZ; z++) {
blockPos.set(x, y, z);
VoxelShape shape = world.getBlockState(blockPos).getCollisionShape(world, blockPos);
if (shape != Shapes.empty()) {
return shape != Shapes.block() && world.noCollision(bb);
}
}
}
}
int minBelow = minY - 1;
for (int x = minX; x <= maxX; x++) {
for (int z = minZ; z <= maxZ; z++) {
blockPos.set(x, minBelow, z);
BlockState state = world.getBlockState(blockPos);
Block block = state.getBlock();
if (state.is(BlockTags.FENCES)
|| state.is(BlockTags.WALLS)
|| block instanceof FenceGateBlock && !state.getValue(FenceGateBlock.OPEN)) {
if (x == minX || x == maxX || z == minZ || z == maxZ) {
return world.noCollision(bb);
}
return false;
}
}
}
return true;
}
public static double limitDistanceSq(MobCategory category, double distanceSq) {
return distanceSq > 16384.0 && category != MobCategory.CREATURE ? 0.0 : distanceSq;
}
public static @Nullable Mob getOrCreateMob(ServerLevel level, EntityType<?> entityType) {
RegionizedWorldData data = level.getCurrentWorldData();
if (data == null) return createMob(level, entityType);
Map<EntityType<?>, Mob> cache = PRECOOKED_MOBS.computeIfAbsent(level.getCurrentWorldData(), key -> new HashMap<>());
Mob mob = cache.get(entityType);
if (mob != null && !mob.isRemoved()) {
if (!TickThread.isTickThreadFor(mob)) return createMob(level, entityType);
return mob;
}
return createMob(level, entityType);
}
public static @Nullable Mob createMob(ServerLevel level, EntityType<?> entityType) {
try {
if (entityType.create(level, EntitySpawnReason.NATURAL) instanceof Mob created) {
PRECOOKED_MOBS.get(level.getCurrentWorldData()).put(entityType, created);
return created;
}
LOGGER.warn("Can't precook non-mob entity type: {}", entityType);
} catch (Exception exception) {
LOGGER.warn("Failed to precook mob {}", entityType, exception);
}
return null;
}
public static void markSpawned(ServerLevel level, EntityType<?> entityType) {
Map<EntityType<?>, Mob> cache = PRECOOKED_MOBS.get(level.getCurrentWorldData());
if (cache != null) {
cache.remove(entityType);
}
}
}
@@ -0,0 +1,29 @@
package fun.bm.lophine.carpet.config.modules;
import fun.bm.lophine.carpet.CarpetCompatSync;
import me.earthme.luminol.config.IConfigModule;
import me.earthme.luminol.config.flags.ConfigClassInfo;
import me.earthme.luminol.config.flags.ConfigInfo;
import me.earthme.luminol.enums.EnumConfigCategory;
@ConfigClassInfo(
category = EnumConfigCategory.ROOT,
name = "core",
directory = {"carpet"}
)
public class CoreConfig implements IConfigModule {
@ConfigInfo(name = "enabled", comments = """
Enable carpet features.
If you want to use some function from Carpet modifier,
you need to enable it.
(some function is not managed by this option)
ONLY GENERAL DIRECTORY WAS CONTROLLED BY THIS OPTION.
WARNING: IF YOU ENABLED IT, ORIGINAL CONFIG IN LOPHINE CONFIG WILL BE OVERWRITTEN.""")
public static boolean enabled = false;
@Override
public void beforeFinalLoad() {
CarpetCompatSync.apply();
}
}
@@ -0,0 +1,24 @@
package fun.bm.lophine.carpet.config.modules;
import me.earthme.luminol.config.IConfigModule;
import me.earthme.luminol.config.flags.ConfigClassInfo;
import me.earthme.luminol.config.flags.ConfigInfo;
import me.earthme.luminol.enums.EnumConfigCategory;
@ConfigClassInfo(
category = EnumConfigCategory.ROOT,
name = "hopper_counter",
directory = {"carpet"},
comments = """
Hopper counter compatibility mapped onto Lophine's wool hopper counter implementation."""
)
public class CounterCompatConfig implements IConfigModule {
@ConfigInfo(name = "hopperCounters", comments = """
Enable the existing wool hopper counter implementation.""")
public static boolean hopperCounters = false;
@ConfigInfo(name = "hopperCountersUnlimitedSpeed", comments = """
Remove the hopper transfer speed limit for counters.
Only effective when hopperCounters is enabled.""")
public static boolean hopperCountersUnlimitedSpeed = false;
}
@@ -0,0 +1,64 @@
package fun.bm.lophine.carpet.config.modules;
import me.earthme.luminol.config.IConfigModule;
import me.earthme.luminol.config.flags.ConfigClassInfo;
import me.earthme.luminol.config.flags.ConfigInfo;
import me.earthme.luminol.enums.EnumConfigCategory;
@ConfigClassInfo(
category = EnumConfigCategory.ROOT,
name = "fakeplayer",
directory = {"carpet"},
comments = """
Carpet fakeplayer compatibility mapped onto Lophine fakeplayers.
commandPlayer is currently backed by Lophine's /bot command surface."""
)
public class FakePlayerCompatConfig implements IConfigModule {
@ConfigInfo(name = "commandBot", comments = """
Enable Lophine's /bot command.""")
public static boolean commandBot = false;
@ConfigInfo(name = "commandPlayer", comments = """
Map Carpet's commandPlayer rule to the same fakeplayer command surface used by /bot.""")
public static boolean commandPlayer = false;
@ConfigInfo(name = "fakePlayerResident", comments = """
Keep fakeplayers resident across unload and restart.""")
public static boolean fakePlayerResident = false;
@ConfigInfo(name = "openFakePlayerInventory", comments = """
Allow opening fakeplayer inventories.""")
public static boolean openFakePlayerInventory = false;
@ConfigInfo(name = "fakePlayerTicksLikeRealPlayer", comments = """
Tick fakeplayers in the network phase to better match real player timing.""")
public static boolean fakePlayerTicksLikeRealPlayer = false;
@ConfigInfo(name = "fakePlayerDefaultSurvivalMode", comments = """
Force newly created fakeplayers to start in survival instead of the server default gamemode.""")
public static boolean fakePlayerDefaultSurvivalMode = false;
@ConfigInfo(name = "fakePlayerInteractLikeClient", comments = """
Make fakeplayer entity interaction follow client-side fallback behavior more closely.""")
public static boolean fakePlayerInteractLikeClient = false;
@ConfigInfo(name = "fakePlayerAutoReplaceTool", comments = """
Toggle automatic tool replacement for fakeplayers.""")
public static boolean fakePlayerAutoReplaceTool = false;
@ConfigInfo(name = "fakePlayerAutoReplenishment", comments = """
Toggle automatic stack replenishment for fakeplayers.""")
public static boolean fakePlayerAutoReplenishment = false;
@ConfigInfo(name = "fakePlayerAutoReplenishmentFormShulkerBox", comments = """
Let fakeplayer replenishment pull matching items out of shulker boxes in the inventory.""")
public static boolean fakePlayerAutoReplenishmentFormShulkerBox = false;
@ConfigInfo(name = "fakePlayerAutoFish", comments = """
Let fakeplayers holding a fishing rod automatically cast and reel it in.""")
public static boolean fakePlayerAutoFish = false;
@ConfigInfo(name = "fakePlayerReloadAction", comments = """
Persist queued fakeplayer actions across save and reload.""")
public static boolean fakePlayerReloadAction = false;
}
@@ -0,0 +1,239 @@
package fun.bm.lophine.carpet.config.modules;
import me.earthme.luminol.config.IConfigModule;
import me.earthme.luminol.config.flags.ConfigClassInfo;
import me.earthme.luminol.config.flags.ConfigInfo;
import me.earthme.luminol.enums.EnumConfigCategory;
import java.util.List;
@ConfigClassInfo(
category = EnumConfigCategory.ROOT,
name = "general",
directory = {"carpet"},
comments = """
Carpet/AMS/TIS/Org compatibility rules backed by existing Lophine features.
Only rules that already have a working server-side implementation are exposed here."""
)
public class GeneralCompatConfig implements IConfigModule {
@ConfigInfo(name = "language", comments = """
Carpet language value forwarded to lophine.function.language.lang.""")
public static String language = "en_us";
@ConfigInfo(name = "amsUpdateSuppressionCrashFix", comments = """
Map AMS update suppression crash protection to Lophine's existing crash fix.""")
public static boolean amsUpdateSuppressionCrashFix = false;
@ConfigInfo(name = "yeetUpdateSuppressionCrash", comments = """
Map TIS update suppression crash yeeting to the same Lophine crash fix.""")
public static boolean yeetUpdateSuppressionCrash = false;
@ConfigInfo(name = "dustTrapdoorReintroduced", comments = """
Map dust-on-open-trapdoor behavior to Lophine's redstone-ignore-upwards-update option.""")
public static boolean dustTrapdoorReintroduced = false;
@ConfigInfo(name = "shulkerBoxCCEReintroduced", comments = """
Map shulker-box CCE update suppression to Lophine's cce-update-suppression option.""")
public static boolean shulkerBoxCCEReintroduced = false;
@ConfigInfo(name = "instantBlockUpdaterReintroduced", comments = """
Enable the existing instant block updater patch already carried by Lophine.""")
public static boolean instantBlockUpdaterReintroduced = false;
@ConfigInfo(name = "commandTick", comments = """
Enable the tick command support already patched into Lophine.""")
public static boolean commandTick = false;
@ConfigInfo(name = "creativeNoClip", comments = """
Enable the existing creative fly no clip implementation.""")
public static boolean creativeNoClip = false;
@ConfigInfo(name = "optimizedDragonRespawn", comments = """
Enable the existing optimized dragon respawn implementation from Luminol.""")
public static boolean optimizedDragonRespawn = false;
@ConfigInfo(name = "antiSpamDisabled", comments = """
Disable the server-side chat and creative-drop spam throttles used by vanilla/Spigot.""")
public static boolean antiSpamDisabled = false;
@ConfigInfo(name = "blockPlacementIgnoreEntity", comments = """
Allow creative players to place blocks without entity collision checks.""")
public static boolean blockPlacementIgnoreEntity = false;
@ConfigInfo(name = "creativeOpenContainerForcibly", comments = """
Allow creative players to forcibly open blocked chests, ender chests and shulker boxes.""")
public static boolean creativeOpenContainerForcibly = false;
@ConfigInfo(name = "creativeOneHitKill", comments = """
Allow creative players to instantly kill attackable non-creative, non-spectator entities.
Sneaking expands the effect into a small area attack.""")
public static boolean creativeOneHitKill = false;
@ConfigInfo(name = "observerNoDetection", comments = """
Disable observer detection pulses entirely.""")
public static boolean observerNoDetection = false;
@ConfigInfo(name = "bambooModelNoOffset", comments = """
Remove the random horizontal model offset from bamboo and bamboo saplings.""")
public static boolean bambooModelNoOffset = false;
@ConfigInfo(name = "creativeNoItemCooldown", comments = """
Skip item cooldown application for creative players.""")
public static boolean creativeNoItemCooldown = false;
@ConfigInfo(name = "ctrlQCraftingFix", comments = """
Compatibility flag for the upstream result-slot Ctrl+Q crafting fix already present in the current menu code.""")
public static boolean ctrlQCraftingFix = false;
@ConfigInfo(name = "carpetAlwaysSetDefault", comments = """
Compatibility flag for Lophine's config loader, which already writes default values into the compat config during preload.""")
public static boolean carpetAlwaysSetDefault = false;
@ConfigInfo(name = "placementRotationFix", comments = """
Use the player's main body rotation for placement direction checks instead of interpolated head yaw.""")
public static boolean placementRotationFix = false;
@ConfigInfo(name = "tntDoNotUpdate", comments = """
Prevent TNT from checking redstone power when first placed.""")
public static boolean tntDoNotUpdate = false;
@ConfigInfo(name = "totallyNoBlockUpdate", comments = """
Suppress neighbor and shape updates globally for block changes.""")
public static boolean totallyNoBlockUpdate = false;
@ConfigInfo(name = "tiscmNetworkProtocol", comments = """
Enable the native Carpet TIS Addition network channel on `tiscm:network/v1`.""")
public static boolean tiscmNetworkProtocol = false;
@ConfigInfo(name = "hopperNoItemCost", comments = """
Restore the transferred stack into a hopper when a wool block is placed on top of it.""")
public static boolean hopperNoItemCost = false;
@ConfigInfo(name = "explosionNoBlockDamage", comments = """
Let explosions damage entities without breaking blocks.""")
public static boolean explosionNoBlockDamage = false;
@ConfigInfo(name = "optimizedTNTHighPriority", comments = """
Compatibility flag for the already optimized server explosion path carried by the current runtime.""")
public static boolean optimizedTNTHighPriority = false;
@ConfigInfo(name = "tntPrimerMomentumRemoved", comments = """
Remove the random horizontal launch momentum from newly primed TNT.""")
public static boolean tntPrimerMomentumRemoved = false;
@ConfigInfo(name = "tntIgnoreRedstoneSignal", comments = """
Ignore redstone power when deciding whether TNT should auto-prime.""")
public static boolean tntIgnoreRedstoneSignal = false;
@ConfigInfo(name = "tntDupingFix", comments = """
Toggle the piston desync path used by vanilla TNT duplication setups.""")
public static boolean tntDupingFix = false;
@ConfigInfo(name = "interactionUpdates", comments = """
Control whether player interaction block changes emit normal block updates.
Set to false to suppress neighbor and shape updates during block use and breaking.""")
public static boolean interactionUpdates = true;
@ConfigInfo(name = "xpNoCooldown", comments = """
Allow players to absorb multiple experience orbs in the same tick without pickup delay.""")
public static boolean xpNoCooldown = false;
@ConfigInfo(name = "powerfulExpMending", comments = """
Let picked-up experience repair all damaged mending items in the player's inventory, not only equipped gear.""")
public static boolean powerfulExpMending = false;
@ConfigInfo(name = "clientSettingsLostOnRespawnFix", comments = """
Reapply the player's last known client settings after respawn.""")
public static boolean clientSettingsLostOnRespawnFix = false;
@ConfigInfo(name = "sensibleEnderman", comments = """
Restrict enderman block pickup to pumpkins and melons only.""")
public static boolean sensibleEnderman = false;
@ConfigInfo(name = "entityInstantDeathRemoval", comments = """
Remove the normal 20gt delay before dead living entities are discarded.""")
public static boolean entityInstantDeathRemoval = false;
@ConfigInfo(name = "farmlandTrampledDisabled", comments = """
Prevent farmland from turning into dirt when entities land on it.""")
public static boolean farmlandTrampledDisabled = false;
@ConfigInfo(name = "shulkerGolem", comments = """
Allow a carved pumpkin on top of a shulker box to summon a shulker.""")
public static boolean shulkerGolem = false;
@ConfigInfo(name = "preventEndSpikeRespawn", comments = """
Skip obsidian spike regeneration during dragon respawn.""")
public static boolean preventEndSpikeRespawn = false;
@ConfigInfo(name = "yeetOutOfOrderChatKick", comments = """
Ignore out-of-order secure chat chain checks instead of invalidating the chat session.""")
public static boolean yeetOutOfOrderChatKick = false;
@ConfigInfo(name = "betterCraftableBoneBlock", comments = """
Add the AMS alternate bone block recipe that yields 3 bone blocks from 9 bones.""")
public static boolean betterCraftableBoneBlock = false;
@ConfigInfo(name = "betterCraftableDispenser", comments = """
Add the AMS alternate dispenser recipes using a dropper.""")
public static boolean betterCraftableDispenser = false;
@ConfigInfo(name = "viewDistance", comments = """
Override the dedicated server's startup view distance with the Carpet-compatible value.""")
public static int viewDistance = 12;
@ConfigInfo(name = "tickCommandPermission", comments = """
Override the `/tick` command permission level.
Accepts values in the range 0..4, where 2 matches old Carpet behavior and 3 keeps vanilla.""")
public static int tickCommandPermission = 3;
@ConfigInfo(name = "tickFreezeCommandToggleable", comments = """
Make `/tick freeze` toggle back to running when executed while the server is already frozen.""")
public static boolean tickFreezeCommandToggleable = false;
@ConfigInfo(name = "syncServerMsptMetricsData", comments = """
Broadcast live MSPT samples through the native TISCM protocol channel.""")
public static boolean syncServerMsptMetricsData = false;
@ConfigInfo(name = "simpleInGameCalculator", comments = """
Evaluate chat messages prefixed with `=` as a simple calculator expression and reply privately.""")
public static boolean simpleInGameCalculator = false;
@ConfigInfo(name = "microTiming", comments = """
Compatibility flag for the built-in region profiler and timing instrumentation carried by Folia/Moonrise.""")
public static boolean microTiming = false;
@ConfigInfo(name = "fastRedstoneDust", comments = """
Route redstone dust updates through the Alternate Current fast-update backend.""")
public static boolean fastRedstoneDust = false;
@ConfigInfo(name = "lagFreeSpawning", comments = """
Use the lightweight collision and precooked-mob spawning path for natural spawning checks.""")
public static boolean lagFreeSpawning = false;
@ConfigInfo(name = "optimizedFastEntityMovement", comments = """
Compatibility flag for the always-on Moonrise/Paper fast entity movement collision pipeline.""")
public static boolean optimizedFastEntityMovement = false;
@ConfigInfo(name = "optimizedHardHitBoxEntityCollision", comments = """
Compatibility flag for the always-on Moonrise/Paper hard-hitbox entity collision optimizations.""")
public static boolean optimizedHardHitBoxEntityCollision = false;
@ConfigInfo(name = "tntFuseDuration", comments = """
Override the default primed TNT fuse duration in ticks.
Accepts values in the range 0..32767.""")
public static int tntFuseDuration = 80;
@ConfigInfo(name = "defaultLoggers", comments = """
Carpet-style default logger subscriptions for players.
Examples: ["tps", "mob_caps", "counter white"]""")
public static List<String> defaultLoggers = List.of();
public static int normalizedTntFuseDuration() {
return Math.clamp(tntFuseDuration, 0, Short.MAX_VALUE);
}
public static int normalizedTickCommandPermission() {
return Math.clamp(tickCommandPermission, 0, 4);
}
}
@@ -39,4 +39,8 @@ public class CommandConfig implements IConfigModule {
"""
Log all process of save-all command to console""")
public static boolean logAllProcess = false;
@ConfigInfo(name = "save_all_command_timeout", directory = {"save_all_command"}, comments = """
Maximum seconds to save before the chunk report it is timeout.""")
public static long saveAllTimeout = 30;
}
@@ -17,4 +17,11 @@ public class GlobalEntitiesCounter implements IConfigModule {
@HotReloadUnsupported
@ConfigInfo(name = "async", comments = "Enable Asynchronous(maybe cause bugs)")
public static boolean async = false;
@HotReloadUnsupported
@ConfigInfo(name = "always_count", comments = """
Always count entities.
If you want to count entities loaded by chunk loader,
you must to enabled it.""")
public static boolean alwaysCount = false;
}
@@ -0,0 +1,25 @@
package fun.bm.lophine.config.modules.function;
import com.electronwill.nightconfig.core.file.CommentedFileConfig;
import me.earthme.luminol.config.IConfigModule;
import me.earthme.luminol.config.flags.ConfigClassInfo;
import me.earthme.luminol.config.flags.ConfigInfo;
import me.earthme.luminol.enums.EnumConfigCategory;
import org.jetbrains.annotations.Nullable;
import org.leavesmc.leaves.protocol.CarpetServerProtocol;
import java.util.Set;
@ConfigClassInfo(name = "creative_fly_no_clip", category = EnumConfigCategory.FUNCTION)
public class CreativeFlyNoClipConfig implements IConfigModule {
@ConfigInfo(name = "enabled", comments = """
Whether to enable creative fly no clip.
When enabled, players in creative mode will not collide with blocks while flying.
This allows them to pass through blocks without obstruction.""")
public static boolean enabled = false;
@Override
public void onLoaded(CommentedFileConfig configInstance, @Nullable Set<Exception> e) {
CarpetServerProtocol.CarpetRules.register(CarpetServerProtocol.CarpetRule.of("carpet", "creativeNoClip", enabled));
}
}
@@ -81,7 +81,6 @@ public class FakeplayerConfig implements IConfigModule {
@ConfigInfo(name = "enable-locator-bar", comments = """
Enable locator bar for fakeplayers""")
public static boolean enableLocatorBar = false;
public static ServerBot.TickType tickType = ServerBot.TickType.ENTITY_LIST;
private BotCommand command = null;
@@ -13,4 +13,12 @@ public class LanguageConfig implements IConfigModule {
Please use the key from https://minecraft.wiki/w/Language
Sample of format: en_us zh_cn zh_hk zh_tw""")
public static String lang = "en_us";
@ConfigInfo(name = "full_blocking_load", comments = """
Whether to allow blocking server loading when loading localized language.
If you want only use your localized language to shown in your terminal,
you need to enable it.
WARNING: This may slow down the startup speed!""")
public static boolean full_blocking_load = false;
}
@@ -5,8 +5,8 @@ import fun.bm.lophine.command.counter.CounterCommand;
import me.earthme.luminol.config.IConfigModule;
import me.earthme.luminol.config.flags.ConfigClassInfo;
import me.earthme.luminol.config.flags.ConfigInfo;
import me.earthme.luminol.config.flags.DoNotLoad;
import me.earthme.luminol.enums.EnumConfigCategory;
import org.bukkit.Bukkit;
import org.jetbrains.annotations.Nullable;
import java.util.Set;
@@ -19,13 +19,23 @@ public class WoolHopperCounterConfig implements IConfigModule {
@ConfigInfo(name = "unlimited-speed")
public static boolean unlimitedSpeed = false;
@DoNotLoad
private static CounterCommand counterCommand = null;
@Override
public void onLoaded(CommentedFileConfig configInstance, @Nullable Set<Exception> exs) {
if (enabled) new CounterCommand().register();
if (enabled) {
if (counterCommand == null) {
counterCommand = new CounterCommand();
}
counterCommand.register();
}
}
@Override
public void onUnloaded(CommentedFileConfig configInstance) {
Bukkit.getCommandMap().getKnownCommands().remove("luminol:tpsbar");
if (counterCommand != null) {
counterCommand.unregister();
}
}
}
@@ -0,0 +1,29 @@
package fun.bm.lophine.config.modules.misc;
import me.earthme.luminol.config.IConfigModule;
import me.earthme.luminol.config.flags.ConfigClassInfo;
import me.earthme.luminol.config.flags.ConfigInfo;
import me.earthme.luminol.enums.EnumConfigCategory;
/*
* This file is only for showing auto update config.
* The function is implemented in luminol-server and not provide any function.
* Please use luminol-server's auto update config.
*/
@ConfigClassInfo(
category = EnumConfigCategory.MISC,
name = "auto_update",
comments = """
Checks GitHub Releases for newer version's jars on a schedule.
Downloads are staged under auto_update/lophine and written to auto_update/core.path,
which Hyacinthusclip can consume on the next restart.
ATTENTION: full config option should be edited in luminol config system >> misc >> auto_update"""
)
public class AutoUpdateConfig implements IConfigModule {
@ConfigInfo(name = "enabled", comments = """
Whether the server should check for updates automatically.
You can enable it by edit this config, because they are both control the function.
One of them enabled, the full function will be enabled.""")
public static boolean enabled = false;
}
@@ -0,0 +1,302 @@
package fun.bm.lophine.protocol;
import fun.bm.lophine.carpet.config.modules.GeneralCompatConfig;
import io.papermc.paper.adventure.PaperAdventure;
import io.papermc.paper.threadedregions.RegionizedWorldData;
import io.papermc.paper.threadedregions.TickRegionScheduler;
import net.kyori.adventure.text.Component;
import net.minecraft.ChatFormatting;
import net.minecraft.network.chat.MutableComponent;
import net.minecraft.network.protocol.game.ClientboundTabListPacket;
import net.minecraft.server.MinecraftServer;
import net.minecraft.server.ServerTickRateManager;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.server.level.ServerPlayer;
import net.minecraft.world.entity.LivingEntity;
import net.minecraft.world.entity.MobCategory;
import net.minecraft.world.item.DyeColor;
import net.minecraft.world.level.Level;
import net.minecraft.world.level.NaturalSpawner;
import org.jetbrains.annotations.NotNull;
import org.leavesmc.leaves.protocol.core.LeavesProtocol;
import org.leavesmc.leaves.protocol.core.ProtocolHandler;
import org.leavesmc.leaves.util.HopperCounter;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
@LeavesProtocol.Register(namespace = "carpet")
public class CarpetLoggerProtocol implements LeavesProtocol {
private static final Logger LOGGER = LoggerFactory.getLogger("CarpetLoggerProtocol");
private static final Map<String, Map<String, String>> PLAYER_SUBSCRIPTIONS = new ConcurrentHashMap<>();
private static volatile Map<String, String> configuredSubscriptions = null;
public static void refreshConfiguredDefaults(boolean initial) {
Map<String, String> defaults = parseConfiguredDefaults(GeneralCompatConfig.defaultLoggers);
configuredSubscriptions = defaults;
if (defaults == null || defaults.isEmpty()) {
PLAYER_SUBSCRIPTIONS.clear();
if (initial) return;
for (ServerPlayer player : MinecraftServer.getServer().getPlayerList().getPlayers()) {
clearHud(player);
}
return;
}
PLAYER_SUBSCRIPTIONS.replaceAll((playerName, ignored) -> defaults);
}
public static String serializeConfiguredDefaults(List<String> configuredLoggers) {
List<String> serialized = new ArrayList<>();
if (configuredLoggers != null) {
for (String entry : configuredLoggers) {
if (entry == null) {
continue;
}
String trimmed = entry.trim();
if (!trimmed.isEmpty()) {
serialized.add(trimmed);
}
}
}
return serialized.isEmpty() ? "none" : String.join(",", serialized);
}
@ProtocolHandler.PlayerJoin
public static void onPlayerJoin(ServerPlayer player) {
if (configuredSubscriptions != null && !configuredSubscriptions.isEmpty()) {
PLAYER_SUBSCRIPTIONS.putIfAbsent(player.getScoreboardName(), configuredSubscriptions);
}
}
@ProtocolHandler.PlayerLeave
public static void onPlayerLeave(ServerPlayer player) {
clearHud(player);
}
@ProtocolHandler.Ticker(tickerId = "hud")
public static void onHudTick() {
if (PLAYER_SUBSCRIPTIONS.isEmpty()) {
return;
}
MinecraftServer server = MinecraftServer.getServer();
for (ServerPlayer player : server.getPlayerList().getPlayers()) {
Map<String, String> subscriptions = PLAYER_SUBSCRIPTIONS.get(player.getScoreboardName());
if (subscriptions == null || subscriptions.isEmpty()) {
continue;
}
player.getBukkitEntity().taskScheduler.schedule((LivingEntity livingEntity) -> {
if (livingEntity instanceof ServerPlayer scheduledPlayer) {
sendHud(server, scheduledPlayer, subscriptions);
}
}, null, 1L);
}
}
@Override
public boolean isActive() {
return true;
}
@Override
public int tickerInterval(String tickerID) {
return "hud".equals(tickerID) ? 20 : 1;
}
private static void sendHud(MinecraftServer server, ServerPlayer player, Map<String, String> subscriptions) {
List<net.minecraft.network.chat.Component> lines = new ArrayList<>();
subscriptions.forEach((loggerName, option) -> {
switch (loggerName) {
case "tps" -> lines.add(buildTpsLine(server));
case "mobcaps" -> {
net.minecraft.network.chat.Component line = buildMobcapsLine(player, option);
if (line != null) {
lines.add(line);
}
}
case "counter" -> lines.addAll(buildCounterLines(server, option));
default -> {
}
}
});
MutableComponent footer = net.minecraft.network.chat.Component.empty();
for (int i = 0; i < lines.size(); i++) {
if (i > 0) {
footer.append(net.minecraft.network.chat.Component.literal("\n"));
}
footer.append(lines.get(i));
}
player.connection.send(new ClientboundTabListPacket(net.minecraft.network.chat.Component.empty(), footer));
}
private static void clearHud(ServerPlayer player) {
player.connection.send(new ClientboundTabListPacket(net.minecraft.network.chat.Component.empty(), net.minecraft.network.chat.Component.empty()));
}
private static net.minecraft.network.chat.Component buildTpsLine(MinecraftServer server) {
ServerTickRateManager tickManager = server.tickRateManager();
ca.spottedleaf.moonrise.common.time.TickData.TickReportData tickData = TickRegionScheduler.getCurrentRegion().getData().getRegionSchedulingHandle().getTickReport5s(System.nanoTime());
final double tps = tickData.tpsData().segmentAll().average();
final double mspt = tickData.timePerTickData().segmentAll().average() / 1.0E6;
ChatFormatting color = heatmapColor(mspt, tickManager.millisecondsPerTick());
return net.minecraft.network.chat.Component.empty()
.append(net.minecraft.network.chat.Component.literal("TPS: ").withStyle(ChatFormatting.GRAY))
.append(net.minecraft.network.chat.Component.literal(String.format(Locale.US, "%.1f", tps)).withStyle(color))
.append(net.minecraft.network.chat.Component.literal(" MSPT: ").withStyle(ChatFormatting.GRAY))
.append(net.minecraft.network.chat.Component.literal(String.format(Locale.US, "%.1f", mspt)).withStyle(color));
}
private static net.minecraft.network.chat.Component buildMobcapsLine(ServerPlayer player, String option) {
final ServerLevel level = resolveMobcapsLevel(player, option);
if (level == null) {
return null;
}
final RegionizedWorldData data = level.getCurrentWorldData();
if (data == null) {
return null;
}
final NaturalSpawner.SpawnState spawnState = data.lastSpawnState;
if (spawnState == null) {
return net.minecraft.network.chat.Component.literal("Mobcaps: unavailable").withStyle(ChatFormatting.DARK_GRAY);
}
int chunks = spawnState.getSpawnableChunkCount();
var counts = spawnState.getMobCategoryCounts();
MutableComponent line = net.minecraft.network.chat.Component.literal("Mobcaps").withStyle(ChatFormatting.GRAY);
for (MobCategory category : MobCategory.values()) {
if (category == MobCategory.MISC) {
continue;
}
int current = counts.getOrDefault(category, 0);
int limit = NaturalSpawner.globalLimitForCategory(level, category, chunks);
line.append(net.minecraft.network.chat.Component.literal(" " + shortName(category) + " ").withStyle(ChatFormatting.DARK_GRAY));
line.append(net.minecraft.network.chat.Component.literal(current + "/" + limit).withStyle(categoryColor(current, limit)));
}
return line;
}
private static List<net.minecraft.network.chat.Component> buildCounterLines(MinecraftServer server, String option) {
List<net.minecraft.network.chat.Component> lines = new ArrayList<>();
String colors = option == null || option.isBlank() ? "white" : option;
for (String rawColor : colors.split(",")) {
String colorName = rawColor.trim();
if (colorName.isEmpty()) {
continue;
}
DyeColor color = DyeColor.byName(colorName, null);
if (color == null) {
continue;
}
HopperCounter counter = HopperCounter.getCounter(color);
if (counter == null) {
continue;
}
for (Component component : counter.format(server, false)) {
lines.add(PaperAdventure.asVanilla(component));
}
}
return lines;
}
private static ServerLevel resolveMobcapsLevel(ServerPlayer player, String option) {
if (option == null || option.isBlank() || option.equalsIgnoreCase("dynamic")) {
return player.level();
}
return switch (option.toLowerCase(Locale.ROOT)) {
case "overworld" -> player.level().dimension() == Level.OVERWORLD ? player.level() : null;
case "nether" -> player.level().dimension() == Level.NETHER ? player.level() : null;
case "end" -> player.level().dimension() == Level.END ? player.level() : null;
default -> player.level();
};
}
private static ChatFormatting heatmapColor(double actual, double reference) {
if (actual > reference) {
return ChatFormatting.LIGHT_PURPLE;
}
if (actual > 0.8D * reference) {
return ChatFormatting.RED;
}
if (actual > 0.5D * reference) {
return ChatFormatting.YELLOW;
}
if (actual >= 0.0D) {
return ChatFormatting.DARK_GREEN;
}
return ChatFormatting.GRAY;
}
private static ChatFormatting categoryColor(int current, int limit) {
if (limit <= 0) {
return ChatFormatting.DARK_GRAY;
}
double ratio = current / (double) limit;
if (ratio >= 1.0D) {
return ChatFormatting.RED;
}
if (ratio >= 0.8D) {
return ChatFormatting.YELLOW;
}
return ChatFormatting.GREEN;
}
private static String shortName(MobCategory category) {
return switch (category) {
case MONSTER -> "M";
case CREATURE -> "C";
case AMBIENT -> "A";
case AXOLOTLS -> "Ax";
case UNDERGROUND_WATER_CREATURE -> "UWC";
case WATER_CREATURE -> "WC";
case WATER_AMBIENT -> "WA";
case MISC -> "X";
};
}
private static Map<String, String> parseConfiguredDefaults(List<String> configuredLoggers) {
LinkedHashMap<String, String> subscriptions = new LinkedHashMap<>();
if (configuredLoggers == null) {
return null;
}
for (String entry : configuredLoggers) {
if (entry == null) {
continue;
}
for (String chunk : entry.split(",")) {
String token = chunk.trim();
if (token.isEmpty() || token.equalsIgnoreCase("none")) {
continue;
}
String[] parts = token.split("\\s+", 2);
String loggerName = parts[0].toLowerCase(Locale.ROOT);
if (!isSupported(loggerName)) {
LOGGER.debug("Ignoring unsupported Carpet default logger '{}'", loggerName);
continue;
}
String option = parts.length == 1 ? defaultOption(loggerName) : parts[1].trim();
if (option.isEmpty()) {
option = defaultOption(loggerName);
}
subscriptions.put(loggerName, option);
}
}
return subscriptions.isEmpty() ? null : Map.copyOf(subscriptions);
}
private static boolean isSupported(String loggerName) {
return Arrays.asList("tps", "mobcaps", "counter").contains(loggerName);
}
private static @NotNull String defaultOption(String loggerName) {
return switch (loggerName) {
case "mobcaps" -> "dynamic";
case "counter" -> "white";
default -> "";
};
}
}
@@ -0,0 +1,201 @@
package fun.bm.lophine.protocol.tiscm;
import com.mojang.logging.LogUtils;
import fun.bm.lophine.carpet.config.modules.GeneralCompatConfig;
import io.papermc.paper.ServerBuildInfo;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.nbt.ListTag;
import net.minecraft.nbt.StringTag;
import net.minecraft.network.RegistryFriendlyByteBuf;
import net.minecraft.network.codec.ByteBufCodecs;
import net.minecraft.network.codec.StreamCodec;
import net.minecraft.resources.Identifier;
import net.minecraft.server.MinecraftServer;
import net.minecraft.server.level.ServerPlayer;
import org.jetbrains.annotations.Contract;
import org.leavesmc.leaves.protocol.core.LeavesCustomPayload;
import org.leavesmc.leaves.protocol.core.LeavesProtocol;
import org.leavesmc.leaves.protocol.core.ProtocolHandler;
import org.leavesmc.leaves.protocol.core.ProtocolUtils;
import org.slf4j.Logger;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.stream.Collectors;
@LeavesProtocol.Register(namespace = TISCMProtocol.PROTOCOL_ID)
public class TISCMProtocol implements LeavesProtocol {
private static final Logger LOGGER = LogUtils.getLogger();
public static final String PROTOCOL_ID = "tiscm";
private static final String PLATFORM_NAME = "Lophine";
private static final String PLATFORM_VERSION = ServerBuildInfo.buildInfo().asString(ServerBuildInfo.StringRepresentation.VERSION_SIMPLE);
private static final Map<String, EnumSet<S2CPacket>> CLIENT_SUPPORTED_PACKETS = new ConcurrentHashMap<>();
@Contract("_ -> new")
public static Identifier id(String path) {
return Identifier.fromNamespaceAndPath(PROTOCOL_ID, path);
}
public static void broadcastMsptSample(long tickCounter, long nanosecond) {
if (!GeneralCompatConfig.tiscmNetworkProtocol || !GeneralCompatConfig.syncServerMsptMetricsData) {
return;
}
CompoundTag nbt = new CompoundTag();
nbt.putInt("version", 2);
nbt.putLong("millisecond", nanosecond / 1_000_000L);
nbt.putLong("nanosecond", nanosecond);
nbt.putString("type", "tick_server_method");
TISCMPayload payload = new TISCMPayload(S2CPacket.MSPT_METRICS_SAMPLE.id, nbt);
for (ServerPlayer player : MinecraftServer.getServer().getPlayerList().getPlayers()) {
if (supports(player, S2CPacket.MSPT_METRICS_SAMPLE)) {
ProtocolUtils.sendPayloadPacket(player, payload);
}
}
}
@ProtocolHandler.PayloadReceiver(payload = TISCMPayload.class)
public static void handlePayload(ServerPlayer player, TISCMPayload payload) {
if (!GeneralCompatConfig.tiscmNetworkProtocol) {
return;
}
Optional<C2SPacket> packetType = C2SPacket.fromId(payload.packetId());
if (packetType.isEmpty()) {
return;
}
switch (packetType.get()) {
case HI -> handleHi(player, payload.nbt());
case SUPPORTED_S2C_PACKETS -> handleSupportedS2CPackets(player, payload.nbt());
default -> {
}
}
}
@ProtocolHandler.PlayerLeave
public static void onPlayerLeave(ServerPlayer player) {
CLIENT_SUPPORTED_PACKETS.remove(player.getStringUUID());
}
private static void handleHi(ServerPlayer player, CompoundTag payload) {
String platformName = payload.getString("platform_name").orElse("Unknown");
String platformVersion = payload.getString("platform_version").orElse("Unknown");
LOGGER.info("Player {} connected with TISCM protocol support ({} @ {})", player.getScoreboardName(), platformName, platformVersion);
send(player, S2CPacket.HELLO, nbt -> {
nbt.putString("platform_name", PLATFORM_NAME);
nbt.putString("platform_version", PLATFORM_VERSION);
});
send(player, S2CPacket.SUPPORTED_C2S_PACKETS, nbt -> nbt.put("supported_c2s_packets", stringList(List.of(
C2SPacket.HI.id,
C2SPacket.SUPPORTED_S2C_PACKETS.id
))));
}
private static void handleSupportedS2CPackets(ServerPlayer player, CompoundTag payload) {
EnumSet<S2CPacket> packets = EnumSet.noneOf(S2CPacket.class);
ListTag listTag = payload.getListOrEmpty("supported_s2c_packets");
for (int i = 0; i < listTag.size(); i++) {
S2CPacket.fromId(listTag.getString(i).orElse("")).ifPresent(packets::add);
}
CLIENT_SUPPORTED_PACKETS.put(player.getStringUUID(), packets);
}
private static void send(ServerPlayer player, S2CPacket packet, PayloadBuilder builder) {
if (!supports(player, packet)) {
return;
}
CompoundTag nbt = new CompoundTag();
builder.accept(nbt);
ProtocolUtils.sendPayloadPacket(player, new TISCMPayload(packet.id, nbt));
}
private static boolean supports(ServerPlayer player, S2CPacket packet) {
if (packet.handshake) {
return true;
}
EnumSet<S2CPacket> packets = CLIENT_SUPPORTED_PACKETS.get(player.getStringUUID());
return packets != null && packets.contains(packet);
}
private static ListTag stringList(List<String> values) {
ListTag list = new ListTag();
for (String value : values) {
list.add(StringTag.valueOf(value));
}
return list;
}
@Override
public boolean isActive() {
return GeneralCompatConfig.tiscmNetworkProtocol;
}
private interface PayloadBuilder {
void accept(CompoundTag nbt);
}
public enum C2SPacket {
HI("hi", true),
SUPPORTED_S2C_PACKETS("supported_s2c_packets", true),
SPEED_TEST_UPLOAD_PAYLOAD("speed_test_upload_payload", false),
SPEED_TEST_PING("speed_test_ping", false);
private static final Map<String, C2SPacket> BY_ID = Arrays.stream(values()).collect(Collectors.toMap(packet -> packet.id, packet -> packet));
private final String id;
private final boolean handshake;
C2SPacket(String id, boolean handshake) {
this.id = id;
this.handshake = handshake;
}
public static Optional<C2SPacket> fromId(String id) {
return Optional.ofNullable(BY_ID.get(id));
}
}
public enum S2CPacket {
HELLO("hello", true),
SUPPORTED_C2S_PACKETS("supported_c2s_packets", true),
MSPT_METRICS_SAMPLE("mspt_metrics_sample", false),
SPEED_TEST_DOWNLOAD_PAYLOAD("speed_test_download_payload", false),
SPEED_TEST_UPLOAD_REQUEST("speed_test_upload_request", false),
SPEED_TEST_PING("speed_test_ping", false),
SPEED_TEST_ABORT("speed_test_abort", false);
private static final Map<String, S2CPacket> BY_ID = Arrays.stream(values()).collect(Collectors.toMap(packet -> packet.id, packet -> packet));
private final String id;
private final boolean handshake;
S2CPacket(String id, boolean handshake) {
this.id = id;
this.handshake = handshake;
}
public static Optional<S2CPacket> fromId(String id) {
return Optional.ofNullable(BY_ID.get(id));
}
}
public record TISCMPayload(String packetId, CompoundTag nbt) implements LeavesCustomPayload {
@ID
private static final Identifier NETWORK_ID = TISCMProtocol.id("network/v1");
@Codec
private static final StreamCodec<RegistryFriendlyByteBuf, TISCMPayload> CODEC = StreamCodec.composite(
ByteBufCodecs.STRING_UTF8,
TISCMPayload::packetId,
ByteBufCodecs.COMPOUND_TAG,
TISCMPayload::nbt,
TISCMPayload::new
);
}
}
@@ -2,6 +2,9 @@ package fun.bm.lophine.utils;
import ca.spottedleaf.moonrise.common.list.ReferenceList;
import ca.spottedleaf.moonrise.common.misc.PositionCountingAreaMap;
import com.google.common.cache.Cache;
import com.google.common.cache.CacheBuilder;
import com.mojang.logging.LogUtils;
import fun.bm.lophine.config.modules.experiment.GlobalEntitiesCounter;
import it.unimi.dsi.fastutil.objects.Object2IntOpenHashMap;
import net.minecraft.core.BlockPos;
@@ -18,25 +21,27 @@ import net.minecraft.world.level.biome.MobSpawnSettings;
import org.bukkit.event.entity.CreatureSpawnEvent;
import org.jetbrains.annotations.Nullable;
import java.util.HashSet;
import java.util.Set;
import java.util.WeakHashMap;
import java.util.*;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ConcurrentHashMap;
import static net.minecraft.world.level.NaturalSpawner.getRoughBiome;
public class EntitiesCounterUtil {
private static final WeakHashMap<ServerLevel, WeakHashMap<Integer, ReferenceList<Entity>>> globalLoadedEntities = new WeakHashMap<>();
private static final WeakHashMap<ServerLevel, Object2IntOpenHashMap<MobCategory>> mobsMap = new WeakHashMap<>();
private static final WeakHashMap<ServerLevel, WeakHashMap<Integer, PositionCountingAreaMap<ServerPlayer>>> mobsAreaMap = new WeakHashMap<>();
private static final WeakHashMap<ServerLevel, Integer> spawnableChunkCount = new WeakHashMap<>();
private static final WeakHashMap<ServerLevel, CompletableFuture<Void>> tasks = new WeakHashMap<>();
private static final Set<Integer> UniqueIds = new HashSet<>();
private static final Map<ServerLevel, Cache<Integer, ReferenceList<Entity>>> globalLoadedEntities = new ConcurrentHashMap<>();
private static final Map<ServerLevel, Object2IntOpenHashMap<MobCategory>> mobsMap = Collections.synchronizedMap(new WeakHashMap<>());
private static final Map<ServerLevel, Cache<Integer, PositionCountingAreaMap<ServerPlayer>>> mobsAreaMap = new ConcurrentHashMap<>();
private static final Map<ServerLevel, Integer> spawnableChunkCount = new ConcurrentHashMap<>();
private static final Map<ServerLevel, CompletableFuture<Void>> tasks = new ConcurrentHashMap<>();
private static final Set<Integer> UniqueIds = ConcurrentHashMap.newKeySet();
private static int lastUsedId = 0;
private static final int CLEANUP_INTERVAL = 200; // each 200 ids used
public static int generateUniqueId() {
synchronized (UniqueIds) {
if (lastUsedId % CLEANUP_INTERVAL == 0) runCleanUp();
int id = lastUsedId;
while (UniqueIds.contains(id)) {
id++;
@@ -49,26 +54,39 @@ public class EntitiesCounterUtil {
}
public static void onWorldDataUnload(ServerLevel level, int uniqueId) {
synchronized (UniqueIds) {
UniqueIds.remove(uniqueId);
UniqueIds.remove(uniqueId);
Cache<Integer, ReferenceList<Entity>> entitiesCache = globalLoadedEntities.get(level);
if (entitiesCache != null) {
entitiesCache.invalidate(uniqueId);
}
synchronized (globalLoadedEntities) {
globalLoadedEntities.get(level).remove(uniqueId);
Cache<Integer, PositionCountingAreaMap<ServerPlayer>> areaCache = mobsAreaMap.get(level);
if (areaCache != null) {
areaCache.invalidate(uniqueId);
}
synchronized (mobsAreaMap) {
mobsAreaMap.get(level).remove(uniqueId);
}
private static void runCleanUp() {
Set<Integer> logged = new HashSet<>();
for (Cache<Integer, ReferenceList<Entity>> collection : globalLoadedEntities.values()) {
logged.addAll(collection.asMap().keySet());
}
for (int num : UniqueIds) {
if (logged.contains(num)) continue;
UniqueIds.remove(num);
}
}
public static void addDataToLoaded(ServerLevel level, ReferenceList<Entity> data, int uniqueId) {
WeakHashMap<Integer, ReferenceList<Entity>> data0 = globalLoadedEntities.computeIfAbsent(level, k -> new WeakHashMap<>());
if (data0.containsKey(uniqueId)) return;
Cache<Integer, ReferenceList<Entity>> data0 = globalLoadedEntities.computeIfAbsent(level, k -> CacheBuilder.newBuilder().concurrencyLevel(16).weakValues().build());
if (data0.asMap().containsKey(uniqueId)) return;
data0.put(uniqueId, data);
}
public static void reportAreaMap(ServerLevel level, PositionCountingAreaMap<ServerPlayer> areaMap, int uniqueId) {
WeakHashMap<Integer, PositionCountingAreaMap<ServerPlayer>> areaMap0 = mobsAreaMap.computeIfAbsent(level, k -> new WeakHashMap<>());
if (areaMap0.containsKey(uniqueId)) return;
Cache<Integer, PositionCountingAreaMap<ServerPlayer>> areaMap0 = mobsAreaMap.computeIfAbsent(level, k -> CacheBuilder.newBuilder().concurrencyLevel(16).weakValues().build());
if (areaMap0.asMap().containsKey(uniqueId)) return;
areaMap0.put(uniqueId, areaMap);
}
@@ -87,39 +105,53 @@ public class EntitiesCounterUtil {
public static void tick(ServerLevel level) {
Runnable task = () -> {
WeakHashMap<Integer, ReferenceList<Entity>> data0 = globalLoadedEntities.get(level);
Object2IntOpenHashMap<MobCategory> map = new Object2IntOpenHashMap<>();
for (ReferenceList<Entity> data : data0.values()) {
for (Entity entity : GlobalEntitiesCounter.enabled ? data.copy() : data) {
if (entity == null || entity.isRemoved() || !entity.isAlive()) continue;
// Lophine start - Copy from net/minecraft/world/level/NaturalSpawner
MobCategory category = entity.getType().getCategory();
if (category != MobCategory.MISC) {
// Paper start - Only count natural spawns
if (!entity.level().paperConfig().entities.spawning.countAllMobsForSpawning &&
!(entity.spawnReason == CreatureSpawnEvent.SpawnReason.NATURAL ||
entity.spawnReason == CreatureSpawnEvent.SpawnReason.CHUNK_GEN)) {
continue;
try {
Cache<Integer, ReferenceList<Entity>> data0 = globalLoadedEntities.get(level);
if (data0 == null) return;
Object2IntOpenHashMap<MobCategory> map = new Object2IntOpenHashMap<>();
Collection<ReferenceList<Entity>> snapshot = data0.asMap().values();
for (ReferenceList<Entity> data : snapshot) {
if (data == null) continue;
for (Entity entity : GlobalEntitiesCounter.async ? data.copy() : data) {
if (entity == null || entity.isRemoved() || !entity.isAlive()) continue;
// Lophine start - Copy from net/minecraft/world/level/NaturalSpawner
MobCategory category = entity.getType().getCategory();
if (category != MobCategory.MISC) {
// Paper start - Only count natural spawns
if (!entity.level().paperConfig().entities.spawning.countAllMobsForSpawning &&
!(entity.spawnReason == CreatureSpawnEvent.SpawnReason.NATURAL ||
entity.spawnReason == CreatureSpawnEvent.SpawnReason.CHUNK_GEN)) {
continue;
}
// Paper end - Only count natural spawns
map.addTo(category, 1);
}
// Paper end - Only count natural spawns
map.addTo(category, 1);
}
// Lophine start - Copy from net/minecraft/world/level/NaturalSpawner
// Lophine end - Copy from net/minecraft/world/level/NaturalSpawner
}
}
mobsMap.put(level, map);
for (ServerLevel world : mobsAreaMap.keySet()) {
int count = 0;
WeakHashMap<Integer, PositionCountingAreaMap<ServerPlayer>> collection = mobsAreaMap.get(level);
if (collection == null) continue;
for (PositionCountingAreaMap<ServerPlayer> areaMap : collection.values()) {
count += areaMap.getTotalPositions();
mobsMap.put(level, map);
Cache<Integer, PositionCountingAreaMap<ServerPlayer>> collection = mobsAreaMap.get(level);
if (collection != null) {
int count = 0;
for (PositionCountingAreaMap<ServerPlayer> areaMap : collection.asMap().values()) {
if (areaMap != null) {
count += areaMap.getTotalPositions();
}
}
spawnableChunkCount.put(level, count);
}
spawnableChunkCount.put(world, count);
} catch (Exception e) {
LogUtils.getClassLogger().error("Failed to run task", e);
}
};
if (GlobalEntitiesCounter.async) {
tasks.put(level, CompletableFuture.runAsync(task));
tasks.put(level, CompletableFuture.runAsync(task).exceptionally(ex -> {
LogUtils.getClassLogger().error("Failed to run task", ex);
return null;
}));
} else {
task.run();
}
@@ -13,7 +13,6 @@ import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger;
public class SaveAllUtil {
private static final long TIMEOUT = 1000 * 30; // 30 seconds
public static final Object lock = new Object();
public static volatile long lastSaveAllTime = 0;
private static volatile Pair<CommandSourceStack, Boolean> currentSaveAll = null;
@@ -31,9 +30,7 @@ public class SaveAllUtil {
// we need to count how many regions we need to save
AtomicInteger count = new AtomicInteger();
for (ServerLevel world : source.getServer().getAllLevels()) {
world.regioniser.computeForAllRegions(unused -> {
count.getAndIncrement();
});
world.regioniser.computeForAllRegions(unused -> count.getAndIncrement());
}
regionCount = count.get();
savedRegionCount.set(0);
@@ -76,7 +73,7 @@ public class SaveAllUtil {
public static void checkTimeout() {
Pair<CommandSourceStack, Boolean> currentSaveAll = SaveAllUtil.currentSaveAll;
if (!isSaving()) return;
if (System.currentTimeMillis() - lastSaveAllTime > TIMEOUT) {
if (System.currentTimeMillis() - lastSaveAllTime > CommandConfig.saveAllTimeout) {
currentSaveAll.getFirst().sendFailure(Component.literal("At least one region save data timeout!"));
currentSaveAll.getFirst().sendFailure(Component.literal("Regions need to save expect is " + regionCount + ", but only " + savedRegionCount.get() + " saved!"));
SaveAllUtil.currentSaveAll = null;
@@ -64,7 +64,11 @@ public class ServerI18nUtil {
langJsonPath = "minecraft/lang/" + LanguageConfig.lang + ".json";
lophineLangPath = "/assets/lophine/lang/" + LanguageConfig.lang + ".json";
logger.info("Starting load language: {}", LanguageConfig.lang);
preloadTask.thenAcceptAsync(v -> loadI18n(LanguageConfig.lang, 2));
if (LanguageConfig.full_blocking_load) {
loadI18n(LanguageConfig.lang, 2);
} else {
preloadTask.thenAcceptAsync(v -> loadI18n(LanguageConfig.lang, 2));
}
}
public static void preInit() {
@@ -82,7 +86,7 @@ public class ServerI18nUtil {
} catch (UnsupportedLanguageException e) {
logger.warn("Unsupported language: {}", LanguageConfig.lang);
// Fallback to English
final ConfigsInstance configsInstance = ConfigManager.configfiles.get("lophine");
final ConfigsInstance configsInstance = ConfigManager.getConfigs("lophine");
configsInstance.setConfig(new String[]{"function", "language", "lang"}, "en_us");
configsInstance.reloadAsync(true);
} catch (Exception e) {
@@ -0,0 +1,36 @@
package fun.bm.lophine.utils.concurrent;
import java.util.List;
import java.util.Map;
public abstract class AbstractConcurrentTable<X, Y, Z> {
public abstract void put(X x, Y y, Z z);
public abstract void remove(X x, Y y, Z z);
public abstract List<Z> getZ(X x, Y y);
public abstract List<Y> getY(X x, Z z);
public abstract List<X> getX(Y y, Z z);
public abstract Map<X, Y> getXY(Z z);
public abstract Map<Y, Z> getYZ(X x);
public abstract Map<X, Z> getXZ(Y y);
public abstract List<X> getAllX();
public abstract List<Y> getAllY();
public abstract List<Z> getAllZ();
public abstract void clearXY(Z z);
public abstract void clearYZ(X x);
public abstract void clearXZ(Y y);
public abstract void clearAll();
}
@@ -0,0 +1,182 @@
package fun.bm.lophine.utils.concurrent;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentLinkedDeque;
import java.util.function.Predicate;
public class ConcurrentTable<X, Y, Z> extends AbstractConcurrentTable<X, Y, Z> {
protected final ConcurrentLinkedDeque<TableEntry<X, Y, Z>> data = new ConcurrentLinkedDeque<>();
protected final boolean flagX;
protected final boolean flagY;
protected final boolean flagZ;
public ConcurrentTable() {
this(false, false, false);
}
public ConcurrentTable(boolean flagX, boolean flagY, boolean flagZ) {
this.flagX = flagX;
this.flagY = flagY;
this.flagZ = flagZ;
}
@Override
public void put(X x, Y y, Z z) {
put(x, y, z, false);
}
public void put(X x, Y y, Z z, boolean flag) {
if (!flag) {
if (flagX) {
List<X> datas = getX(y, z);
for (X x1 : datas) {
if (!x1.equals(x)) {
remove(x1, y, z);
}
}
}
if (flagY) {
List<Y> datas = getY(x, z);
for (Y y1 : datas) {
if (!y1.equals(y)) {
remove(x, y1, z);
}
}
}
if (flagZ) {
List<Z> datas = getZ(x, y);
for (Z z1 : datas) {
if (!z1.equals(z)) {
remove(x, y, z1);
}
}
}
}
data.add(new TableEntry<>(x, y, z));
}
@Override
public void remove(X x, Y y, Z z) {
data.removeIf(entry -> entry.getX().equals(x) && entry.getY().equals(y) && entry.getZ().equals(z));
}
@Override
public List<Z> getZ(X x, Y y) {
return filterAndCollect(
entry -> entry.getX().equals(x) && entry.getY().equals(y),
TableEntry::getZ
);
}
@Override
public List<Y> getY(X x, Z z) {
return filterAndCollect(
entry -> entry.getX().equals(x) && entry.getZ().equals(z),
TableEntry::getY
);
}
@Override
public List<X> getX(Y y, Z z) {
return filterAndCollect(
entry -> entry.getY().equals(y) && entry.getZ().equals(z),
TableEntry::getX
);
}
@Override
public Map<X, Y> getXY(Z z) {
return filterAndMap(
entry -> entry.getZ().equals(z),
TableEntry::getX,
TableEntry::getY
);
}
@Override
public Map<Y, Z> getYZ(X x) {
return filterAndMap(
entry -> entry.getX().equals(x),
TableEntry::getY,
TableEntry::getZ
);
}
@Override
public Map<X, Z> getXZ(Y y) {
return filterAndMap(
entry -> entry.getY().equals(y),
TableEntry::getX,
TableEntry::getZ
);
}
@Override
public List<X> getAllX() {
return collectAll(TableEntry::getX);
}
@Override
public List<Y> getAllY() {
return collectAll(TableEntry::getY);
}
@Override
public List<Z> getAllZ() {
return collectAll(TableEntry::getZ);
}
@Override
public void clearXY(Z z) {
data.removeIf(entry -> entry.getZ().equals(z));
}
@Override
public void clearYZ(X x) {
data.removeIf(entry -> entry.getX().equals(x));
}
@Override
public void clearXZ(Y y) {
data.removeIf(entry -> entry.getY().equals(y));
}
@Override
public void clearAll() {
data.clear();
}
private <T> List<T> filterAndCollect(Predicate<TableEntry<X, Y, Z>> filter,
java.util.function.Function<TableEntry<X, Y, Z>, T> mapper) {
List<T> result = new ArrayList<>();
for (TableEntry<X, Y, Z> entry : data) {
if (filter.test(entry)) {
result.add(mapper.apply(entry));
}
}
return result;
}
private <K, V> Map<K, V> filterAndMap(Predicate<TableEntry<X, Y, Z>> filter,
java.util.function.Function<TableEntry<X, Y, Z>, K> keyMapper,
java.util.function.Function<TableEntry<X, Y, Z>, V> valueMapper) {
Map<K, V> map = new HashMap<>();
for (TableEntry<X, Y, Z> entry : data) {
if (filter.test(entry)) {
map.put(keyMapper.apply(entry), valueMapper.apply(entry));
}
}
return map;
}
private <T> List<T> collectAll(java.util.function.Function<TableEntry<X, Y, Z>, T> mapper) {
List<T> result = new ArrayList<>();
for (TableEntry<X, Y, Z> entry : data) {
result.add(mapper.apply(entry));
}
return result;
}
}
@@ -0,0 +1,207 @@
package fun.bm.lophine.utils.concurrent;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.function.Function;
import java.util.function.Predicate;
public class OptimizedConcurrentTable<X, Y, Z> extends ConcurrentTable<X, Y, Z> {
private final ConcurrentHashMap<X, ConcurrentHashMap<Y, Set<Z>>> xyIndex = new ConcurrentHashMap<>();
private final ConcurrentHashMap<Y, ConcurrentHashMap<Z, Set<X>>> yzIndex = new ConcurrentHashMap<>();
private final ConcurrentHashMap<Z, ConcurrentHashMap<X, Set<Y>>> zxIndex = new ConcurrentHashMap<>();
public OptimizedConcurrentTable() {
super();
}
public OptimizedConcurrentTable(boolean flagX, boolean flagY, boolean flagZ) {
super(flagX, flagY, flagZ);
}
@Override
public void put(X x, Y y, Z z) {
if (flagX) {
List<X> datas = getX(y, z);
for (X x1 : datas) {
if (!x1.equals(x)) {
remove(x1, y, z);
}
}
}
if (flagY) {
List<Y> datas = getY(x, z);
for (Y y1 : datas) {
if (!y1.equals(y)) {
remove(x, y1, z);
}
}
}
if (flagZ) {
List<Z> datas = getZ(x, y);
for (Z z1 : datas) {
if (!z1.equals(z)) {
remove(x, y, z1);
}
}
}
super.put(x, y, z, true);
xyIndex.computeIfAbsent(x, k -> new ConcurrentHashMap<>())
.computeIfAbsent(y, k -> ConcurrentHashMap.newKeySet()).add(z);
yzIndex.computeIfAbsent(y, k -> new ConcurrentHashMap<>())
.computeIfAbsent(z, k -> ConcurrentHashMap.newKeySet()).add(x);
zxIndex.computeIfAbsent(z, k -> new ConcurrentHashMap<>())
.computeIfAbsent(x, k -> ConcurrentHashMap.newKeySet()).add(y);
}
@Override
public void remove(X x, Y y, Z z) {
super.remove(x, y, z);
removeFromIndex(xyIndex, x, y, z);
removeFromIndex(yzIndex, y, z, x);
removeFromIndex(zxIndex, z, x, y);
}
private <K, V, T> void removeFromIndex(ConcurrentHashMap<K, ConcurrentHashMap<V, Set<T>>> index,
K key1, V key2, T value) {
index.computeIfPresent(key1, (k, map) -> {
map.computeIfPresent(key2, (k2, set) -> {
set.remove(value);
return set.isEmpty() ? null : set;
});
return map.isEmpty() ? null : map;
});
}
public void removeAll(Predicate<TableEntry<X, Y, Z>> predicate) {
data.removeIf(entry -> {
boolean shouldRemove = predicate.test(entry);
if (shouldRemove) {
removeFromIndex(xyIndex, entry.getX(), entry.getY(), entry.getZ());
removeFromIndex(yzIndex, entry.getY(), entry.getZ(), entry.getX());
removeFromIndex(zxIndex, entry.getZ(), entry.getX(), entry.getY());
}
return shouldRemove;
});
}
public boolean putIfAbsent(X x, Y y, Z z) {
if (data.stream().anyMatch(entry ->
Objects.equals(entry.getX(), x) &&
Objects.equals(entry.getY(), y) &&
Objects.equals(entry.getZ(), z))) {
return false;
}
put(x, y, z);
return true;
}
@Override
public List<Z> getZ(X x, Y y) {
Set<Z> result = xyIndex.getOrDefault(x, new ConcurrentHashMap<>()).get(y);
return result != null ? new ArrayList<>(result) : new ArrayList<>();
}
@Override
public List<Y> getY(X x, Z z) {
Set<Y> result = zxIndex.getOrDefault(z, new ConcurrentHashMap<>()).get(x);
return result != null ? new ArrayList<>(result) : new ArrayList<>();
}
public List<X> getX(Y y, Z z) {
Set<X> result = yzIndex.getOrDefault(y, new ConcurrentHashMap<>()).get(z);
return result != null ? new ArrayList<>(result) : new ArrayList<>();
}
@Override
public Map<X, Y> getXY(Z z) {
return buildMapFromIndex(zxIndex.get(z), Function.identity(), Function.identity());
}
@Override
public Map<Y, Z> getYZ(X x) {
return buildMapFromIndex(xyIndex.get(x), Function.identity(), Function.identity());
}
@Override
public Map<X, Z> getXZ(Y y) {
return reverseMapFromIndex(yzIndex.get(y));
}
@Override
public List<X> getAllX() {
Set<X> resultSet = new HashSet<>(xyIndex.keySet());
return new ArrayList<>(resultSet);
}
@Override
public List<Y> getAllY() {
Set<Y> resultSet = new HashSet<>(yzIndex.keySet());
return new ArrayList<>(resultSet);
}
@Override
public List<Z> getAllZ() {
Set<Z> resultSet = new HashSet<>(zxIndex.keySet());
return new ArrayList<>(resultSet);
}
@Override
public void clearXY(Z z) {
super.clearXY(z);
zxIndex.remove(z);
}
@Override
public void clearYZ(X x) {
super.clearYZ(x);
xyIndex.remove(x);
}
@Override
public void clearXZ(Y y) {
super.clearXZ(y);
yzIndex.remove(y);
}
@Override
public void clearAll() {
super.clearAll();
xyIndex.clear();
yzIndex.clear();
zxIndex.clear();
}
private <K, V, R, S> Map<R, S> buildMapFromIndex(ConcurrentHashMap<K, Set<V>> indexMap, java.util.function.Function<K, R> keyMapper, java.util.function.Function<V, S> valueMapper) {
Map<R, S> result = new HashMap<>();
if (indexMap != null) {
for (Map.Entry<K, Set<V>> entry : indexMap.entrySet()) {
K key = entry.getKey();
Set<V> valueSet = entry.getValue();
if (valueSet != null && !valueSet.isEmpty()) {
for (V value : valueSet) {
result.put(keyMapper.apply(key), valueMapper.apply(value));
}
}
}
}
return result;
}
private <K, V> Map<V, K> reverseMapFromIndex(ConcurrentHashMap<K, Set<V>> indexMap) {
Map<V, K> result = new HashMap<>();
if (indexMap != null) {
for (Map.Entry<K, Set<V>> entry : indexMap.entrySet()) {
K key = entry.getKey();
Set<V> valueSet = entry.getValue();
if (valueSet != null && !valueSet.isEmpty()) {
for (V value : valueSet) {
result.put(value, key);
}
}
}
}
return result;
}
}
@@ -0,0 +1,25 @@
package fun.bm.lophine.utils.concurrent;
public class TableEntry<X, Y, Z> {
private final X x;
private final Y y;
private final Z z;
public TableEntry(X x, Y y, Z z) {
this.x = x;
this.y = y;
this.z = z;
}
public X getX() {
return x;
}
public Y getY() {
return y;
}
public Z getZ() {
return z;
}
}
@@ -18,11 +18,15 @@
package org.leavesmc.leaves.bot;
import com.google.common.base.Charsets;
import fun.bm.lophine.carpet.config.modules.FakePlayerCompatConfig;
import fun.bm.lophine.config.modules.function.FakeplayerConfig;
import net.minecraft.core.NonNullList;
import net.minecraft.core.component.DataComponents;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.tags.ItemTags;
import net.minecraft.world.entity.EquipmentSlot;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.item.component.ItemContainerContents;
import org.bukkit.Bukkit;
import org.jetbrains.annotations.NotNull;
@@ -34,23 +38,66 @@ public class BotUtil {
public static void replenishment(@NotNull ItemStack itemStack, NonNullList<ItemStack> itemStackList) {
int count = itemStack.getMaxStackSize() / 2;
if (itemStack.getCount() <= 8 && count > 8) {
for (ItemStack itemStack1 : itemStackList) {
if (itemStack1 == ItemStack.EMPTY || itemStack1 == itemStack) {
if (pullMatchingStack(itemStack, itemStackList, count)) {
return;
}
if (FakePlayerCompatConfig.fakePlayerAutoReplenishmentFormShulkerBox) {
pullMatchingStackFromShulkerBox(itemStack, itemStackList, count);
}
}
}
private static boolean pullMatchingStack(@NotNull ItemStack targetStack, NonNullList<ItemStack> itemStackList, int transferLimit) {
for (ItemStack inventoryStack : itemStackList) {
if (inventoryStack == ItemStack.EMPTY || inventoryStack == targetStack) {
continue;
}
if (ItemStack.isSameItemSameComponents(inventoryStack, targetStack)) {
if (inventoryStack.getCount() > transferLimit) {
targetStack.setCount(targetStack.getCount() + transferLimit);
inventoryStack.setCount(inventoryStack.getCount() - transferLimit);
} else {
targetStack.setCount(targetStack.getCount() + inventoryStack.getCount());
inventoryStack.setCount(0);
}
return true;
}
}
return false;
}
private static boolean pullMatchingStackFromShulkerBox(@NotNull ItemStack targetStack, NonNullList<ItemStack> itemStackList, int transferLimit) {
for (ItemStack containerStack : itemStackList) {
if (containerStack == ItemStack.EMPTY || !containerStack.is(ItemTags.SHULKER_BOXES)) {
continue;
}
ItemContainerContents contents = containerStack.getOrDefault(DataComponents.CONTAINER, ItemContainerContents.EMPTY);
NonNullList<ItemStack> shulkerItems = NonNullList.withSize(contents.items.size(), ItemStack.EMPTY);
contents.copyInto(shulkerItems);
for (int slot = 0; slot < shulkerItems.size(); slot++) {
ItemStack shulkerStack = shulkerItems.get(slot);
if (shulkerStack.isEmpty() || !ItemStack.isSameItemSameComponents(shulkerStack, targetStack)) {
continue;
}
if (ItemStack.isSameItemSameComponents(itemStack1, itemStack)) {
if (itemStack1.getCount() > count) {
itemStack.setCount(itemStack.getCount() + count);
itemStack1.setCount(itemStack1.getCount() - count);
} else {
itemStack.setCount(itemStack.getCount() + itemStack1.getCount());
itemStack1.setCount(0);
}
break;
int moved = Math.min(Math.min(transferLimit, shulkerStack.getCount()), targetStack.getMaxStackSize() - targetStack.getCount());
if (moved <= 0) {
return false;
}
targetStack.grow(moved);
shulkerStack.shrink(moved);
shulkerItems.set(slot, shulkerStack.isEmpty() ? ItemStack.EMPTY : shulkerStack);
containerStack.set(DataComponents.CONTAINER, ItemContainerContents.fromItems(shulkerItems));
return true;
}
}
return false;
}
public static void replaceTool(@NotNull EquipmentSlot slot, @NotNull ServerBot bot) {
@@ -20,6 +20,7 @@ package org.leavesmc.leaves.bot;
import com.google.common.collect.ImmutableMap;
import com.mojang.authlib.GameProfile;
import fun.bm.lophine.LophineLogger;
import fun.bm.lophine.carpet.config.modules.FakePlayerCompatConfig;
import fun.bm.lophine.config.modules.function.FakeplayerConfig;
import io.papermc.paper.adventure.PaperAdventure;
import io.papermc.paper.event.entity.EntityKnockbackEvent;
@@ -52,9 +53,11 @@ import net.minecraft.world.entity.PositionMoveRotation;
import net.minecraft.world.entity.item.ItemEntity;
import net.minecraft.world.entity.player.Input;
import net.minecraft.world.entity.player.Player;
import net.minecraft.world.entity.projectile.FishingHook;
import net.minecraft.world.entity.projectile.ProjectileUtil;
import net.minecraft.world.entity.vehicle.boat.AbstractBoat;
import net.minecraft.world.inventory.ChestMenu;
import net.minecraft.world.item.FishingRodItem;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.item.crafting.RecipeHolder;
import net.minecraft.world.level.gameevent.GameEvent;
@@ -84,6 +87,8 @@ import java.util.function.Predicate;
import static net.minecraft.server.MinecraftServer.getServer;
public class ServerBot extends ServerPlayer {
private static final int AUTO_FISH_RECAST_DELAY = 4;
private static final int AUTO_FISH_ENTITY_DELAY = 20;
private final List<AbstractBotAction<?>> actions;
private final Map<String, AbstractBotConfig<?, ?>> configs;
@@ -100,6 +105,7 @@ public class ServerBot extends ServerPlayer {
public int notSleepTicks;
public int removeTaskId = -1;
private int autoFishCooldown = 0;
public ServerBot(MinecraftServer server, ServerLevel world, GameProfile profile) {
super(server, world, profile, ClientInformation.createDefault());
@@ -237,6 +243,7 @@ public class ServerBot extends ServerPlayer {
}
this.getCooldowns().tick();
this.tickAutoFish();
this.updatePlayerPose();
}
@@ -339,8 +346,10 @@ public class ServerBot extends ServerPlayer {
ItemStack item = this.getItemInHand(hand);
if (!item.isEmpty()) {
BotUtil.replenishment(item, getInventory().getNonEquipmentItems());
if (BotUtil.isDamage(item, 10)) {
if (FakePlayerCompatConfig.fakePlayerAutoReplenishment) {
BotUtil.replenishment(item, getInventory().getNonEquipmentItems());
}
if (FakePlayerCompatConfig.fakePlayerAutoReplaceTool && BotUtil.isDamage(item, 10)) {
BotUtil.replaceTool(hand == InteractionHand.MAIN_HAND ? EquipmentSlot.MAINHAND : EquipmentSlot.OFFHAND, this);
}
}
@@ -388,7 +397,7 @@ public class ServerBot extends ServerPlayer {
nbt.store("createStatus", CompoundTag.CODEC, createNbt);
if (!this.actions.isEmpty()) {
if (FakePlayerCompatConfig.fakePlayerReloadAction && !this.actions.isEmpty()) {
ValueOutput.TypedOutputList<CompoundTag> actionNbt = nbt.list("actions", CompoundTag.CODEC);
for (AbstractBotAction<?> action : this.actions) {
actionNbt.add(action.save(new CompoundTag()));
@@ -431,7 +440,7 @@ public class ServerBot extends ServerPlayer {
this.gameProfile = BotList.createBotProfile(this.getUUID(), this.createState.fullName(), this.createState.skin());
if (nbt.list("actions", CompoundTag.CODEC).isPresent()) {
if (FakePlayerCompatConfig.fakePlayerReloadAction && nbt.list("actions", CompoundTag.CODEC).isPresent()) {
ValueInput.TypedInputList<CompoundTag> actionNbt = nbt.list("actions", CompoundTag.CODEC).orElseThrow();
actionNbt.forEach(actionTag -> {
AbstractBotAction<?> action = Actions.getForName(actionTag.getString("actionName").orElseThrow());
@@ -656,6 +665,45 @@ public class ServerBot extends ServerPlayer {
}
}
private void tickAutoFish() {
if (!fun.bm.lophine.carpet.config.modules.FakePlayerCompatConfig.fakePlayerAutoFish || this.hasActiveAction("fish")) {
this.autoFishCooldown = 0;
return;
}
if (this.autoFishCooldown > 0) {
this.autoFishCooldown--;
return;
}
ItemStack mainHand = this.getMainHandItem();
if (mainHand.isEmpty() || !(mainHand.getItem() instanceof FishingRodItem)) {
return;
}
FishingHook fishingHook = this.fishing;
if (fishingHook != null) {
if (fishingHook.currentState == FishingHook.FishHookState.HOOKED_IN_ENTITY) {
mainHand.use(this.level(), this, InteractionHand.MAIN_HAND);
this.autoFishCooldown = AUTO_FISH_ENTITY_DELAY;
return;
}
if (fishingHook.nibble > 0) {
mainHand.use(this.level(), this, InteractionHand.MAIN_HAND);
this.autoFishCooldown = AUTO_FISH_RECAST_DELAY;
}
return;
}
mainHand.use(this.level(), this, InteractionHand.MAIN_HAND);
this.autoFishCooldown = AUTO_FISH_RECAST_DELAY;
}
private boolean hasActiveAction(String actionName) {
return this.actions.stream().anyMatch(action -> !action.isCancelled() && action.getName().equals(actionName));
}
public boolean addBotAction(AbstractBotAction<?> action, CommandSender sender) {
if (!FakeplayerConfig.canUseAction) {
return false;
@@ -17,8 +17,10 @@
package org.leavesmc.leaves.bot;
import fun.bm.lophine.carpet.config.modules.FakePlayerCompatConfig;
import net.kyori.adventure.text.Component;
import net.minecraft.core.BlockPos;
import net.minecraft.server.MinecraftServer;
import net.minecraft.server.level.ServerPlayer;
import net.minecraft.server.level.ServerPlayerGameMode;
import net.minecraft.world.InteractionHand;
@@ -40,7 +42,7 @@ public class ServerBotGameMode extends ServerPlayerGameMode {
public ServerBotGameMode(ServerBot bot) {
super(bot);
super.setGameModeForPlayer(GameType.SURVIVAL, null);
super.setGameModeForPlayer(getInitialGameMode(bot), null);
}
@Override
@@ -133,4 +135,8 @@ public class ServerBotGameMode extends ServerPlayerGameMode {
return InteractionResult.PASS;
}
}
private static GameType getInitialGameMode(ServerBot bot) {
return FakePlayerCompatConfig.fakePlayerDefaultSurvivalMode ? GameType.SURVIVAL : MinecraftServer.getServer().getDefaultGameType();
}
}
@@ -17,9 +17,12 @@
package org.leavesmc.leaves.bot.agent.actions;
import fun.bm.lophine.carpet.config.modules.FakePlayerCompatConfig;
import net.minecraft.world.InteractionHand;
import net.minecraft.world.InteractionResult;
import net.minecraft.world.entity.Entity;
import net.minecraft.world.entity.decoration.ArmorStand;
import net.minecraft.world.item.Items;
import net.minecraft.world.phys.EntityHitResult;
import net.minecraft.world.phys.Vec3;
import org.jetbrains.annotations.NotNull;
@@ -51,6 +54,13 @@ public class ServerUseItemToAction extends AbstractUseBotAction<ServerUseItemToA
Vec3 vec3 = hitResult.getLocation().subtract(entity.getX(), entity.getY(), entity.getZ());
bot.updateItemInHand(hand);
InteractionResult interactionResult = entity.interactAt(bot, vec3, hand);
if (FakePlayerCompatConfig.fakePlayerInteractLikeClient
&& entity instanceof ArmorStand stand
&& !stand.isMarker()
&& !bot.isSpectator()
&& !bot.getItemInHand(hand).is(Items.NAME_TAG)) {
interactionResult = InteractionResult.PASS;
}
if (!interactionResult.consumesAction()) {
interactionResult = bot.interactOn(hitResult.getEntity(), hand);
}
@@ -24,7 +24,6 @@ import fun.bm.lophine.config.modules.function.FakeplayerConfig;
import me.earthme.luminol.utils.FoliaServerWaypointManager;
import me.earthme.luminol.utils.NullPlugin;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.server.waypoints.ServerWaypointManager;
import org.bukkit.Bukkit;
import org.jetbrains.annotations.NotNull;
import org.leavesmc.leaves.command.CommandContext;
@@ -0,0 +1,139 @@
package org.leavesmc.leaves.protocol;
import com.mojang.logging.LogUtils;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.network.FriendlyByteBuf;
import net.minecraft.network.codec.ByteBufCodecs;
import net.minecraft.network.codec.StreamCodec;
import net.minecraft.resources.Identifier;
import net.minecraft.server.level.ServerPlayer;
import org.jetbrains.annotations.Contract;
import org.jetbrains.annotations.NotNull;
import org.leavesmc.leaves.protocol.core.LeavesCustomPayload;
import org.leavesmc.leaves.protocol.core.LeavesProtocol;
import org.leavesmc.leaves.protocol.core.ProtocolHandler;
import org.leavesmc.leaves.protocol.core.ProtocolUtils;
import org.slf4j.Logger;
import java.util.HashMap;
import java.util.Locale;
import java.util.Map;
@LeavesProtocol.Register(namespace = "carpet")
public class CarpetServerProtocol implements LeavesProtocol {
private static final Logger LOGGER = LogUtils.getClassLogger();
public static final String PROTOCOL_ID = "carpet";
public static final String VERSION = ProtocolUtils.buildProtocolVersion(PROTOCOL_ID);
private static final String HI = "69";
private static final String HELLO = "420";
@Contract("_ -> new")
public static Identifier id(String path) {
return Identifier.fromNamespaceAndPath(PROTOCOL_ID, path);
}
@ProtocolHandler.PlayerJoin
public static void onPlayerJoin(ServerPlayer player) {
CompoundTag data = new CompoundTag();
data.putString(HI, VERSION);
ProtocolUtils.sendPayloadPacket(player, new CarpetPayload(data));
}
@ProtocolHandler.PayloadReceiver(payload = CarpetPayload.class)
private static void handleHello(@NotNull ServerPlayer player, @NotNull CarpetServerProtocol.CarpetPayload payload) {
if (payload.nbt.contains(HELLO)) {
LOGGER.info("Player {} joined with carpet {}", player.getScoreboardName(), payload.nbt.getString(HELLO).orElse("Unknown"));
CompoundTag data = new CompoundTag();
CarpetRules.write(data);
ProtocolUtils.sendPayloadPacket(player, new CarpetPayload(data));
}
}
@Override
public boolean isActive() {
return CarpetRules.hasRules();
}
public static class CarpetRules {
private static final Map<String, CarpetRule> rules = new HashMap<>();
public static void write(@NotNull CompoundTag tag) {
CompoundTag rulesNbt = new CompoundTag();
rules.values().forEach(rule -> rule.writeNBT(rulesNbt));
tag.put("Rules", rulesNbt);
}
public static void register(CarpetRule rule) {
rules.put(rule.name, rule);
}
public static void clear() {
rules.clear();
}
public static boolean hasRules() {
return !rules.isEmpty();
}
}
public record CarpetRule(String identifier, String name, String value) {
@NotNull
@Contract("_, _, _ -> new")
public static CarpetRule of(String identifier, String name, Enum<?> value) {
return new CarpetRule(identifier, name, value.name().toLowerCase(Locale.ROOT));
}
@NotNull
@Contract("_, _, _ -> new")
public static CarpetRule of(String identifier, String name, boolean value) {
return new CarpetRule(identifier, name, Boolean.toString(value));
}
@NotNull
@Contract("_, _, _ -> new")
public static CarpetRule of(String identifier, String name, int value) {
return new CarpetRule(identifier, name, Integer.toString(value));
}
@NotNull
@Contract("_, _, _ -> new")
public static CarpetRule of(String identifier, String name, long value) {
return new CarpetRule(identifier, name, Long.toString(value));
}
@NotNull
@Contract("_, _, _ -> new")
public static CarpetRule of(String identifier, String name, String value) {
return new CarpetRule(identifier, name, value);
}
public void writeNBT(@NotNull CompoundTag rules) {
CompoundTag rule = new CompoundTag();
String key = name;
while (rules.contains(key)) {
key = key + "2";
}
rule.putString("Value", value);
rule.putString("Manager", identifier);
rule.putString("Rule", name);
rules.put(key, rule);
}
}
public record CarpetPayload(CompoundTag nbt) implements LeavesCustomPayload {
@ID
private static final Identifier HELLO_ID = CarpetServerProtocol.id("hello");
@Codec
private static final StreamCodec<FriendlyByteBuf, CarpetPayload> CODEC = StreamCodec.composite(
ByteBufCodecs.COMPOUND_TAG, CarpetPayload::nbt, CarpetPayload::new
);
}
}
@@ -17,10 +17,10 @@
package org.leavesmc.leaves.protocol.servux;
import com.google.common.collect.HashBasedTable;
import com.google.common.collect.Table;
import com.mojang.serialization.DataResult;
import fun.bm.lophine.config.modules.function.protocol.ServuxProtocolConfig;
import fun.bm.lophine.utils.concurrent.AbstractConcurrentTable;
import fun.bm.lophine.utils.concurrent.OptimizedConcurrentTable;
import io.netty.buffer.Unpooled;
import net.minecraft.core.BlockPos;
import net.minecraft.nbt.CompoundTag;
@@ -53,12 +53,12 @@ public class ServuxHudDataProtocol implements LeavesProtocol {
public static final int PROTOCOL_VERSION = 2;
private static final List<ServerPlayer> players = new ArrayList<>();
private static final Set<ServerPlayer> players = ConcurrentHashMap.newKeySet();
private static final int updateInterval = 80;
private static final Map<ServerPlayer, List<DataLogger.Type>> loggerPlayers = new ConcurrentHashMap<>();
private static final Map<DataLogger.Type, DataLogger<?>> LOGGERS = new ConcurrentHashMap<>();
private static final Table<DataLogger.Type, ServerPlayer, Tag> DATA = HashBasedTable.create();
private static final AbstractConcurrentTable<DataLogger.Type, ServerPlayer, Tag> DATA = new OptimizedConcurrentTable<>(false, false, true);
public static boolean refreshSpawnMetadata = false;
@@ -84,6 +84,7 @@ public class ServuxHudDataProtocol implements LeavesProtocol {
private static void onPlayerLeave(ServerPlayer player) {
players.remove(player);
loggerPlayers.remove(player);
DATA.clearXZ(player);
}
@ProtocolHandler.PayloadReceiver(payload = HudDataPayload.class)
@@ -258,10 +259,12 @@ public class ServuxHudDataProtocol implements LeavesProtocol {
loggerPlayers.forEach((player, list) -> {
CompoundTag nbt = new CompoundTag();
for (DataLogger.Type type : list) {
Tag data = DATA.get(type, player);
List<Tag> data0 = DATA.getZ(type, player);
if (data0.isEmpty()) continue;
Tag data = data0.getFirst();
if (data != null) {
nbt.put(type.getSerializedName(), data);
DATA.remove(type, player);
DATA.remove(type, player, data);
}
}
if (!nbt.isEmpty()) {
@@ -372,4 +375,4 @@ public class ServuxHudDataProtocol implements LeavesProtocol {
this(type, new CompoundTag(), buffer);
}
}
}
}
@@ -28,6 +28,7 @@ import net.minecraft.server.level.ServerPlayer;
import net.minecraft.world.level.block.Mirror;
import net.minecraft.world.level.block.Rotation;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.leavesmc.leaves.protocol.core.LeavesProtocol;
import org.leavesmc.leaves.protocol.core.ProtocolHandler;
import org.leavesmc.leaves.protocol.syncmatica.exchange.*;
@@ -38,13 +39,16 @@ import java.io.IOException;
import java.security.NoSuchAlgorithmException;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentLinkedQueue;
import static fun.bm.lophine.LophineLogger.LOGGER;
@LeavesProtocol.Register(namespace = "syncmatica")
public class CommunicationManager implements LeavesProtocol {
protected static final Collection<ExchangeTarget> broadcastTargets = new ArrayList<>();
protected static final Map<UUID, Boolean> downloadState = new HashMap<>();
protected static final Map<UUID, Exchange> modifyState = new HashMap<>();
protected static final Collection<ExchangeTarget> broadcastTargets = new ConcurrentLinkedQueue<>();
protected static final Map<UUID, Boolean> downloadState = new ConcurrentHashMap<>();
protected static final Map<UUID, Exchange> modifyState = new ConcurrentHashMap<>();
protected static final Rotation[] rotOrdinals = Rotation.values();
protected static final Mirror[] mirOrdinals = Mirror.values();
private static final Map<UUID, List<ServerPlacement>> downloadingFile = new ConcurrentHashMap<>();
@@ -177,6 +181,10 @@ public class CommunicationManager implements LeavesProtocol {
if (id.equals(PacketType.MODIFY_REQUEST.identifier)) {
final UUID placementId = packetBuf.readUUID();
final ModifyExchangeServer modifier = new ModifyExchangeServer(placementId, source);
if (modifier.getPlacement() == null) {
LOGGER.warn("Could not find placement for modify request {}", placementId);
return;
}
startExchange(modifier);
}
}
@@ -350,8 +358,12 @@ public class CommunicationManager implements LeavesProtocol {
return downloadState.getOrDefault(syncmatic.getHash(), false);
}
public static void setModifier(final @NotNull ServerPlacement syncmatic, final Exchange exchange) {
modifyState.put(syncmatic.getHash(), exchange);
public static void setModifier(final @NotNull ServerPlacement syncmatic, final @Nullable Exchange exchange) {
if (exchange == null) {
modifyState.remove(syncmatic.getHash());
} else {
modifyState.put(syncmatic.getHash(), exchange);
}
}
public static Exchange getModifier(final @NotNull ServerPlacement syncmatic) {
@@ -39,7 +39,6 @@ import org.leavesmc.leaves.entity.photographer.Photographer;
import java.io.File;
import java.io.IOException;
import java.util.List;
import java.util.Objects;
import java.util.UUID;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.CopyOnWriteArrayList;
+7
View File
@@ -1,10 +1,17 @@
pluginManagement {
val weightVersion: String by settings
repositories {
gradlePluginPortal()
mavenLocal()
maven("https://repo.papermc.io/repository/maven-public/")
maven("https://repo.menthamc.org/repository/maven-public/")
}
plugins {
id("moe.luminolmc.hyacinthusweight.patcher") version weightVersion
id("moe.luminolmc.hyacinthusweight.core") version weightVersion
}
}
plugins {