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>
This commit is contained in:
@@ -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` 动作 |
|
||||||
@@ -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();
|
||||||
|
}
|
||||||
|
|
||||||
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,135 @@
|
|||||||
|
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.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();
|
||||||
|
registerProtocolRules();
|
||||||
|
init = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void applyGeneralRules() {
|
||||||
|
LanguageConfig.lang = GeneralCompatConfig.language;
|
||||||
|
UpdateSuppressionCrashFixConfig.enabled = GeneralCompatConfig.amsUpdateSuppressionCrashFix || GeneralCompatConfig.yeetUpdateSuppressionCrash;
|
||||||
|
fun.bm.lophine.config.modules.experiment.RedStoneConfig.redstoneIgnoreUpwardsUpdate = GeneralCompatConfig.dustTrapdoorReintroduced;
|
||||||
|
fun.bm.lophine.config.modules.experiment.RedStoneConfig.cce = GeneralCompatConfig.shulkerBoxCCEReintroduced;
|
||||||
|
fun.bm.lophine.config.modules.experiment.RedStoneConfig.instantBlockUpdater = GeneralCompatConfig.instantBlockUpdaterReintroduced;
|
||||||
|
CommandConfig.tick = GeneralCompatConfig.commandTick;
|
||||||
|
CreativeFlyNoClipConfig.enabled = GeneralCompatConfig.creativeNoClip;
|
||||||
|
OptimizedDragonRespawnConfig.optimizedRespawn = GeneralCompatConfig.optimizedDragonRespawn;
|
||||||
|
CarpetLoggerProtocol.refreshConfiguredDefaults(!init);
|
||||||
|
}
|
||||||
|
|
||||||
|
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,28 @@
|
|||||||
|
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 any function in general directory from Carpet modifier,
|
||||||
|
you need to enable it.
|
||||||
|
|
||||||
|
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();
|
||||||
|
}
|
||||||
|
}
|
||||||
+24
@@ -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;
|
||||||
|
}
|
||||||
+64
@@ -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;
|
||||||
|
}
|
||||||
+239
@@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
-1
@@ -81,7 +81,6 @@ public class FakeplayerConfig implements IConfigModule {
|
|||||||
@ConfigInfo(name = "enable-locator-bar", comments = """
|
@ConfigInfo(name = "enable-locator-bar", comments = """
|
||||||
Enable locator bar for fakeplayers""")
|
Enable locator bar for fakeplayers""")
|
||||||
public static boolean enableLocatorBar = false;
|
public static boolean enableLocatorBar = false;
|
||||||
|
|
||||||
public static ServerBot.TickType tickType = ServerBot.TickType.ENTITY_LIST;
|
public static ServerBot.TickType tickType = ServerBot.TickType.ENTITY_LIST;
|
||||||
|
|
||||||
private BotCommand command = null;
|
private BotCommand command = null;
|
||||||
|
|||||||
@@ -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
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -18,11 +18,15 @@
|
|||||||
package org.leavesmc.leaves.bot;
|
package org.leavesmc.leaves.bot;
|
||||||
|
|
||||||
import com.google.common.base.Charsets;
|
import com.google.common.base.Charsets;
|
||||||
|
import fun.bm.lophine.carpet.config.modules.FakePlayerCompatConfig;
|
||||||
import fun.bm.lophine.config.modules.function.FakeplayerConfig;
|
import fun.bm.lophine.config.modules.function.FakeplayerConfig;
|
||||||
import net.minecraft.core.NonNullList;
|
import net.minecraft.core.NonNullList;
|
||||||
|
import net.minecraft.core.component.DataComponents;
|
||||||
import net.minecraft.nbt.CompoundTag;
|
import net.minecraft.nbt.CompoundTag;
|
||||||
|
import net.minecraft.tags.ItemTags;
|
||||||
import net.minecraft.world.entity.EquipmentSlot;
|
import net.minecraft.world.entity.EquipmentSlot;
|
||||||
import net.minecraft.world.item.ItemStack;
|
import net.minecraft.world.item.ItemStack;
|
||||||
|
import net.minecraft.world.item.component.ItemContainerContents;
|
||||||
import org.bukkit.Bukkit;
|
import org.bukkit.Bukkit;
|
||||||
import org.jetbrains.annotations.NotNull;
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
|
||||||
@@ -34,23 +38,66 @@ public class BotUtil {
|
|||||||
public static void replenishment(@NotNull ItemStack itemStack, NonNullList<ItemStack> itemStackList) {
|
public static void replenishment(@NotNull ItemStack itemStack, NonNullList<ItemStack> itemStackList) {
|
||||||
int count = itemStack.getMaxStackSize() / 2;
|
int count = itemStack.getMaxStackSize() / 2;
|
||||||
if (itemStack.getCount() <= 8 && count > 8) {
|
if (itemStack.getCount() <= 8 && count > 8) {
|
||||||
for (ItemStack itemStack1 : itemStackList) {
|
if (pullMatchingStack(itemStack, itemStackList, count)) {
|
||||||
if (itemStack1 == ItemStack.EMPTY || itemStack1 == itemStack) {
|
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;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ItemStack.isSameItemSameComponents(itemStack1, itemStack)) {
|
int moved = Math.min(Math.min(transferLimit, shulkerStack.getCount()), targetStack.getMaxStackSize() - targetStack.getCount());
|
||||||
if (itemStack1.getCount() > count) {
|
if (moved <= 0) {
|
||||||
itemStack.setCount(itemStack.getCount() + count);
|
return false;
|
||||||
itemStack1.setCount(itemStack1.getCount() - count);
|
|
||||||
} else {
|
|
||||||
itemStack.setCount(itemStack.getCount() + itemStack1.getCount());
|
|
||||||
itemStack1.setCount(0);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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) {
|
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.google.common.collect.ImmutableMap;
|
||||||
import com.mojang.authlib.GameProfile;
|
import com.mojang.authlib.GameProfile;
|
||||||
import fun.bm.lophine.LophineLogger;
|
import fun.bm.lophine.LophineLogger;
|
||||||
|
import fun.bm.lophine.carpet.config.modules.FakePlayerCompatConfig;
|
||||||
import fun.bm.lophine.config.modules.function.FakeplayerConfig;
|
import fun.bm.lophine.config.modules.function.FakeplayerConfig;
|
||||||
import io.papermc.paper.adventure.PaperAdventure;
|
import io.papermc.paper.adventure.PaperAdventure;
|
||||||
import io.papermc.paper.event.entity.EntityKnockbackEvent;
|
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.item.ItemEntity;
|
||||||
import net.minecraft.world.entity.player.Input;
|
import net.minecraft.world.entity.player.Input;
|
||||||
import net.minecraft.world.entity.player.Player;
|
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.projectile.ProjectileUtil;
|
||||||
import net.minecraft.world.entity.vehicle.boat.AbstractBoat;
|
import net.minecraft.world.entity.vehicle.boat.AbstractBoat;
|
||||||
import net.minecraft.world.inventory.ChestMenu;
|
import net.minecraft.world.inventory.ChestMenu;
|
||||||
|
import net.minecraft.world.item.FishingRodItem;
|
||||||
import net.minecraft.world.item.ItemStack;
|
import net.minecraft.world.item.ItemStack;
|
||||||
import net.minecraft.world.item.crafting.RecipeHolder;
|
import net.minecraft.world.item.crafting.RecipeHolder;
|
||||||
import net.minecraft.world.level.gameevent.GameEvent;
|
import net.minecraft.world.level.gameevent.GameEvent;
|
||||||
@@ -84,6 +87,8 @@ import java.util.function.Predicate;
|
|||||||
import static net.minecraft.server.MinecraftServer.getServer;
|
import static net.minecraft.server.MinecraftServer.getServer;
|
||||||
|
|
||||||
public class ServerBot extends ServerPlayer {
|
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 List<AbstractBotAction<?>> actions;
|
||||||
private final Map<String, AbstractBotConfig<?, ?>> configs;
|
private final Map<String, AbstractBotConfig<?, ?>> configs;
|
||||||
@@ -100,6 +105,7 @@ public class ServerBot extends ServerPlayer {
|
|||||||
public int notSleepTicks;
|
public int notSleepTicks;
|
||||||
|
|
||||||
public int removeTaskId = -1;
|
public int removeTaskId = -1;
|
||||||
|
private int autoFishCooldown = 0;
|
||||||
|
|
||||||
public ServerBot(MinecraftServer server, ServerLevel world, GameProfile profile) {
|
public ServerBot(MinecraftServer server, ServerLevel world, GameProfile profile) {
|
||||||
super(server, world, profile, ClientInformation.createDefault());
|
super(server, world, profile, ClientInformation.createDefault());
|
||||||
@@ -237,6 +243,7 @@ public class ServerBot extends ServerPlayer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
this.getCooldowns().tick();
|
this.getCooldowns().tick();
|
||||||
|
this.tickAutoFish();
|
||||||
this.updatePlayerPose();
|
this.updatePlayerPose();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -339,8 +346,10 @@ public class ServerBot extends ServerPlayer {
|
|||||||
ItemStack item = this.getItemInHand(hand);
|
ItemStack item = this.getItemInHand(hand);
|
||||||
|
|
||||||
if (!item.isEmpty()) {
|
if (!item.isEmpty()) {
|
||||||
BotUtil.replenishment(item, getInventory().getNonEquipmentItems());
|
if (FakePlayerCompatConfig.fakePlayerAutoReplenishment) {
|
||||||
if (BotUtil.isDamage(item, 10)) {
|
BotUtil.replenishment(item, getInventory().getNonEquipmentItems());
|
||||||
|
}
|
||||||
|
if (FakePlayerCompatConfig.fakePlayerAutoReplaceTool && BotUtil.isDamage(item, 10)) {
|
||||||
BotUtil.replaceTool(hand == InteractionHand.MAIN_HAND ? EquipmentSlot.MAINHAND : EquipmentSlot.OFFHAND, this);
|
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);
|
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);
|
ValueOutput.TypedOutputList<CompoundTag> actionNbt = nbt.list("actions", CompoundTag.CODEC);
|
||||||
for (AbstractBotAction<?> action : this.actions) {
|
for (AbstractBotAction<?> action : this.actions) {
|
||||||
actionNbt.add(action.save(new CompoundTag()));
|
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());
|
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();
|
ValueInput.TypedInputList<CompoundTag> actionNbt = nbt.list("actions", CompoundTag.CODEC).orElseThrow();
|
||||||
actionNbt.forEach(actionTag -> {
|
actionNbt.forEach(actionTag -> {
|
||||||
AbstractBotAction<?> action = Actions.getForName(actionTag.getString("actionName").orElseThrow());
|
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) {
|
public boolean addBotAction(AbstractBotAction<?> action, CommandSender sender) {
|
||||||
if (!FakeplayerConfig.canUseAction) {
|
if (!FakeplayerConfig.canUseAction) {
|
||||||
return false;
|
return false;
|
||||||
|
|||||||
@@ -17,8 +17,10 @@
|
|||||||
|
|
||||||
package org.leavesmc.leaves.bot;
|
package org.leavesmc.leaves.bot;
|
||||||
|
|
||||||
|
import fun.bm.lophine.carpet.config.modules.FakePlayerCompatConfig;
|
||||||
import net.kyori.adventure.text.Component;
|
import net.kyori.adventure.text.Component;
|
||||||
import net.minecraft.core.BlockPos;
|
import net.minecraft.core.BlockPos;
|
||||||
|
import net.minecraft.server.MinecraftServer;
|
||||||
import net.minecraft.server.level.ServerPlayer;
|
import net.minecraft.server.level.ServerPlayer;
|
||||||
import net.minecraft.server.level.ServerPlayerGameMode;
|
import net.minecraft.server.level.ServerPlayerGameMode;
|
||||||
import net.minecraft.world.InteractionHand;
|
import net.minecraft.world.InteractionHand;
|
||||||
@@ -40,7 +42,7 @@ public class ServerBotGameMode extends ServerPlayerGameMode {
|
|||||||
|
|
||||||
public ServerBotGameMode(ServerBot bot) {
|
public ServerBotGameMode(ServerBot bot) {
|
||||||
super(bot);
|
super(bot);
|
||||||
super.setGameModeForPlayer(GameType.SURVIVAL, null);
|
super.setGameModeForPlayer(getInitialGameMode(bot), null);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -133,4 +135,8 @@ public class ServerBotGameMode extends ServerPlayerGameMode {
|
|||||||
return InteractionResult.PASS;
|
return InteractionResult.PASS;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static GameType getInitialGameMode(ServerBot bot) {
|
||||||
|
return FakePlayerCompatConfig.fakePlayerDefaultSurvivalMode ? GameType.SURVIVAL : MinecraftServer.getServer().getDefaultGameType();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+10
@@ -17,9 +17,12 @@
|
|||||||
|
|
||||||
package org.leavesmc.leaves.bot.agent.actions;
|
package org.leavesmc.leaves.bot.agent.actions;
|
||||||
|
|
||||||
|
import fun.bm.lophine.carpet.config.modules.FakePlayerCompatConfig;
|
||||||
import net.minecraft.world.InteractionHand;
|
import net.minecraft.world.InteractionHand;
|
||||||
import net.minecraft.world.InteractionResult;
|
import net.minecraft.world.InteractionResult;
|
||||||
import net.minecraft.world.entity.Entity;
|
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.EntityHitResult;
|
||||||
import net.minecraft.world.phys.Vec3;
|
import net.minecraft.world.phys.Vec3;
|
||||||
import org.jetbrains.annotations.NotNull;
|
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());
|
Vec3 vec3 = hitResult.getLocation().subtract(entity.getX(), entity.getY(), entity.getZ());
|
||||||
bot.updateItemInHand(hand);
|
bot.updateItemInHand(hand);
|
||||||
InteractionResult interactionResult = entity.interactAt(bot, vec3, 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()) {
|
if (!interactionResult.consumesAction()) {
|
||||||
interactionResult = bot.interactOn(hitResult.getEntity(), hand);
|
interactionResult = bot.interactOn(hitResult.getEntity(), hand);
|
||||||
}
|
}
|
||||||
|
|||||||
+28
-3
@@ -1,7 +1,6 @@
|
|||||||
package org.leavesmc.leaves.protocol;
|
package org.leavesmc.leaves.protocol;
|
||||||
|
|
||||||
import com.mojang.logging.LogUtils;
|
import com.mojang.logging.LogUtils;
|
||||||
import fun.bm.lophine.config.modules.function.CreativeFlyNoClipConfig;
|
|
||||||
import net.minecraft.nbt.CompoundTag;
|
import net.minecraft.nbt.CompoundTag;
|
||||||
import net.minecraft.network.FriendlyByteBuf;
|
import net.minecraft.network.FriendlyByteBuf;
|
||||||
import net.minecraft.network.codec.ByteBufCodecs;
|
import net.minecraft.network.codec.ByteBufCodecs;
|
||||||
@@ -54,8 +53,8 @@ public class CarpetServerProtocol implements LeavesProtocol {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean isActive() {
|
public boolean isActive() {
|
||||||
return CreativeFlyNoClipConfig.enabled;
|
return CarpetRules.hasRules();
|
||||||
} // re-edit for creative no clip
|
}
|
||||||
|
|
||||||
public static class CarpetRules {
|
public static class CarpetRules {
|
||||||
|
|
||||||
@@ -71,6 +70,14 @@ public class CarpetServerProtocol implements LeavesProtocol {
|
|||||||
public static void register(CarpetRule rule) {
|
public static void register(CarpetRule rule) {
|
||||||
rules.put(rule.name, 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) {
|
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));
|
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) {
|
public void writeNBT(@NotNull CompoundTag rules) {
|
||||||
CompoundTag rule = new CompoundTag();
|
CompoundTag rule = new CompoundTag();
|
||||||
String key = name;
|
String key = name;
|
||||||
|
|||||||
+1
@@ -84,6 +84,7 @@ public class ServuxHudDataProtocol implements LeavesProtocol {
|
|||||||
private static void onPlayerLeave(ServerPlayer player) {
|
private static void onPlayerLeave(ServerPlayer player) {
|
||||||
players.remove(player);
|
players.remove(player);
|
||||||
loggerPlayers.remove(player);
|
loggerPlayers.remove(player);
|
||||||
|
DATA.rowMap().values().forEach(row -> row.remove(player));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ProtocolHandler.PayloadReceiver(payload = HudDataPayload.class)
|
@ProtocolHandler.PayloadReceiver(payload = HudDataPayload.class)
|
||||||
|
|||||||
Reference in New Issue
Block a user