Compare commits

...

15 Commits

Author SHA1 Message Date
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
47 changed files with 2834 additions and 108 deletions
+1 -1
View File
@@ -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 -1
View File
@@ -2,10 +2,12 @@ group=fun.bm.lophine
version=1.21.11-R0.1-SNAPSHOT
mcVersion=1.21.11
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=e5ff8dff2fcfb6a8900bd87ed40ae9d4102c0c75
luminolRef=4553d0974982c54663b47c996ae5383371ff2db3
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
@@ -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
@@ -41,7 +41,7 @@ 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 3acd8d2226d4a7ba8fcecc1cc715b01cc9ce63d1..cc82414e813fe0a97bfd405096d06ad279098479 100644
index 5c5dfcefd9ce6c9d7f69fb6d03216805b9e63822..d35ef52a6adb9a560138d1a2af32969c3a73f42b 100644
--- a/net/minecraft/server/network/ServerGamePacketListenerImpl.java
+++ b/net/minecraft/server/network/ServerGamePacketListenerImpl.java
@@ -3170,7 +3170,7 @@ public class ServerGamePacketListenerImpl
@@ -446,7 +446,7 @@ 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 53c50d776e5ca66e5903a464d763a7df5b7672be..6cdb59762b5b223a220d420ff99fa9eadc64f3e0 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
@@ -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
@@ -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
@@ -22,7 +22,7 @@ index 7c2d53ca863c2e50c6871938efc426ddefff9b5c..8c1e6837b0328c66fdda322c3c7ceba3
// Luminol End - Cross Region Damage trace
}
diff --git a/net/minecraft/server/network/ServerGamePacketListenerImpl.java b/net/minecraft/server/network/ServerGamePacketListenerImpl.java
index 3aa7e5fb13326bd3667d470adbc7df7b7e9b19b4..6de8c1010b57aaeab0c97e2836cee5bf4b087578 100644
index df66e24c61af9d57d619c91849e28f1bec64a036..8386757c5fd3d8df3ae77a4eeeb25a2f28840cda 100644
--- a/net/minecraft/server/network/ServerGamePacketListenerImpl.java
+++ b/net/minecraft/server/network/ServerGamePacketListenerImpl.java
@@ -3843,7 +3843,7 @@ public class ServerGamePacketListenerImpl
@@ -10,7 +10,7 @@ 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 6cdb59762b5b223a220d420ff99fa9eadc64f3e0..fe93f0d6b2b7e8503da068179f5d7667634c7ff3 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
@@ -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
@@ -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 6de3e60d63a838cf9d24822cbbf1cd2b445e6b4d..782f6810d4ed99052169c7099318dc8a2f8937b0 100644
index 33d186d660ce3e3dc8cf26cd521fa99291ff78a5..ce1918d12b88835ea79a5c0dc90e0315c408830b 100644
--- a/io/papermc/paper/threadedregions/RegionizedWorldData.java
+++ b/io/papermc/paper/threadedregions/RegionizedWorldData.java
@@ -482,7 +482,7 @@ public final class RegionizedWorldData {
@@ -494,7 +494,7 @@ public final class RegionizedWorldData {
public long lastMidTickExecuteFailure;
// From Level
public boolean populating;
@@ -17,7 +17,7 @@ index 6de3e60d63a838cf9d24822cbbf1cd2b445e6b4d..782f6810d4ed99052169c7099318dc8a
public boolean captureBlockStates = false;
public boolean captureTreeGeneration = false;
public final Map<BlockPos, CraftBlockState> capturedBlockStates = new java.util.LinkedHashMap<>(); // Paper
@@ -570,7 +570,7 @@ public final class RegionizedWorldData {
@@ -582,7 +582,7 @@ public final class RegionizedWorldData {
this.world = world;
this.blockLevelTicks = new LevelTicks<>(world::isPositionTickingWithEntitiesLoaded, world, true);
this.fluidLevelTicks = new LevelTicks<>(world::isPositionTickingWithEntitiesLoaded, world, false);
@@ -5,10 +5,10 @@ Subject: [PATCH] Global Entities Counter
diff --git a/io/papermc/paper/threadedregions/RegionizedWorldData.java b/io/papermc/paper/threadedregions/RegionizedWorldData.java
index 782f6810d4ed99052169c7099318dc8a2f8937b0..e166c430938eb22bb90d651298562d704fa8f1d9 100644
index ce1918d12b88835ea79a5c0dc90e0315c408830b..c5caad99caed20e2bb4475fcf057417ee27d737c 100644
--- a/io/papermc/paper/threadedregions/RegionizedWorldData.java
+++ b/io/papermc/paper/threadedregions/RegionizedWorldData.java
@@ -564,6 +564,9 @@ public final class RegionizedWorldData {
@@ -576,6 +576,9 @@ public final class RegionizedWorldData {
}
// Luminol end
@@ -18,7 +18,7 @@ index 782f6810d4ed99052169c7099318dc8a2f8937b0..e166c430938eb22bb90d651298562d70
public final TickRegions.TickRegionData regionData;
public RegionizedWorldData(final ServerLevel world, final TickRegions.TickRegionData regionData) {
@@ -576,6 +579,15 @@ public final class RegionizedWorldData {
@@ -588,6 +591,15 @@ public final class RegionizedWorldData {
this.turbo = new io.papermc.paper.redstone.RedstoneWireTurbo((RedStoneWireBlock)Blocks.REDSTONE_WIRE);
this.regionData = regionData;
@@ -112,6 +112,55 @@ index d8a2f8a1fbf0189e86ccbce70a84641a143cdb91..76930a603a5bf07e62e8ef3a41bbb521
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 e166c430938eb22bb90d651298562d704fa8f1d9..bb6e46644f23fcf3d785f20003f01ea22ee75430 100644
index c5caad99caed20e2bb4475fcf057417ee27d737c..c2e50a03a41958d9b44753adc9a9bf4ea1fb24a4 100644
--- a/io/papermc/paper/threadedregions/RegionizedWorldData.java
+++ b/io/papermc/paper/threadedregions/RegionizedWorldData.java
@@ -239,14 +239,17 @@ public final class RegionizedWorldData {
@@ -251,14 +251,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 e166c430938eb22bb90d651298562d704fa8f1d9..bb6e46644f23fcf3d785f20003f01ea2
}
// entities
for (final ServerPlayer player : from.localPlayers) {
@@ -257,6 +260,13 @@ public final class RegionizedWorldData {
@@ -269,6 +272,13 @@ public final class RegionizedWorldData {
into.localPlayers.add(player);
player.getBukkitEntity().updateRegion(into);
into.nearbyPlayers.addPlayer(player);
@@ -41,7 +41,7 @@ index e166c430938eb22bb90d651298562d704fa8f1d9..bb6e46644f23fcf3d785f20003f01ea2
}
for (final Entity entity : from.allEntities) {
final ChunkPos pos = entity.chunkPosition();
@@ -624,6 +634,7 @@ public final class RegionizedWorldData {
@@ -636,6 +646,7 @@ public final class RegionizedWorldData {
private static void cleanUpConnection(final Connection conn) {
// note: ALL connections HERE have a player
final ServerPlayer player = conn.getPlayer();
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,112 @@
package fun.bm.lophine.carpet;
import com.mojang.logging.LogUtils;
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<ServerLevel, Map<EntityType<?>, Mob>> PRECOOKED_MOBS = new WeakHashMap<>();
public static boolean hasNoCollision(ServerLevel world, AABB 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) {
Map<EntityType<?>, Mob> cache = PRECOOKED_MOBS.computeIfAbsent(level, key -> new HashMap<>());
Mob mob = cache.get(entityType);
if (mob != null && !mob.isRemoved()) {
return mob;
}
try {
if (entityType.create(level, EntitySpawnReason.NATURAL) instanceof Mob created) {
cache.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);
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);
}
}
@@ -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;
}
@@ -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,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();
}
@@ -30,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);
@@ -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() {
@@ -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);
}
@@ -1,7 +1,6 @@
package org.leavesmc.leaves.protocol;
import com.mojang.logging.LogUtils;
import fun.bm.lophine.config.modules.function.CreativeFlyNoClipConfig;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.network.FriendlyByteBuf;
import net.minecraft.network.codec.ByteBufCodecs;
@@ -54,8 +53,8 @@ public class CarpetServerProtocol implements LeavesProtocol {
@Override
public boolean isActive() {
return CreativeFlyNoClipConfig.enabled;
} // re-edit for creative no clip
return CarpetRules.hasRules();
}
public static class CarpetRules {
@@ -71,6 +70,14 @@ public class CarpetServerProtocol implements LeavesProtocol {
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) {
@@ -87,6 +94,24 @@ public class CarpetServerProtocol implements LeavesProtocol {
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;
@@ -19,6 +19,7 @@ package org.leavesmc.leaves.protocol.servux;
import com.google.common.collect.HashBasedTable;
import com.google.common.collect.Table;
import com.google.common.collect.Tables;
import com.mojang.serialization.DataResult;
import fun.bm.lophine.config.modules.function.protocol.ServuxProtocolConfig;
import io.netty.buffer.Unpooled;
@@ -53,12 +54,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 Table<DataLogger.Type, ServerPlayer, Tag> DATA = Tables.synchronizedTable(HashBasedTable.create());
public static boolean refreshSpawnMetadata = false;
@@ -84,6 +85,7 @@ public class ServuxHudDataProtocol implements LeavesProtocol {
private static void onPlayerLeave(ServerPlayer player) {
players.remove(player);
loggerPlayers.remove(player);
DATA.rowMap().values().forEach(row -> row.remove(player));
}
@ProtocolHandler.PayloadReceiver(payload = HudDataPayload.class)
@@ -372,4 +374,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.*;
@@ -40,6 +41,8 @@ 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 {
@@ -178,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);
}
}
@@ -351,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) {
+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 {