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,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 = """
|
||||
Enable locator bar for fakeplayers""")
|
||||
public static boolean enableLocatorBar = false;
|
||||
|
||||
public static ServerBot.TickType tickType = ServerBot.TickType.ENTITY_LIST;
|
||||
|
||||
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;
|
||||
|
||||
import com.google.common.base.Charsets;
|
||||
import fun.bm.lophine.carpet.config.modules.FakePlayerCompatConfig;
|
||||
import fun.bm.lophine.config.modules.function.FakeplayerConfig;
|
||||
import net.minecraft.core.NonNullList;
|
||||
import net.minecraft.core.component.DataComponents;
|
||||
import net.minecraft.nbt.CompoundTag;
|
||||
import net.minecraft.tags.ItemTags;
|
||||
import net.minecraft.world.entity.EquipmentSlot;
|
||||
import net.minecraft.world.item.ItemStack;
|
||||
import net.minecraft.world.item.component.ItemContainerContents;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
@@ -34,23 +38,66 @@ public class BotUtil {
|
||||
public static void replenishment(@NotNull ItemStack itemStack, NonNullList<ItemStack> itemStackList) {
|
||||
int count = itemStack.getMaxStackSize() / 2;
|
||||
if (itemStack.getCount() <= 8 && count > 8) {
|
||||
for (ItemStack itemStack1 : itemStackList) {
|
||||
if (itemStack1 == ItemStack.EMPTY || itemStack1 == itemStack) {
|
||||
if (pullMatchingStack(itemStack, itemStackList, count)) {
|
||||
return;
|
||||
}
|
||||
if (FakePlayerCompatConfig.fakePlayerAutoReplenishmentFormShulkerBox) {
|
||||
pullMatchingStackFromShulkerBox(itemStack, itemStackList, count);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static boolean pullMatchingStack(@NotNull ItemStack targetStack, NonNullList<ItemStack> itemStackList, int transferLimit) {
|
||||
for (ItemStack inventoryStack : itemStackList) {
|
||||
if (inventoryStack == ItemStack.EMPTY || inventoryStack == targetStack) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (ItemStack.isSameItemSameComponents(inventoryStack, targetStack)) {
|
||||
if (inventoryStack.getCount() > transferLimit) {
|
||||
targetStack.setCount(targetStack.getCount() + transferLimit);
|
||||
inventoryStack.setCount(inventoryStack.getCount() - transferLimit);
|
||||
} else {
|
||||
targetStack.setCount(targetStack.getCount() + inventoryStack.getCount());
|
||||
inventoryStack.setCount(0);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private static boolean pullMatchingStackFromShulkerBox(@NotNull ItemStack targetStack, NonNullList<ItemStack> itemStackList, int transferLimit) {
|
||||
for (ItemStack containerStack : itemStackList) {
|
||||
if (containerStack == ItemStack.EMPTY || !containerStack.is(ItemTags.SHULKER_BOXES)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
ItemContainerContents contents = containerStack.getOrDefault(DataComponents.CONTAINER, ItemContainerContents.EMPTY);
|
||||
NonNullList<ItemStack> shulkerItems = NonNullList.withSize(contents.items.size(), ItemStack.EMPTY);
|
||||
contents.copyInto(shulkerItems);
|
||||
|
||||
for (int slot = 0; slot < shulkerItems.size(); slot++) {
|
||||
ItemStack shulkerStack = shulkerItems.get(slot);
|
||||
if (shulkerStack.isEmpty() || !ItemStack.isSameItemSameComponents(shulkerStack, targetStack)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (ItemStack.isSameItemSameComponents(itemStack1, itemStack)) {
|
||||
if (itemStack1.getCount() > count) {
|
||||
itemStack.setCount(itemStack.getCount() + count);
|
||||
itemStack1.setCount(itemStack1.getCount() - count);
|
||||
} else {
|
||||
itemStack.setCount(itemStack.getCount() + itemStack1.getCount());
|
||||
itemStack1.setCount(0);
|
||||
}
|
||||
break;
|
||||
int moved = Math.min(Math.min(transferLimit, shulkerStack.getCount()), targetStack.getMaxStackSize() - targetStack.getCount());
|
||||
if (moved <= 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
targetStack.grow(moved);
|
||||
shulkerStack.shrink(moved);
|
||||
shulkerItems.set(slot, shulkerStack.isEmpty() ? ItemStack.EMPTY : shulkerStack);
|
||||
containerStack.set(DataComponents.CONTAINER, ItemContainerContents.fromItems(shulkerItems));
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public static void replaceTool(@NotNull EquipmentSlot slot, @NotNull ServerBot bot) {
|
||||
|
||||
@@ -20,6 +20,7 @@ package org.leavesmc.leaves.bot;
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
import com.mojang.authlib.GameProfile;
|
||||
import fun.bm.lophine.LophineLogger;
|
||||
import fun.bm.lophine.carpet.config.modules.FakePlayerCompatConfig;
|
||||
import fun.bm.lophine.config.modules.function.FakeplayerConfig;
|
||||
import io.papermc.paper.adventure.PaperAdventure;
|
||||
import io.papermc.paper.event.entity.EntityKnockbackEvent;
|
||||
@@ -52,9 +53,11 @@ import net.minecraft.world.entity.PositionMoveRotation;
|
||||
import net.minecraft.world.entity.item.ItemEntity;
|
||||
import net.minecraft.world.entity.player.Input;
|
||||
import net.minecraft.world.entity.player.Player;
|
||||
import net.minecraft.world.entity.projectile.FishingHook;
|
||||
import net.minecraft.world.entity.projectile.ProjectileUtil;
|
||||
import net.minecraft.world.entity.vehicle.boat.AbstractBoat;
|
||||
import net.minecraft.world.inventory.ChestMenu;
|
||||
import net.minecraft.world.item.FishingRodItem;
|
||||
import net.minecraft.world.item.ItemStack;
|
||||
import net.minecraft.world.item.crafting.RecipeHolder;
|
||||
import net.minecraft.world.level.gameevent.GameEvent;
|
||||
@@ -84,6 +87,8 @@ import java.util.function.Predicate;
|
||||
import static net.minecraft.server.MinecraftServer.getServer;
|
||||
|
||||
public class ServerBot extends ServerPlayer {
|
||||
private static final int AUTO_FISH_RECAST_DELAY = 4;
|
||||
private static final int AUTO_FISH_ENTITY_DELAY = 20;
|
||||
|
||||
private final List<AbstractBotAction<?>> actions;
|
||||
private final Map<String, AbstractBotConfig<?, ?>> configs;
|
||||
@@ -100,6 +105,7 @@ public class ServerBot extends ServerPlayer {
|
||||
public int notSleepTicks;
|
||||
|
||||
public int removeTaskId = -1;
|
||||
private int autoFishCooldown = 0;
|
||||
|
||||
public ServerBot(MinecraftServer server, ServerLevel world, GameProfile profile) {
|
||||
super(server, world, profile, ClientInformation.createDefault());
|
||||
@@ -237,6 +243,7 @@ public class ServerBot extends ServerPlayer {
|
||||
}
|
||||
|
||||
this.getCooldowns().tick();
|
||||
this.tickAutoFish();
|
||||
this.updatePlayerPose();
|
||||
}
|
||||
|
||||
@@ -339,8 +346,10 @@ public class ServerBot extends ServerPlayer {
|
||||
ItemStack item = this.getItemInHand(hand);
|
||||
|
||||
if (!item.isEmpty()) {
|
||||
BotUtil.replenishment(item, getInventory().getNonEquipmentItems());
|
||||
if (BotUtil.isDamage(item, 10)) {
|
||||
if (FakePlayerCompatConfig.fakePlayerAutoReplenishment) {
|
||||
BotUtil.replenishment(item, getInventory().getNonEquipmentItems());
|
||||
}
|
||||
if (FakePlayerCompatConfig.fakePlayerAutoReplaceTool && BotUtil.isDamage(item, 10)) {
|
||||
BotUtil.replaceTool(hand == InteractionHand.MAIN_HAND ? EquipmentSlot.MAINHAND : EquipmentSlot.OFFHAND, this);
|
||||
}
|
||||
}
|
||||
@@ -388,7 +397,7 @@ public class ServerBot extends ServerPlayer {
|
||||
|
||||
nbt.store("createStatus", CompoundTag.CODEC, createNbt);
|
||||
|
||||
if (!this.actions.isEmpty()) {
|
||||
if (FakePlayerCompatConfig.fakePlayerReloadAction && !this.actions.isEmpty()) {
|
||||
ValueOutput.TypedOutputList<CompoundTag> actionNbt = nbt.list("actions", CompoundTag.CODEC);
|
||||
for (AbstractBotAction<?> action : this.actions) {
|
||||
actionNbt.add(action.save(new CompoundTag()));
|
||||
@@ -431,7 +440,7 @@ public class ServerBot extends ServerPlayer {
|
||||
this.gameProfile = BotList.createBotProfile(this.getUUID(), this.createState.fullName(), this.createState.skin());
|
||||
|
||||
|
||||
if (nbt.list("actions", CompoundTag.CODEC).isPresent()) {
|
||||
if (FakePlayerCompatConfig.fakePlayerReloadAction && nbt.list("actions", CompoundTag.CODEC).isPresent()) {
|
||||
ValueInput.TypedInputList<CompoundTag> actionNbt = nbt.list("actions", CompoundTag.CODEC).orElseThrow();
|
||||
actionNbt.forEach(actionTag -> {
|
||||
AbstractBotAction<?> action = Actions.getForName(actionTag.getString("actionName").orElseThrow());
|
||||
@@ -656,6 +665,45 @@ public class ServerBot extends ServerPlayer {
|
||||
}
|
||||
}
|
||||
|
||||
private void tickAutoFish() {
|
||||
if (!fun.bm.lophine.carpet.config.modules.FakePlayerCompatConfig.fakePlayerAutoFish || this.hasActiveAction("fish")) {
|
||||
this.autoFishCooldown = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
if (this.autoFishCooldown > 0) {
|
||||
this.autoFishCooldown--;
|
||||
return;
|
||||
}
|
||||
|
||||
ItemStack mainHand = this.getMainHandItem();
|
||||
if (mainHand.isEmpty() || !(mainHand.getItem() instanceof FishingRodItem)) {
|
||||
return;
|
||||
}
|
||||
|
||||
FishingHook fishingHook = this.fishing;
|
||||
if (fishingHook != null) {
|
||||
if (fishingHook.currentState == FishingHook.FishHookState.HOOKED_IN_ENTITY) {
|
||||
mainHand.use(this.level(), this, InteractionHand.MAIN_HAND);
|
||||
this.autoFishCooldown = AUTO_FISH_ENTITY_DELAY;
|
||||
return;
|
||||
}
|
||||
|
||||
if (fishingHook.nibble > 0) {
|
||||
mainHand.use(this.level(), this, InteractionHand.MAIN_HAND);
|
||||
this.autoFishCooldown = AUTO_FISH_RECAST_DELAY;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
mainHand.use(this.level(), this, InteractionHand.MAIN_HAND);
|
||||
this.autoFishCooldown = AUTO_FISH_RECAST_DELAY;
|
||||
}
|
||||
|
||||
private boolean hasActiveAction(String actionName) {
|
||||
return this.actions.stream().anyMatch(action -> !action.isCancelled() && action.getName().equals(actionName));
|
||||
}
|
||||
|
||||
public boolean addBotAction(AbstractBotAction<?> action, CommandSender sender) {
|
||||
if (!FakeplayerConfig.canUseAction) {
|
||||
return false;
|
||||
|
||||
@@ -17,8 +17,10 @@
|
||||
|
||||
package org.leavesmc.leaves.bot;
|
||||
|
||||
import fun.bm.lophine.carpet.config.modules.FakePlayerCompatConfig;
|
||||
import net.kyori.adventure.text.Component;
|
||||
import net.minecraft.core.BlockPos;
|
||||
import net.minecraft.server.MinecraftServer;
|
||||
import net.minecraft.server.level.ServerPlayer;
|
||||
import net.minecraft.server.level.ServerPlayerGameMode;
|
||||
import net.minecraft.world.InteractionHand;
|
||||
@@ -40,7 +42,7 @@ public class ServerBotGameMode extends ServerPlayerGameMode {
|
||||
|
||||
public ServerBotGameMode(ServerBot bot) {
|
||||
super(bot);
|
||||
super.setGameModeForPlayer(GameType.SURVIVAL, null);
|
||||
super.setGameModeForPlayer(getInitialGameMode(bot), null);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -133,4 +135,8 @@ public class ServerBotGameMode extends ServerPlayerGameMode {
|
||||
return InteractionResult.PASS;
|
||||
}
|
||||
}
|
||||
|
||||
private static GameType getInitialGameMode(ServerBot bot) {
|
||||
return FakePlayerCompatConfig.fakePlayerDefaultSurvivalMode ? GameType.SURVIVAL : MinecraftServer.getServer().getDefaultGameType();
|
||||
}
|
||||
}
|
||||
|
||||
+10
@@ -17,9 +17,12 @@
|
||||
|
||||
package org.leavesmc.leaves.bot.agent.actions;
|
||||
|
||||
import fun.bm.lophine.carpet.config.modules.FakePlayerCompatConfig;
|
||||
import net.minecraft.world.InteractionHand;
|
||||
import net.minecraft.world.InteractionResult;
|
||||
import net.minecraft.world.entity.Entity;
|
||||
import net.minecraft.world.entity.decoration.ArmorStand;
|
||||
import net.minecraft.world.item.Items;
|
||||
import net.minecraft.world.phys.EntityHitResult;
|
||||
import net.minecraft.world.phys.Vec3;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
@@ -51,6 +54,13 @@ public class ServerUseItemToAction extends AbstractUseBotAction<ServerUseItemToA
|
||||
Vec3 vec3 = hitResult.getLocation().subtract(entity.getX(), entity.getY(), entity.getZ());
|
||||
bot.updateItemInHand(hand);
|
||||
InteractionResult interactionResult = entity.interactAt(bot, vec3, hand);
|
||||
if (FakePlayerCompatConfig.fakePlayerInteractLikeClient
|
||||
&& entity instanceof ArmorStand stand
|
||||
&& !stand.isMarker()
|
||||
&& !bot.isSpectator()
|
||||
&& !bot.getItemInHand(hand).is(Items.NAME_TAG)) {
|
||||
interactionResult = InteractionResult.PASS;
|
||||
}
|
||||
if (!interactionResult.consumesAction()) {
|
||||
interactionResult = bot.interactOn(hitResult.getEntity(), hand);
|
||||
}
|
||||
|
||||
+28
-3
@@ -1,7 +1,6 @@
|
||||
package org.leavesmc.leaves.protocol;
|
||||
|
||||
import com.mojang.logging.LogUtils;
|
||||
import fun.bm.lophine.config.modules.function.CreativeFlyNoClipConfig;
|
||||
import net.minecraft.nbt.CompoundTag;
|
||||
import net.minecraft.network.FriendlyByteBuf;
|
||||
import net.minecraft.network.codec.ByteBufCodecs;
|
||||
@@ -54,8 +53,8 @@ public class CarpetServerProtocol implements LeavesProtocol {
|
||||
|
||||
@Override
|
||||
public boolean isActive() {
|
||||
return CreativeFlyNoClipConfig.enabled;
|
||||
} // re-edit for creative no clip
|
||||
return CarpetRules.hasRules();
|
||||
}
|
||||
|
||||
public static class CarpetRules {
|
||||
|
||||
@@ -71,6 +70,14 @@ public class CarpetServerProtocol implements LeavesProtocol {
|
||||
public static void register(CarpetRule rule) {
|
||||
rules.put(rule.name, rule);
|
||||
}
|
||||
|
||||
public static void clear() {
|
||||
rules.clear();
|
||||
}
|
||||
|
||||
public static boolean hasRules() {
|
||||
return !rules.isEmpty();
|
||||
}
|
||||
}
|
||||
|
||||
public record CarpetRule(String identifier, String name, String value) {
|
||||
@@ -87,6 +94,24 @@ public class CarpetServerProtocol implements LeavesProtocol {
|
||||
return new CarpetRule(identifier, name, Boolean.toString(value));
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Contract("_, _, _ -> new")
|
||||
public static CarpetRule of(String identifier, String name, int value) {
|
||||
return new CarpetRule(identifier, name, Integer.toString(value));
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Contract("_, _, _ -> new")
|
||||
public static CarpetRule of(String identifier, String name, long value) {
|
||||
return new CarpetRule(identifier, name, Long.toString(value));
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Contract("_, _, _ -> new")
|
||||
public static CarpetRule of(String identifier, String name, String value) {
|
||||
return new CarpetRule(identifier, name, value);
|
||||
}
|
||||
|
||||
public void writeNBT(@NotNull CompoundTag rules) {
|
||||
CompoundTag rule = new CompoundTag();
|
||||
String key = name;
|
||||
|
||||
+1
@@ -84,6 +84,7 @@ public class ServuxHudDataProtocol implements LeavesProtocol {
|
||||
private static void onPlayerLeave(ServerPlayer player) {
|
||||
players.remove(player);
|
||||
loggerPlayers.remove(player);
|
||||
DATA.rowMap().values().forEach(row -> row.remove(player));
|
||||
}
|
||||
|
||||
@ProtocolHandler.PayloadReceiver(payload = HudDataPayload.class)
|
||||
|
||||
Reference in New Issue
Block a user