diff --git a/1todos/server/luminol-patches/features/0006-Carpet-features.patch b/1todos/server/luminol-patches/features/0006-Carpet-features.patch deleted file mode 100644 index 405b42a..0000000 --- a/1todos/server/luminol-patches/features/0006-Carpet-features.patch +++ /dev/null @@ -1,24 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: Bacteriawa -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(); - } - diff --git a/1todos/server/minecraft-patches/features/0044-Carpet-features.patch b/1todos/server/minecraft-patches/features/0044-Carpet-features.patch deleted file mode 100644 index 8909c1d..0000000 --- a/1todos/server/minecraft-patches/features/0044-Carpet-features.patch +++ /dev/null @@ -1,1148 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: Bacteriawa -Date: Sun, 22 Mar 2026 01:39:15 +0800 -Subject: [PATCH] Carpet features - - -diff --git a/io/papermc/paper/threadedregions/RegionizedServer.java b/io/papermc/paper/threadedregions/RegionizedServer.java -index 8d32d906f87a1cb866a7497ee0de3045e9adce2c..8dbf7a1f448277cc2a40dec8d9627a8571f2b5a3 100644 ---- a/io/papermc/paper/threadedregions/RegionizedServer.java -+++ b/io/papermc/paper/threadedregions/RegionizedServer.java -@@ -331,6 +331,7 @@ public final class RegionizedServer { - } - this.randomWalk(); - */ -+ final long tickStartNanos = System.nanoTime(); - ++this.tickCount; - // Luminol start - Add a config to enable tick command - if (fun.bm.lophine.config.modules.experiment.CommandConfig.tick) { -@@ -369,6 +370,8 @@ public final class RegionizedServer { - } - // Luminol end - Add a config to enable tick command - -+ fun.bm.lophine.protocol.tiscm.TISCMProtocol.broadcastMsptSample(this.tickCount, System.nanoTime() - tickStartNanos); -+ - org.leavesmc.leaves.protocol.core.LeavesProtocolManager.handleTick(); // Leaves - protocol - - // tick connections -diff --git a/net/minecraft/commands/arguments/blocks/BlockInput.java b/net/minecraft/commands/arguments/blocks/BlockInput.java -index f9a5058e50c7fc6ebc2fc311bbc7064bfbd89035..9b737c3fcfae1f6afeee42cd4a4d83ba56f7c3e9 100644 ---- a/net/minecraft/commands/arguments/blocks/BlockInput.java -+++ b/net/minecraft/commands/arguments/blocks/BlockInput.java -@@ -65,7 +65,9 @@ public class BlockInput implements Predicate { - } - - public boolean place(ServerLevel level, BlockPos pos, @Block.UpdateFlags int flags) { -- BlockState blockState = (flags & Block.UPDATE_KNOWN_SHAPE) != 0 ? this.state : Block.updateFromNeighbourShapes(this.state, level, pos); -+ BlockState blockState = (flags & Block.UPDATE_KNOWN_SHAPE) != 0 || fun.bm.lophine.carpet.InteractionUpdateCompatHelper.shouldSkipUpdates() -+ ? this.state -+ : Block.updateFromNeighbourShapes(this.state, level, pos); - if (blockState.isAir()) { - blockState = this.state; - } -diff --git a/net/minecraft/core/Direction.java b/net/minecraft/core/Direction.java -index b21ce33550e2516562607b847e5f0f89c2ea3fc7..e6433818288b3b41742c524dc5bef9ad4e1817b7 100644 ---- a/net/minecraft/core/Direction.java -+++ b/net/minecraft/core/Direction.java -@@ -138,7 +138,7 @@ public enum Direction implements StringRepresentable, ca.spottedleaf.moonrise.pa - - public static Direction[] orderedByNearest(Entity entity) { - float f = entity.getViewXRot(1.0F) * (float) (Math.PI / 180.0); -- float f1 = -entity.getViewYRot(1.0F) * (float) (Math.PI / 180.0); -+ float f1 = -(fun.bm.lophine.carpet.config.modules.GeneralCompatConfig.placementRotationFix ? entity.getYRot(1.0F) : entity.getViewYRot(1.0F)) * (float) (Math.PI / 180.0); - float sin = Mth.sin(f); - float cos = Mth.cos(f); - float sin1 = Mth.sin(f1); -diff --git a/net/minecraft/network/chat/SignedMessageValidator.java b/net/minecraft/network/chat/SignedMessageValidator.java -index bf1ea9ea011b3067e329abf77bd6cf4ef991a02a..d971d67b2911a5763f9b1f690bc4991c3a6186a2 100644 ---- a/net/minecraft/network/chat/SignedMessageValidator.java -+++ b/net/minecraft/network/chat/SignedMessageValidator.java -@@ -31,7 +31,9 @@ public interface SignedMessageValidator { - private boolean validateChain(PlayerChatMessage message) { - if (message.equals(this.lastMessage)) { - return true; -- } else if (this.lastMessage != null && !message.link().isDescendantOf(this.lastMessage.link())) { -+ } else if (this.lastMessage != null -+ && !fun.bm.lophine.carpet.config.modules.GeneralCompatConfig.yeetOutOfOrderChatKick -+ && !message.link().isDescendantOf(this.lastMessage.link())) { - LOGGER.error( - "Received out-of-order chat message from {}: expected index > {} for session {}, but was {} for session {}", - message.sender(), -diff --git a/net/minecraft/server/commands/TickCommand.java b/net/minecraft/server/commands/TickCommand.java -index 99096896a4512eed579b234e7fa0c6e9c3e8c246..daddf1892ba250e08da295c7608e8f7c40841434 100644 ---- a/net/minecraft/server/commands/TickCommand.java -+++ b/net/minecraft/server/commands/TickCommand.java -@@ -20,7 +20,7 @@ public class TickCommand { - public static void register(CommandDispatcher dispatcher) { - dispatcher.register( - Commands.literal("tick") -- .requires(Commands.hasPermission(Commands.LEVEL_ADMINS)) -+ .requires(Commands.hasPermission(tickCommandPermissionCheck())) - .then(Commands.literal("query").executes(commandContext -> tickQuery(commandContext.getSource()))) - .then( - Commands.literal("rate") -@@ -116,6 +116,10 @@ public class TickCommand { - - private static int setFreeze(CommandSourceStack source, boolean frozen) { - ServerTickRateManager serverTickRateManager = source.getServer().tickRateManager(); -+ if (frozen && fun.bm.lophine.carpet.config.modules.GeneralCompatConfig.tickFreezeCommandToggleable && serverTickRateManager.isFrozen()) { -+ frozen = false; -+ } -+ - if (frozen) { - if (serverTickRateManager.isSprinting()) { - serverTickRateManager.stopSprinting(); -@@ -171,4 +175,8 @@ public class TickCommand { - return 0; - } - } -+ -+ private static net.minecraft.server.permissions.PermissionCheck tickCommandPermissionCheck() { -+ return new net.minecraft.server.permissions.PermissionCheck.Require(new net.minecraft.server.permissions.Permission.HasCommandLevel(net.minecraft.server.permissions.PermissionLevel.byId(fun.bm.lophine.carpet.config.modules.GeneralCompatConfig.normalizedTickCommandPermission()))); -+ } - } -diff --git a/net/minecraft/server/dedicated/DedicatedPlayerList.java b/net/minecraft/server/dedicated/DedicatedPlayerList.java -index f7f4060370e896316acf5d50dba09c2aeece8bd8..aed0c60d4235776eb6495666693bba6b83defda4 100644 ---- a/net/minecraft/server/dedicated/DedicatedPlayerList.java -+++ b/net/minecraft/server/dedicated/DedicatedPlayerList.java -@@ -14,7 +14,7 @@ public class DedicatedPlayerList extends PlayerList { - - public DedicatedPlayerList(DedicatedServer server, LayeredRegistryAccess registries, PlayerDataStorage playerIo) { - super(server, registries, playerIo, server.notificationManager()); -- this.setViewDistance(server.viewDistance()); -+ this.setViewDistance(Math.max(2, fun.bm.lophine.carpet.config.modules.GeneralCompatConfig.viewDistance)); - this.setSimulationDistance(server.simulationDistance()); - // Paper start - fix converting txt to json file; moved from constructor - } -diff --git a/net/minecraft/server/level/ServerPlayer.java b/net/minecraft/server/level/ServerPlayer.java -index e6ec0788b88d3431c06134c879b4bae9c769dac1..bc94a9094e3cee5d5d4c5b919bf4c2a1d9a73ea4 100644 ---- a/net/minecraft/server/level/ServerPlayer.java -+++ b/net/minecraft/server/level/ServerPlayer.java -@@ -255,6 +255,7 @@ public class ServerPlayer extends Player implements ca.spottedleaf.moonrise.patc - private int requestedViewDistance = 2; - public String language = null; // Paper - default to null - public java.util.Locale adventure$locale = java.util.Locale.US; // Paper -+ private @Nullable ClientInformation lastKnownClientInformation; - private @Nullable Vec3 startingToFallPosition; - private @Nullable Vec3 enteredNetherPosition; - private @Nullable Vec3 enteredLavaOnVehiclePosition; -@@ -2768,6 +2769,9 @@ public class ServerPlayer extends Player implements ca.spottedleaf.moonrise.patc - this.setShoulderEntityRight(that.getShoulderEntityRight()); - this.setLastDeathLocation(that.getLastDeathLocation()); - this.waypointIcon().copyFrom(that.waypointIcon()); -+ if (fun.bm.lophine.carpet.config.modules.GeneralCompatConfig.clientSettingsLostOnRespawnFix && that.lastKnownClientInformation != null) { -+ this.updateOptions(that.lastKnownClientInformation); -+ } - } - - private void transferInventoryXpAndScore(Player player) { -@@ -3027,6 +3031,9 @@ public class ServerPlayer extends Player implements ca.spottedleaf.moonrise.patc - this.particleStatus = clientInformation.particleStatus(); - this.getEntityData().set(DATA_PLAYER_MODE_CUSTOMISATION, (byte)clientInformation.modelCustomisation()); - this.getEntityData().set(DATA_PLAYER_MAIN_HAND, clientInformation.mainHand()); -+ if (fun.bm.lophine.carpet.config.modules.GeneralCompatConfig.clientSettingsLostOnRespawnFix) { -+ this.lastKnownClientInformation = clientInformation; -+ } - } - - public ClientInformation clientInformation() { -diff --git a/net/minecraft/server/network/ServerGamePacketListenerImpl.java b/net/minecraft/server/network/ServerGamePacketListenerImpl.java -index 4a079d3033908f3b732c70eddab3c8e86d2c3e78..4d95e36855aecc8be0133c1d6f7d6a58bf26b0a0 100644 ---- a/net/minecraft/server/network/ServerGamePacketListenerImpl.java -+++ b/net/minecraft/server/network/ServerGamePacketListenerImpl.java -@@ -2050,7 +2050,13 @@ public class ServerGamePacketListenerImpl - this.player.gameMode.capturedBlockEntity = false; - this.player.gameMode.captureSentBlockEntities = true; - // Paper end - Send block entities after destroy prediction -- this.player.gameMode.handleBlockBreakAction(pos, action, packet.getDirection(), this.player.level().getMaxY(), packet.getSequence()); -+ if (fun.bm.lophine.carpet.config.modules.GeneralCompatConfig.interactionUpdates) { -+ this.player.gameMode.handleBlockBreakAction(pos, action, packet.getDirection(), this.player.level().getMaxY(), packet.getSequence()); -+ } else { -+ fun.bm.lophine.carpet.InteractionUpdateCompatHelper.runWithSuppressedUpdates( -+ () -> this.player.gameMode.handleBlockBreakAction(pos, action, packet.getDirection(), this.player.level().getMaxY(), packet.getSequence()) -+ ); -+ } - this.ackBlockChangesUpTo(packet.getSequence()); - // Paper start - Send block entities after destroy prediction - this.player.gameMode.captureSentBlockEntities = false; -@@ -2145,7 +2151,9 @@ public class ServerGamePacketListenerImpl - this.player.stopUsingItem(); // CraftBukkit - SPIGOT-4706 - } - // Luminol end -- InteractionResult interactionResult = this.player.gameMode.useItemOn(this.player, serverLevel, itemInHand, hand, hitResult); -+ InteractionResult interactionResult = this.runInteractionWithConfiguredUpdates( -+ () -> this.player.gameMode.useItemOn(this.player, serverLevel, itemInHand, hand, hitResult) -+ ); - if (interactionResult.consumesAction()) { - CriteriaTriggers.ANY_BLOCK_USE.trigger(this.player, hitResult.getBlockPos(), itemInHand.copy()); - } -@@ -2251,7 +2259,8 @@ public class ServerGamePacketListenerImpl - } - // CraftBukkit end - -- if (this.player.gameMode.useItem(this.player, serverLevel, itemInHand, hand) instanceof InteractionResult.Success success -+ ItemStack itemInHandToUse = itemInHand; -+ if (this.runInteractionWithConfiguredUpdates(() -> this.player.gameMode.useItem(this.player, serverLevel, itemInHandToUse, hand)) instanceof InteractionResult.Success success - && success.swingSource() == InteractionResult.SwingSource.SERVER) { - this.player.swing(hand, true); - } -@@ -2259,6 +2268,14 @@ public class ServerGamePacketListenerImpl - } - } - -+ private T runInteractionWithConfiguredUpdates(java.util.function.Supplier action) { -+ if (fun.bm.lophine.carpet.config.modules.GeneralCompatConfig.interactionUpdates) { -+ return action.get(); -+ } -+ -+ return fun.bm.lophine.carpet.InteractionUpdateCompatHelper.supplyWithSuppressedUpdates(action); -+ } -+ - @Override - public void handleTeleportToEntityPacket(ServerboundTeleportToEntityPacket packet) { - PacketUtils.ensureRunningOnSameThread(packet, this, this.player.level()); -@@ -2648,6 +2665,8 @@ public class ServerGamePacketListenerImpl - if (true) throw new UnsupportedOperationException(); // Folia - region threading - final String conversationInput = rawMessage; - this.server.processQueue.add(() -> ServerGamePacketListenerImpl.this.getCraftPlayer().acceptConversationInput(conversationInput)); -+ } else if (fun.bm.lophine.carpet.CarpetCalculatorCompatHelper.handleChat(this.player, rawMessage)) { -+ return; - } else if (this.player.getChatVisibility() == ChatVisiblity.SYSTEM) { // Re-add "Command Only" flag check - this.send(new ClientboundSystemChatPacket(Component.translatable("chat.cannotSend").withStyle(ChatFormatting.RED), false)); - } else { -@@ -2660,6 +2679,9 @@ public class ServerGamePacketListenerImpl - - // Spigot start - spam exclusions - private void detectRateSpam(String message) { -+ if (fun.bm.lophine.carpet.config.modules.GeneralCompatConfig.antiSpamDisabled) { -+ return; -+ } - if (me.earthme.luminol.config.modules.misc.PaperPacketLimiterConfig.forceDisable) return; // Leaves - disable - // CraftBukkit start - replaced with thread safe throttle - if (org.spigotmc.SpigotConfig.enableSpamExclusions) { -@@ -3583,8 +3605,10 @@ public class ServerGamePacketListenerImpl - if (org.leavesmc.leaves.util.ItemOverstackUtils.hasOverstackingItem()) this.player.containerMenu.sendSingleSlot(packet.slotNum(), itemStack); // Leaves - item over-stack util - force send carried item - if (io.papermc.paper.configuration.GlobalConfiguration.get().unsupportedSettings.updateEquipmentOnPlayerActions) this.player.detectEquipmentUpdates(); // Paper - Force update attributes. - } else if (flag && flag2) { -- if (this.dropSpamThrottler.isUnderThreshold()) { -- this.dropSpamThrottler.increment(); -+ if (fun.bm.lophine.carpet.config.modules.GeneralCompatConfig.antiSpamDisabled || this.dropSpamThrottler.isUnderThreshold()) { -+ if (!fun.bm.lophine.carpet.config.modules.GeneralCompatConfig.antiSpamDisabled) { -+ this.dropSpamThrottler.increment(); -+ } - this.player.drop(itemStack, true); - } else { - LOGGER.warn("Player {} was dropping items too fast in creative mode, ignoring.", this.player.getPlainTextName()); -diff --git a/net/minecraft/world/entity/ExperienceOrb.java b/net/minecraft/world/entity/ExperienceOrb.java -index 29d904b02a2c7b3d25261ca26c7c0479b00999ef..34dc71daf2182805465742b1455e13b904f076e9 100644 ---- a/net/minecraft/world/entity/ExperienceOrb.java -+++ b/net/minecraft/world/entity/ExperienceOrb.java -@@ -354,8 +354,22 @@ public class ExperienceOrb extends Entity { - @Override - public void playerTouch(Player entity) { - if (entity instanceof ServerPlayer serverPlayer) { -+ if (fun.bm.lophine.carpet.config.modules.GeneralCompatConfig.xpNoCooldown) { -+ entity.takeXpDelay = 0; -+ } -+ - if (entity.takeXpDelay == 0 && new com.destroystokyo.paper.event.player.PlayerPickupExperienceEvent(serverPlayer.getBukkitEntity(), (org.bukkit.entity.ExperienceOrb) this.getBukkitEntity()).callEvent()) { // Paper - PlayerPickupExperienceEvent -- entity.takeXpDelay = org.bukkit.craftbukkit.event.CraftEventFactory.callPlayerXpCooldownEvent(entity, 2, org.bukkit.event.player.PlayerExpCooldownChangeEvent.ChangeReason.PICKUP_ORB).getNewCooldown(); // CraftBukkit - entity.takeXpDelay = 2; -+ int newCooldown = org.bukkit.craftbukkit.event.CraftEventFactory.callPlayerXpCooldownEvent(entity, 2, org.bukkit.event.player.PlayerExpCooldownChangeEvent.ChangeReason.PICKUP_ORB).getNewCooldown(); // CraftBukkit - entity.takeXpDelay = 2; -+ entity.takeXpDelay = fun.bm.lophine.carpet.config.modules.GeneralCompatConfig.xpNoCooldown ? 0 : newCooldown; -+ if (fun.bm.lophine.carpet.config.modules.GeneralCompatConfig.xpNoCooldown) { -+ while (this.count > 1) { -+ int remainder = this.repairPlayerItems(serverPlayer, this.getValue()); -+ if (remainder > 0) { -+ entity.giveExperiencePoints(remainder); -+ } -+ this.count--; -+ } -+ } - entity.take(this, 1); - int i = this.repairPlayerItems(serverPlayer, this.getValue()); - if (i > 0) { -@@ -371,6 +385,10 @@ public class ExperienceOrb extends Entity { - } - - private int repairPlayerItems(ServerPlayer player, int value) { -+ if (fun.bm.lophine.carpet.config.modules.GeneralCompatConfig.powerfulExpMending) { -+ return this.repairAllPlayerItems(player, value); -+ } -+ - Optional randomItemWith = EnchantmentHelper.getRandomItemWith( - EnchantmentEffectComponents.REPAIR_WITH_XP, player, ItemStack::isDamaged - ); -@@ -402,6 +420,76 @@ public class ExperienceOrb extends Entity { - } - } - -+ private int repairAllPlayerItems(ServerPlayer player, int value) { -+ List candidates = new java.util.ArrayList<>(); -+ net.minecraft.world.entity.player.Inventory inventory = player.getInventory(); -+ -+ for (int slotIndex = 0; slotIndex < inventory.getNonEquipmentItems().size(); slotIndex++) { -+ ItemStack stack = inventory.getNonEquipmentItems().get(slotIndex); -+ EquipmentSlot slot = slotIndex == inventory.getSelectedSlot() ? EquipmentSlot.MAINHAND : null; -+ candidates.add(new MendingCandidate(stack, slot)); -+ } -+ -+ candidates.add(new MendingCandidate(player.getItemBySlot(EquipmentSlot.HEAD), EquipmentSlot.HEAD)); -+ candidates.add(new MendingCandidate(player.getItemBySlot(EquipmentSlot.CHEST), EquipmentSlot.CHEST)); -+ candidates.add(new MendingCandidate(player.getItemBySlot(EquipmentSlot.LEGS), EquipmentSlot.LEGS)); -+ candidates.add(new MendingCandidate(player.getItemBySlot(EquipmentSlot.FEET), EquipmentSlot.FEET)); -+ candidates.add(new MendingCandidate(player.getItemBySlot(EquipmentSlot.OFFHAND), EquipmentSlot.OFFHAND)); -+ -+ java.util.Collections.shuffle(candidates, new java.util.Random(player.getRandom().nextLong())); -+ -+ int remaining = value; -+ for (MendingCandidate candidate : candidates) { -+ if (remaining <= 0) { -+ break; -+ } -+ -+ ItemStack stack = candidate.stack(); -+ if (!this.canRepairWithXp(player, stack)) { -+ continue; -+ } -+ -+ int durabilityToRepair = EnchantmentHelper.modifyDurabilityToRepairFromXp(player.level(), stack, remaining); -+ int repairedAmount = Math.min(durabilityToRepair, stack.getDamageValue()); -+ if (repairedAmount <= 0) { -+ continue; -+ } -+ -+ org.bukkit.event.player.PlayerItemMendEvent event = candidate.slot() != null -+ ? org.bukkit.craftbukkit.event.CraftEventFactory.callPlayerItemMendEvent( -+ player, this, stack, candidate.slot(), repairedAmount, repairedAmount * remaining / durabilityToRepair -+ ) -+ : this.callInventoryItemMendEvent(player, stack, repairedAmount); -+ repairedAmount = event.getRepairAmount(); -+ if (event.isCancelled() || repairedAmount <= 0) { -+ continue; -+ } -+ -+ stack.setDamageValue(stack.getDamageValue() - repairedAmount); -+ remaining -= repairedAmount * remaining / durabilityToRepair; -+ } -+ -+ return remaining; -+ } -+ -+ private boolean canRepairWithXp(ServerPlayer player, ItemStack stack) { -+ return !stack.isEmpty() && stack.isDamaged() && EnchantmentHelper.modifyDurabilityToRepairFromXp(player.level(), stack, 1) > 1; -+ } -+ -+ private org.bukkit.event.player.PlayerItemMendEvent callInventoryItemMendEvent(ServerPlayer player, ItemStack stack, int repairAmount) { -+ org.bukkit.event.player.PlayerItemMendEvent event = new org.bukkit.event.player.PlayerItemMendEvent( -+ player.getBukkitEntity(), -+ org.bukkit.craftbukkit.inventory.CraftItemStack.asCraftMirror(stack), -+ (org.bukkit.entity.ExperienceOrb)this.getBukkitEntity(), -+ repairAmount -+ ); -+ player.level().getCraftServer().getPluginManager().callEvent(event); -+ return event; -+ } -+ -+ private record MendingCandidate(ItemStack stack, @Nullable EquipmentSlot slot) { -+ } -+ - public int getValue() { - return this.entityData.get(DATA_VALUE); - } -diff --git a/net/minecraft/world/entity/LivingEntity.java b/net/minecraft/world/entity/LivingEntity.java -index fc8833655102ca98bb331782a624af79cbef414f..b49ff0baae5493925c0b13876fd0473dbeb649c9 100644 ---- a/net/minecraft/world/entity/LivingEntity.java -+++ b/net/minecraft/world/entity/LivingEntity.java -@@ -607,7 +607,8 @@ public abstract class LivingEntity extends Entity implements Attackable, Waypoin - public boolean broadcastedDeath = false; // Folia - region threading - protected void tickDeath() { - this.deathTime++; -- if (this.deathTime >= 20 && !this.level().isClientSide() && !this.isRemoved()) { -+ int removalDelay = fun.bm.lophine.carpet.config.modules.GeneralCompatConfig.entityInstantDeathRemoval ? 1 : 20; -+ if (this.deathTime >= removalDelay && !this.level().isClientSide() && !this.isRemoved()) { - this.level().broadcastEntityEvent(this, EntityEvent.POOF); this.broadcastedDeath = true; // Folia - region threading - death has been broadcasted - if (!(this instanceof ServerPlayer)) this.remove(Entity.RemovalReason.KILLED, org.bukkit.event.entity.EntityRemoveEvent.Cause.DEATH); // CraftBukkit - add Bukkit remove cause // Folia - region threading - don't remove, we want the tick scheduler to be running - if ((this instanceof ServerPlayer)) this.unRide(); // Folia - region threading - unmount player when dead -diff --git a/net/minecraft/world/entity/item/PrimedTnt.java b/net/minecraft/world/entity/item/PrimedTnt.java -index fd1db18764be58bc5ed5983b98e669c76f7ff427..a0d7658435cb838415121882e4c3bb3d0fa10040 100644 ---- a/net/minecraft/world/entity/item/PrimedTnt.java -+++ b/net/minecraft/world/entity/item/PrimedTnt.java -@@ -68,8 +68,12 @@ public class PrimedTnt extends Entity implements TraceableEntity { - this(EntityType.TNT, level); - this.setPos(x, y, z); - double d = this.random.nextDouble() * (float) (Math.PI * 2); // Paper - Don't use level random in entity constructors -- this.setDeltaMovement(-Math.sin(d) * 0.02, 0.2F, -Math.cos(d) * 0.02); -- this.setFuse(80); -+ if (fun.bm.lophine.carpet.config.modules.GeneralCompatConfig.tntPrimerMomentumRemoved) { -+ this.setDeltaMovement(0.0, 0.20000000298023224D, 0.0); -+ } else { -+ this.setDeltaMovement(-Math.sin(d) * 0.02, 0.2F, -Math.cos(d) * 0.02); -+ } -+ this.setFuse(getConfiguredFuseTime()); - this.xo = x; - this.yo = y; - this.zo = z; -@@ -78,7 +82,7 @@ public class PrimedTnt extends Entity implements TraceableEntity { - - @Override - protected void defineSynchedData(SynchedEntityData.Builder builder) { -- builder.define(DATA_FUSE_ID, 80); -+ builder.define(DATA_FUSE_ID, getConfiguredFuseTime()); - builder.define(DATA_BLOCK_STATE_ID, DEFAULT_BLOCK_STATE); - } - -@@ -175,7 +179,7 @@ public class PrimedTnt extends Entity implements TraceableEntity { - - @Override - protected void readAdditionalSaveData(ValueInput input) { -- this.setFuse(input.getShortOr("fuse", (short)80)); -+ this.setFuse(input.getShortOr("fuse", (short)getConfiguredFuseTime())); - this.setBlockState(input.read("block_state", BlockState.CODEC).orElse(DEFAULT_BLOCK_STATE)); - this.explosionPower = Mth.clamp(input.getFloatOr("explosion_power", 4.0F), 0.0F, 128.0F); - this.owner = EntityReference.read(input, "owner"); -@@ -198,6 +202,10 @@ public class PrimedTnt extends Entity implements TraceableEntity { - this.entityData.set(DATA_FUSE_ID, life); - } - -+ private static int getConfiguredFuseTime() { -+ return fun.bm.lophine.carpet.config.modules.GeneralCompatConfig.normalizedTntFuseDuration(); -+ } -+ - public int getFuse() { - return this.entityData.get(DATA_FUSE_ID); - } -diff --git a/net/minecraft/world/entity/monster/EnderMan.java b/net/minecraft/world/entity/monster/EnderMan.java -index f7c0c1c75ba02c2d70ff2b67e437c0813458ee3d..96b436fe4af6fe45b927e757a856e6fe3190cbe6 100644 ---- a/net/minecraft/world/entity/monster/EnderMan.java -+++ b/net/minecraft/world/entity/monster/EnderMan.java -@@ -651,7 +651,7 @@ public class EnderMan extends Monster implements NeutralMob { - Vec3 vec31 = new Vec3(floor + 0.5, floor1 + 0.5, floor2 + 0.5); - BlockHitResult blockHitResult = level.clip(new ClipContext(vec3, vec31, ClipContext.Block.OUTLINE, ClipContext.Fluid.NONE, this.enderman)); - boolean flag = blockHitResult.getBlockPos().equals(blockPos); -- if (blockState.is(BlockTags.ENDERMAN_HOLDABLE) && flag) { -+ if (canPickupBlock(blockState) && flag) { - if (org.bukkit.craftbukkit.event.CraftEventFactory.callEntityChangeBlockEvent(this.enderman, blockPos, blockState.getFluidState().createLegacyBlock())) { // CraftBukkit - Place event // Paper - fix wrong block state - level.removeBlock(blockPos, false); - level.gameEvent(GameEvent.BLOCK_DESTROY, blockPos, GameEvent.Context.of(this.enderman, blockState)); -@@ -659,5 +659,13 @@ public class EnderMan extends Monster implements NeutralMob { - } // CraftBukkit - } - } -+ -+ private static boolean canPickupBlock(BlockState state) { -+ if (!fun.bm.lophine.carpet.config.modules.GeneralCompatConfig.sensibleEnderman) { -+ return state.is(BlockTags.ENDERMAN_HOLDABLE); -+ } -+ -+ return state.is(Blocks.MELON) || state.is(Blocks.PUMPKIN); -+ } - } - } -diff --git a/net/minecraft/world/entity/player/Player.java b/net/minecraft/world/entity/player/Player.java -index 8966953ee80a1527c9c4cc4d0a7a3bdb2197a2da..0a6f1478856f503408a10c131d20ec3b235753ce 100644 ---- a/net/minecraft/world/entity/player/Player.java -+++ b/net/minecraft/world/entity/player/Player.java -@@ -564,7 +564,13 @@ public abstract class Player extends Avatar implements ContainerUser { - } - - if (!list.isEmpty()) { -- this.touch(Util.getRandom(list, this.random)); -+ if (fun.bm.lophine.carpet.config.modules.GeneralCompatConfig.xpNoCooldown) { -+ for (Entity entity : list) { -+ this.touch(entity); -+ } -+ } else { -+ this.touch(Util.getRandom(list, this.random)); -+ } - } - } - -@@ -1074,6 +1080,22 @@ public abstract class Player extends Avatar implements ContainerUser { - - if (playerAttackEntityEvent.callEvent() && willAttack) { // Logic moved to willAttack local variable. - // Paper end - PlayerAttackEntityEvent -+ if (this.level() instanceof ServerLevel serverLevel -+ && fun.bm.lophine.carpet.config.modules.GeneralCompatConfig.creativeOneHitKill -+ && this.isCreative() -+ && net.minecraft.world.entity.EntitySelector.NO_CREATIVE_OR_SPECTATOR.test(target)) { -+ if (this.isShiftKeyDown()) { -+ for (Entity entity : this.level().getEntities(this, target.getBoundingBox().inflate(2.0D, 0.5D, 2.0D), candidate -> -+ candidate.isAttackable() && net.minecraft.world.entity.EntitySelector.NO_CREATIVE_OR_SPECTATOR.test(candidate))) { -+ this.creativeInstantKill(serverLevel, entity); -+ } -+ this.playServerSideSound(SoundEvents.PLAYER_ATTACK_SWEEP); -+ } else { -+ this.creativeInstantKill(serverLevel, target); -+ this.playServerSideSound(SoundEvents.PLAYER_ATTACK_CRIT); -+ } -+ return; -+ } - float f = this.isAutoSpinAttack() ? this.autoSpinAttackDmg : (float)this.getAttributeValue(Attributes.ATTACK_DAMAGE); - ItemStack weaponItem = this.getWeaponItem(); - DamageSource damageSource = this.createAttackSource(weaponItem); final DamageSource dmgSourceFinal = damageSource; // Paper - damage events -@@ -1131,6 +1153,16 @@ public abstract class Player extends Avatar implements ContainerUser { - } - } - -+ private void creativeInstantKill(ServerLevel level, Entity target) { -+ if (target instanceof EnderDragonPart enderDragonPart) { -+ java.util.Arrays.stream(enderDragonPart.parentMob.getSubEntities()).forEach(part -> part.kill(level)); -+ enderDragonPart.parentMob.kill(level); -+ return; -+ } -+ -+ target.kill(level); -+ } -+ - private void playServerSideSound(SoundEvent sound) { - sendSoundEffect(this, this.getX(), this.getY(), this.getZ(), sound, this.getSoundSource(), 1.0F, 1.0F); // Paper - send while respecting visibility - } -diff --git a/net/minecraft/world/item/BlockItem.java b/net/minecraft/world/item/BlockItem.java -index 44786e4f77285f1fbd4643f560105e410035afeb..77b3c8b4bb9ae2c7dede35f74c38febdf43e2dfc 100644 ---- a/net/minecraft/world/item/BlockItem.java -+++ b/net/minecraft/world/item/BlockItem.java -@@ -172,7 +172,13 @@ public class BlockItem extends Item { - // CraftBukkit start - CollisionContext collisionContext = player == null ? CollisionContext.empty() : CollisionContext.placementContext(player); - Level world = context.getLevel(); // Paper - Cancel hit for vanished players -- boolean canBuild = (!this.mustSurvive() || state.canSurvive(world, context.getClickedPos())) && ((fun.bm.lophine.config.modules.function.CreativeFlyNoClipConfig.enabled && context.getPlayer() != null) ? context.getPlayer().canSpectatingPlace(world, state, context.getClickedPos(), collisionContext) : world.checkEntityCollision(state, player, collisionContext, context.getClickedPos(), true)); // Paper - Cancel hit for vanished players // Leaves - creative no clip -+ boolean ignoreEntityCollision = fun.bm.lophine.carpet.config.modules.GeneralCompatConfig.blockPlacementIgnoreEntity && player != null && player.isCreative(); -+ boolean canBuild = (!this.mustSurvive() || state.canSurvive(world, context.getClickedPos())) -+ && (ignoreEntityCollision -+ ? true -+ : ((fun.bm.lophine.config.modules.function.CreativeFlyNoClipConfig.enabled && context.getPlayer() != null) -+ ? context.getPlayer().canSpectatingPlace(world, state, context.getClickedPos(), collisionContext) -+ : world.checkEntityCollision(state, player, collisionContext, context.getClickedPos(), true))); // Paper - Cancel hit for vanished players // Leaves - creative no clip - org.bukkit.entity.Player bukkitPlayer = (context.getPlayer() instanceof ServerPlayer) ? (org.bukkit.entity.Player) context.getPlayer().getBukkitEntity() : null; - - org.bukkit.event.block.BlockCanBuildEvent event = new org.bukkit.event.block.BlockCanBuildEvent( -diff --git a/net/minecraft/world/item/ServerItemCooldowns.java b/net/minecraft/world/item/ServerItemCooldowns.java -index 8725fe35812ee5f7ace0ac3dce466738b6eefc53..9f5311bec541f5efa5e8c60a7ea98824f7eb5cad 100644 ---- a/net/minecraft/world/item/ServerItemCooldowns.java -+++ b/net/minecraft/world/item/ServerItemCooldowns.java -@@ -22,6 +22,9 @@ public class ServerItemCooldowns extends ItemCooldowns { - - @Override - public void addCooldown(ItemStack item, int duration) { -+ if (fun.bm.lophine.carpet.config.modules.GeneralCompatConfig.creativeNoItemCooldown && this.player.isCreative()) { -+ return; -+ } - final Identifier cooldownGroup = this.getCooldownGroup(item); - final io.papermc.paper.event.player.PlayerItemCooldownEvent event = new io.papermc.paper.event.player.PlayerItemCooldownEvent( - this.player.getBukkitEntity(), -@@ -38,6 +41,9 @@ public class ServerItemCooldowns extends ItemCooldowns { - - @Override - public void addCooldown(Identifier groupId, int duration, boolean callEvent) { -+ if (fun.bm.lophine.carpet.config.modules.GeneralCompatConfig.creativeNoItemCooldown && this.player.isCreative()) { -+ return; -+ } - if (callEvent) { - final io.papermc.paper.event.player.PlayerItemGroupCooldownEvent event = new io.papermc.paper.event.player.PlayerItemGroupCooldownEvent( - this.player.getBukkitEntity(), -diff --git a/net/minecraft/world/item/crafting/RecipeManager.java b/net/minecraft/world/item/crafting/RecipeManager.java -index 4e0b0d630e18bde916a07889bcaf0e7accf064ca..05ace66c11a206443f12537fa829b0186725b526 100644 ---- a/net/minecraft/world/item/crafting/RecipeManager.java -+++ b/net/minecraft/world/item/crafting/RecipeManager.java -@@ -55,6 +55,9 @@ public class RecipeManager extends SimplePreparableReloadListener imp - forSingleInput(RecipeType.CAMPFIRE_COOKING) - ); - private static final FileToIdConverter RECIPE_LISTER = FileToIdConverter.registry(Registries.RECIPE); -+ private static final ResourceKey> BETTER_CRAFTABLE_BONE_BLOCK_RECIPE = compatRecipeKey("better_craftable_bone_block"); -+ private static final ResourceKey> BETTER_CRAFTABLE_DISPENSER_SHAPED_RECIPE = compatRecipeKey("better_craftable_dispenser_shaped"); -+ private static final ResourceKey> BETTER_CRAFTABLE_DISPENSER_SHAPELESS_RECIPE = compatRecipeKey("better_craftable_dispenser_shapeless"); - private final HolderLookup.Provider registries; - public RecipeMap recipes = RecipeMap.EMPTY; - private Map, RecipePropertySet> propertySets = Map.of(); -@@ -83,10 +86,73 @@ public class RecipeManager extends SimplePreparableReloadListener imp - - @Override - protected void apply(RecipeMap object, ResourceManager resourceManager, ProfilerFiller profiler) { -+ this.injectCompatRecipes(object); - this.recipes = object; - LOGGER.info("Loaded {} recipes", object.values().size()); - } - -+ private void injectCompatRecipes(RecipeMap recipeMap) { -+ if (fun.bm.lophine.carpet.config.modules.GeneralCompatConfig.betterCraftableBoneBlock) { -+ addCompatRecipe(recipeMap, createBetterCraftableBoneBlockRecipe()); -+ } -+ -+ if (fun.bm.lophine.carpet.config.modules.GeneralCompatConfig.betterCraftableDispenser) { -+ addCompatRecipe(recipeMap, createBetterCraftableDispenserShapedRecipe()); -+ addCompatRecipe(recipeMap, createBetterCraftableDispenserShapelessRecipe()); -+ } -+ } -+ -+ private static void addCompatRecipe(RecipeMap recipeMap, RecipeHolder recipeHolder) { -+ if (recipeMap.byKey(recipeHolder.id()) == null) { -+ recipeMap.addRecipe(recipeHolder); -+ } -+ } -+ -+ private static RecipeHolder createBetterCraftableBoneBlockRecipe() { -+ return new RecipeHolder<>( -+ BETTER_CRAFTABLE_BONE_BLOCK_RECIPE, -+ new ShapedRecipe( -+ "carpet_ams_better_bone_block", -+ CraftingBookCategory.BUILDING, -+ ShapedRecipePattern.of(Map.of('#', Ingredient.of(net.minecraft.world.item.Items.BONE)), "###", "###", "###"), -+ new net.minecraft.world.item.ItemStack(net.minecraft.world.item.Items.BONE_BLOCK, 3) -+ ) -+ ); -+ } -+ -+ private static RecipeHolder createBetterCraftableDispenserShapedRecipe() { -+ return new RecipeHolder<>( -+ BETTER_CRAFTABLE_DISPENSER_SHAPED_RECIPE, -+ new ShapedRecipe( -+ "carpet_ams_better_dispenser", -+ CraftingBookCategory.REDSTONE, -+ ShapedRecipePattern.of( -+ Map.of('S', Ingredient.of(net.minecraft.world.item.Items.STICK), 'D', Ingredient.of(net.minecraft.world.item.Items.DROPPER), 'X', Ingredient.of(net.minecraft.world.item.Items.STRING)), -+ " SX", -+ "SDX", -+ " SX" -+ ), -+ new net.minecraft.world.item.ItemStack(net.minecraft.world.item.Items.DISPENSER) -+ ) -+ ); -+ } -+ -+ private static RecipeHolder createBetterCraftableDispenserShapelessRecipe() { -+ return new RecipeHolder<>( -+ BETTER_CRAFTABLE_DISPENSER_SHAPELESS_RECIPE, -+ new ShapelessRecipe( -+ "carpet_ams_better_dispenser", -+ CraftingBookCategory.REDSTONE, -+ new net.minecraft.world.item.ItemStack(net.minecraft.world.item.Items.DISPENSER), -+ List.of(Ingredient.of(net.minecraft.world.item.Items.BOW), Ingredient.of(net.minecraft.world.item.Items.DROPPER)) -+ ) -+ ); -+ } -+ -+ private static ResourceKey> compatRecipeKey(String path) { -+ return ResourceKey.create(Registries.RECIPE, Identifier.fromNamespaceAndPath("lophine", path)); -+ } -+ - // CraftBukkit start - public void addRecipe(RecipeHolder holder) { - org.spigotmc.AsyncCatcher.catchOp("Recipe Add"); // Spigot -diff --git a/net/minecraft/world/level/Level.java b/net/minecraft/world/level/Level.java -index 7803ff8e2e37b44d6e1b094e3141e0c99137659a..81db88c8a25ade71b88fcf5b90880f18d8610783 100644 ---- a/net/minecraft/world/level/Level.java -+++ b/net/minecraft/world/level/Level.java -@@ -1169,6 +1169,10 @@ public abstract class Level implements LevelAccessor, AutoCloseable, ca.spottedl - BlockState state = newState; - BlockState blockState = oldState; - BlockState blockState1 = currentState; -+ boolean skipInteractionUpdates = fun.bm.lophine.carpet.InteractionUpdateCompatHelper.shouldSkipUpdates(); -+ if (skipInteractionUpdates) { -+ flags &= ~Block.UPDATE_NEIGHBORS; -+ } - if (blockState1 == state) { - if (blockState != blockState1) { - this.setBlocksDirty(pos, blockState, blockState1); -@@ -1185,7 +1189,7 @@ public abstract class Level implements LevelAccessor, AutoCloseable, ca.spottedl - } - } - -- if ((flags & Block.UPDATE_KNOWN_SHAPE) == 0 && recursionLeft > 0) { -+ if (!skipInteractionUpdates && (flags & Block.UPDATE_KNOWN_SHAPE) == 0 && recursionLeft > 0) { - int i = flags & ~(Block.UPDATE_SUPPRESS_DROPS | Block.UPDATE_NEIGHBORS); - - // CraftBukkit start -diff --git a/net/minecraft/world/level/NaturalSpawner.java b/net/minecraft/world/level/NaturalSpawner.java -index e7223540a7cef20b661bdb857ffba962d0f5d348..4b6fbaa33b3db4d514fae7d0913b5530a4a44214 100644 ---- a/net/minecraft/world/level/NaturalSpawner.java -+++ b/net/minecraft/world/level/NaturalSpawner.java -@@ -267,6 +267,9 @@ public final class NaturalSpawner { - Player nearestPlayer = level.getNearestPlayer(d, y, d1, -1.0, false); - if (nearestPlayer != null) { - double d2 = nearestPlayer.distanceToSqr(d, y, d1); -+ if (fun.bm.lophine.carpet.config.modules.GeneralCompatConfig.lagFreeSpawning) { -+ d2 = fun.bm.lophine.carpet.LagFreeSpawningCompatHelper.limitDistanceSq(category, d2); -+ } - if (level.isLoadedAndInBounds(mutableBlockPos) && isRightDistanceToPlayerAndSpawnPoint(level, chunk, mutableBlockPos, d2)) { // Paper - don't load chunks for mob spawn - if (spawnerData == null) { - Optional randomSpawnMobAt = getRandomSpawnMobAt( -@@ -307,6 +310,9 @@ public final class NaturalSpawner { - // SPIGOT-7045: Give ocelot babies back their special spawn reason. Note: This is the only modification required as ocelots count as monsters which means they only spawn during normal chunk ticking and do not spawn during chunk generation as starter mobs. - level.addFreshEntityWithPassengers(mobForSpawn, (mobForSpawn instanceof net.minecraft.world.entity.animal.feline.Ocelot && !((org.bukkit.entity.Ageable) mobForSpawn.getBukkitEntity()).isAdult()) ? org.bukkit.event.entity.CreatureSpawnEvent.SpawnReason.OCELOT_BABY : org.bukkit.event.entity.CreatureSpawnEvent.SpawnReason.NATURAL); - if (!mobForSpawn.isRemoved()) { -+ if (fun.bm.lophine.carpet.config.modules.GeneralCompatConfig.lagFreeSpawning) { -+ fun.bm.lophine.carpet.LagFreeSpawningCompatHelper.markSpawned(level, mobForSpawn.getType()); -+ } - ++i; - ++i3; - callback.run(mobForSpawn, chunk); -@@ -389,11 +395,20 @@ public final class NaturalSpawner { - && canSpawnMobAt(level, structureManager, generator, category, data, pos) - && SpawnPlacements.isSpawnPositionOk(entityType, level, pos) - && SpawnPlacements.checkSpawnRules(entityType, level, EntitySpawnReason.NATURAL, pos, level.random) -- && level.noCollision(entityType.getSpawnAABB(pos.getX() + 0.5, pos.getY(), pos.getZ() + 0.5)); -+ && (fun.bm.lophine.carpet.config.modules.GeneralCompatConfig.lagFreeSpawning -+ ? fun.bm.lophine.carpet.LagFreeSpawningCompatHelper.hasNoCollision(level, entityType.getSpawnAABB(pos.getX() + 0.5, pos.getY(), pos.getZ() + 0.5)) -+ : level.noCollision(entityType.getSpawnAABB(pos.getX() + 0.5, pos.getY(), pos.getZ() + 0.5))); - return success ? PreSpawnStatus.SUCCESS : PreSpawnStatus.FAIL; // Paper - PreCreatureSpawnEvent - } - - private static @Nullable Mob getMobForSpawn(ServerLevel level, EntityType entityType) { -+ if (fun.bm.lophine.carpet.config.modules.GeneralCompatConfig.lagFreeSpawning) { -+ Mob cachedMob = fun.bm.lophine.carpet.LagFreeSpawningCompatHelper.getOrCreateMob(level, entityType); -+ if (cachedMob != null) { -+ return cachedMob; -+ } -+ } -+ - try { - if (entityType.create(level, EntitySpawnReason.NATURAL) instanceof Mob mob) { - return mob; -@@ -493,7 +508,9 @@ public final class NaturalSpawner { - float width = spawnerData.type().getWidth(); - double d = Mth.clamp((double)i1, (double)minBlockX + width, minBlockX + 16.0 - width); - double d1 = Mth.clamp((double)i2, (double)minBlockZ + width, minBlockZ + 16.0 - width); -- if (!level.noCollision(spawnerData.type().getSpawnAABB(d, topNonCollidingPos.getY(), d1)) -+ if (!(fun.bm.lophine.carpet.config.modules.GeneralCompatConfig.lagFreeSpawning -+ ? fun.bm.lophine.carpet.LagFreeSpawningCompatHelper.hasNoCollision(level.getLevel(), spawnerData.type().getSpawnAABB(d, topNonCollidingPos.getY(), d1)) -+ : level.noCollision(spawnerData.type().getSpawnAABB(d, topNonCollidingPos.getY(), d1))) - || !SpawnPlacements.checkSpawnRules( - spawnerData.type(), - level, -@@ -525,6 +542,9 @@ public final class NaturalSpawner { - level, level.getCurrentDifficultyAt(mob.blockPosition()), EntitySpawnReason.CHUNK_GENERATION, spawnGroupData - ); - level.addFreshEntityWithPassengers(mob, org.bukkit.event.entity.CreatureSpawnEvent.SpawnReason.CHUNK_GEN); // CraftBukkit -+ if (fun.bm.lophine.carpet.config.modules.GeneralCompatConfig.lagFreeSpawning && level instanceof ServerLevel serverLevel) { -+ fun.bm.lophine.carpet.LagFreeSpawningCompatHelper.markSpawned(serverLevel, mob.getType()); -+ } - flag = true; - } - } -diff --git a/net/minecraft/world/level/ServerExplosion.java b/net/minecraft/world/level/ServerExplosion.java -index 33ee967d8a7d4ce2c455db9ffaac740cd589e6f3..4dc8dda135b2d43604058ae832ce63dec796c407 100644 ---- a/net/minecraft/world/level/ServerExplosion.java -+++ b/net/minecraft/world/level/ServerExplosion.java -@@ -379,6 +379,10 @@ public class ServerExplosion implements Explosion { - } - - private List calculateExplodedPositions() { -+ if (fun.bm.lophine.carpet.config.modules.GeneralCompatConfig.explosionNoBlockDamage) { -+ return List.of(); -+ } -+ - // Paper start - collision optimisations - final ObjectArrayList ret = new ObjectArrayList<>(); - -diff --git a/net/minecraft/world/level/block/CarvedPumpkinBlock.java b/net/minecraft/world/level/block/CarvedPumpkinBlock.java -index b45e5f571e72028996f641157473039ba9dd3520..8eeabbcf708644069e34503ad736c49b38c787d1 100644 ---- a/net/minecraft/world/level/block/CarvedPumpkinBlock.java -+++ b/net/minecraft/world/level/block/CarvedPumpkinBlock.java -@@ -59,7 +59,8 @@ public class CarvedPumpkinBlock extends HorizontalDirectionalBlock { - public boolean canSpawnGolem(LevelReader level, BlockPos pos) { - return this.getOrCreateSnowGolemBase().find(level, pos) != null - || this.getOrCreateIronGolemBase().find(level, pos) != null -- || this.getOrCreateCopperGolemBase().find(level, pos) != null; -+ || this.getOrCreateCopperGolemBase().find(level, pos) != null -+ || this.canSpawnShulkerGolem(level, pos); - } - - private void trySpawnGolem(Level level, BlockPos pos) { -@@ -92,6 +93,8 @@ public class CarvedPumpkinBlock extends HorizontalDirectionalBlock { - copperGolem.spawn(this.getWeatherStateFromPattern(blockPatternMatch2)); - } - } -+ -+ this.trySpawnShulkerGolem(level, pos); - } - - private WeatheringCopper.WeatherState getWeatherStateFromPattern(BlockPattern.BlockPatternMatch patternMatch) { -@@ -159,6 +162,42 @@ public class CarvedPumpkinBlock extends HorizontalDirectionalBlock { - builder.add(FACING); - } - -+ private boolean canSpawnShulkerGolem(LevelReader level, BlockPos pos) { -+ return fun.bm.lophine.carpet.config.modules.GeneralCompatConfig.shulkerGolem -+ && PUMPKINS_PREDICATE.test(level.getBlockState(pos)) -+ && level.getBlockState(pos.below()).is(BlockTags.SHULKER_BOXES); -+ } -+ -+ private void trySpawnShulkerGolem(Level level, BlockPos pos) { -+ if (!this.canSpawnShulkerGolem(level, pos)) { -+ return; -+ } -+ -+ net.minecraft.world.entity.monster.Shulker shulker = EntityType.SHULKER.create(level, EntitySpawnReason.TRIGGERED); -+ if (shulker == null) { -+ return; -+ } -+ -+ BlockPos bodyPos = pos.below(); -+ BlockState headState = level.getBlockState(pos); -+ BlockState bodyState = level.getBlockState(bodyPos); -+ shulker.snapTo(bodyPos.getX() + 0.5, bodyPos.getY(), bodyPos.getZ() + 0.5, 0.0F, 0.0F); -+ if (!level.addFreshEntity(shulker, org.bukkit.event.entity.CreatureSpawnEvent.SpawnReason.BUILD_IRONGOLEM)) { -+ return; -+ } -+ -+ level.setBlock(pos, Blocks.AIR.defaultBlockState(), Block.UPDATE_CLIENTS); -+ level.setBlock(bodyPos, Blocks.AIR.defaultBlockState(), Block.UPDATE_CLIENTS); -+ level.levelEvent(LevelEvent.PARTICLES_DESTROY_BLOCK, pos, Block.getId(headState)); -+ level.levelEvent(LevelEvent.PARTICLES_DESTROY_BLOCK, bodyPos, Block.getId(bodyState)); -+ level.updateNeighborsAt(pos, Blocks.AIR); -+ level.updateNeighborsAt(bodyPos, Blocks.AIR); -+ -+ for (ServerPlayer serverPlayer : level.getEntitiesOfClass(ServerPlayer.class, shulker.getBoundingBox().inflate(5.0))) { -+ CriteriaTriggers.SUMMONED_ENTITY.trigger(serverPlayer, shulker); -+ } -+ } -+ - private BlockPattern getOrCreateSnowGolemBase() { - if (this.snowGolemBase == null) { - this.snowGolemBase = BlockPatternBuilder.start() -diff --git a/net/minecraft/world/level/block/ChestBlock.java b/net/minecraft/world/level/block/ChestBlock.java -index 64c0616c294bd7c7b62ae376d50369500b948390..8797841c1c74fb6ff01bd93497d34500ebeca88a 100644 ---- a/net/minecraft/world/level/block/ChestBlock.java -+++ b/net/minecraft/world/level/block/ChestBlock.java -@@ -296,7 +296,8 @@ public class ChestBlock extends AbstractChestBlock implements - @Override - protected InteractionResult useWithoutItem(BlockState state, Level level, BlockPos pos, Player player, BlockHitResult hitResult) { - if (level instanceof ServerLevel serverLevel) { -- MenuProvider menuProvider = this.getMenuProvider(state, level, pos); -+ boolean ignoreObstructions = fun.bm.lophine.carpet.config.modules.GeneralCompatConfig.creativeOpenContainerForcibly && player.isCreative(); -+ MenuProvider menuProvider = this.getMenuProvider(state, level, pos, ignoreObstructions); - if (menuProvider != null && player.openMenu(menuProvider).isPresent()) { // Paper - Fix InventoryOpenEvent cancellation - player.awardStat(this.getOpenChestStat()); - PiglinAi.angerNearbyPiglins(serverLevel, player, true); -diff --git a/net/minecraft/world/level/block/EnderChestBlock.java b/net/minecraft/world/level/block/EnderChestBlock.java -index 202a986762794da9440365953fcb950c6eefa774..dc74a5ee0c6a45b9a0171226b480962a3f793ca2 100644 ---- a/net/minecraft/world/level/block/EnderChestBlock.java -+++ b/net/minecraft/world/level/block/EnderChestBlock.java -@@ -78,7 +78,8 @@ public class EnderChestBlock extends AbstractChestBlock i - PlayerEnderChestContainer enderChestInventory = player.getEnderChestInventory(); - if (enderChestInventory != null && level.getBlockEntity(pos) instanceof EnderChestBlockEntity enderChestBlockEntity) { - BlockPos blockPos = pos.above(); -- if (level.getBlockState(blockPos).isRedstoneConductor(level, blockPos)) { // Paper - diff on change; make sure that EnderChest#isBlocked uses the same logic -+ boolean canOpenForcibly = fun.bm.lophine.carpet.config.modules.GeneralCompatConfig.creativeOpenContainerForcibly && player.isCreative(); -+ if (!canOpenForcibly && level.getBlockState(blockPos).isRedstoneConductor(level, blockPos)) { // Paper - diff on change; make sure that EnderChest#isBlocked uses the same logic - return InteractionResult.SUCCESS; - } else { - // Paper start - Fix InventoryOpenEvent cancellation - moved up; -diff --git a/net/minecraft/world/level/block/FarmBlock.java b/net/minecraft/world/level/block/FarmBlock.java -index eb21b20a72062fc53b8f94aa98dcd380c8ad4db3..38bc450afb3cecc349290391f2c235866ad819b5 100644 ---- a/net/minecraft/world/level/block/FarmBlock.java -+++ b/net/minecraft/world/level/block/FarmBlock.java -@@ -133,7 +133,9 @@ public class FarmBlock extends Block { - return; - } - // CraftBukkit end -- turnToDirt(entity, state, level, pos); -+ if (!fun.bm.lophine.carpet.config.modules.GeneralCompatConfig.farmlandTrampledDisabled) { -+ turnToDirt(entity, state, level, pos); -+ } - } - - // super.fallOn(level, state, pos, entity, fallDistance); // CraftBukkit - moved up -diff --git a/net/minecraft/world/level/block/ObserverBlock.java b/net/minecraft/world/level/block/ObserverBlock.java -index 17c371f4c24e8a81705bdd322b1de5de4b8be8b7..ec049a5473b334032ce77cb44e144de687c14c21 100644 ---- a/net/minecraft/world/level/block/ObserverBlock.java -+++ b/net/minecraft/world/level/block/ObserverBlock.java -@@ -88,6 +88,9 @@ public class ObserverBlock extends DirectionalBlock { - } - - private void startSignal(LevelReader level, ScheduledTickAccess scheduledTickAccess, BlockPos pos) { -+ if (fun.bm.lophine.carpet.config.modules.GeneralCompatConfig.observerNoDetection) { -+ return; -+ } - if (!level.isClientSide() && !scheduledTickAccess.getBlockTicks().hasScheduledTick(pos, this)) { - scheduledTickAccess.scheduleTick(pos, this, 2); - } -diff --git a/net/minecraft/world/level/block/RedStoneWireBlock.java b/net/minecraft/world/level/block/RedStoneWireBlock.java -index 5c154d15e5aa45988436722595c7fca74e166c9a..070c9ad76193690e91bfbfe791ef4fa7cdbf36fe 100644 ---- a/net/minecraft/world/level/block/RedStoneWireBlock.java -+++ b/net/minecraft/world/level/block/RedStoneWireBlock.java -@@ -280,7 +280,8 @@ public class RedStoneWireBlock extends Block { - * Note: Added 'source' argument so as to help determine direction of information flow - */ - private void updateSurroundingRedstone(Level worldIn, BlockPos pos, BlockState state, @Nullable Orientation orientation, boolean blockAdded) { -- if (worldIn.paperConfig().misc.redstoneImplementation == io.papermc.paper.configuration.WorldConfiguration.Misc.RedstoneImplementation.EIGENCRAFT) { -+ if (!fun.bm.lophine.carpet.config.modules.GeneralCompatConfig.fastRedstoneDust -+ && worldIn.paperConfig().misc.redstoneImplementation == io.papermc.paper.configuration.WorldConfiguration.Misc.RedstoneImplementation.EIGENCRAFT) { - // since 24w33a the source pos is no longer given, but instead an Orientation parameter - // when this is not null, it can be used to find the source pos, which the turbo uses - // to find the direction of information flow -@@ -353,7 +354,8 @@ public class RedStoneWireBlock extends Block { - protected void onPlace(BlockState state, Level level, BlockPos pos, BlockState oldState, boolean movedByPiston) { - if (!oldState.is(state.getBlock()) && !level.isClientSide()) { - // Paper start - optimize redstone - replace call to updatePowerStrength -- if (level.paperConfig().misc.redstoneImplementation == io.papermc.paper.configuration.WorldConfiguration.Misc.RedstoneImplementation.ALTERNATE_CURRENT) { -+ if (fun.bm.lophine.carpet.config.modules.GeneralCompatConfig.fastRedstoneDust -+ || level.paperConfig().misc.redstoneImplementation == io.papermc.paper.configuration.WorldConfiguration.Misc.RedstoneImplementation.ALTERNATE_CURRENT) { - level.getWireHandler().onWireAdded(pos, state); // Alternate Current - } else { - this.updateSurroundingRedstone(level, pos, state, null, true); // Vanilla/Eigencraft -@@ -378,7 +380,8 @@ public class RedStoneWireBlock extends Block { - } - - // Paper start - optimize redstone - replace call to updatePowerStrength -- if (level.paperConfig().misc.redstoneImplementation == io.papermc.paper.configuration.WorldConfiguration.Misc.RedstoneImplementation.ALTERNATE_CURRENT) { -+ if (fun.bm.lophine.carpet.config.modules.GeneralCompatConfig.fastRedstoneDust -+ || level.paperConfig().misc.redstoneImplementation == io.papermc.paper.configuration.WorldConfiguration.Misc.RedstoneImplementation.ALTERNATE_CURRENT) { - level.getWireHandler().onWireRemoved(pos, state); // Alternate Current - } else { - this.updateSurroundingRedstone(level, pos, state, null, false); // Vanilla/Eigencraft -@@ -397,7 +400,8 @@ public class RedStoneWireBlock extends Block { - } - - // Paper start - optimize redstone - replace call to updatePowerStrength -- if (level.paperConfig().misc.redstoneImplementation == io.papermc.paper.configuration.WorldConfiguration.Misc.RedstoneImplementation.ALTERNATE_CURRENT) { -+ if (fun.bm.lophine.carpet.config.modules.GeneralCompatConfig.fastRedstoneDust -+ || level.paperConfig().misc.redstoneImplementation == io.papermc.paper.configuration.WorldConfiguration.Misc.RedstoneImplementation.ALTERNATE_CURRENT) { - level.getWireHandler().onWireRemoved(pos, state); // Alternate Current - } else { - this.updateSurroundingRedstone(level, pos, state, null, false); // Vanilla/Eigencraft -@@ -427,7 +431,8 @@ public class RedStoneWireBlock extends Block { - if (!level.isClientSide()) { - // Paper start - optimize redstone (Alternate Current) - // Alternate Current handles breaking of redstone wires in the WireHandler. -- if (level.paperConfig().misc.redstoneImplementation == io.papermc.paper.configuration.WorldConfiguration.Misc.RedstoneImplementation.ALTERNATE_CURRENT) { -+ if (fun.bm.lophine.carpet.config.modules.GeneralCompatConfig.fastRedstoneDust -+ || level.paperConfig().misc.redstoneImplementation == io.papermc.paper.configuration.WorldConfiguration.Misc.RedstoneImplementation.ALTERNATE_CURRENT) { - level.getWireHandler().onWireUpdated(pos, state, orientation); - } else - // Paper end - optimize redstone (Alternate Current) -diff --git a/net/minecraft/world/level/block/ShulkerBoxBlock.java b/net/minecraft/world/level/block/ShulkerBoxBlock.java -index e820cd03e695649a8f83e23ae822524a8f78e23b..6c58bc561116cff645d60f98294bc66fdb0e9919 100644 ---- a/net/minecraft/world/level/block/ShulkerBoxBlock.java -+++ b/net/minecraft/world/level/block/ShulkerBoxBlock.java -@@ -74,9 +74,10 @@ public class ShulkerBoxBlock extends BaseEntityBlock { - - @Override - protected InteractionResult useWithoutItem(BlockState state, Level level, BlockPos pos, Player player, BlockHitResult hitResult) { -+ boolean canOpenForcibly = fun.bm.lophine.carpet.config.modules.GeneralCompatConfig.creativeOpenContainerForcibly && player.isCreative(); - if (level instanceof ServerLevel serverLevel - && level.getBlockEntity(pos) instanceof ShulkerBoxBlockEntity shulkerBoxBlockEntity -- && canOpen(state, level, pos, shulkerBoxBlockEntity) // Paper - Fix InventoryOpenEvent cancellation - expand if for belows check -+ && (canOpen(state, level, pos, shulkerBoxBlockEntity) || canOpenForcibly) // Paper - Fix InventoryOpenEvent cancellation - expand if for belows check - && player.openMenu(shulkerBoxBlockEntity).isPresent()) { // Paper - Fix InventoryOpenEvent cancellation - player.awardStat(Stats.OPEN_SHULKER_BOX); - PiglinAi.angerNearbyPiglins(serverLevel, player, true); -diff --git a/net/minecraft/world/level/block/TntBlock.java b/net/minecraft/world/level/block/TntBlock.java -index 00da328f21e5ce5de6f968323b14a7832a6cbd62..a68ffb1197a19173f00577f656b5894f3c8ab85e 100644 ---- a/net/minecraft/world/level/block/TntBlock.java -+++ b/net/minecraft/world/level/block/TntBlock.java -@@ -47,7 +47,8 @@ public class TntBlock extends Block { - @Override - protected void onPlace(BlockState state, Level level, BlockPos pos, BlockState oldState, boolean movedByPiston) { - if (!oldState.is(state.getBlock())) { -- if (level.hasNeighborSignal(pos) && prime(level, pos, () -> org.bukkit.craftbukkit.event.CraftEventFactory.callTNTPrimeEvent(level, pos, org.bukkit.event.block.TNTPrimeEvent.PrimeCause.REDSTONE, null, null))) { // CraftBukkit - TNTPrimeEvent -+ if (shouldPrimeFromRedstone(level, pos) -+ && prime(level, pos, () -> org.bukkit.craftbukkit.event.CraftEventFactory.callTNTPrimeEvent(level, pos, org.bukkit.event.block.TNTPrimeEvent.PrimeCause.REDSTONE, null, null))) { // CraftBukkit - TNTPrimeEvent - level.removeBlock(pos, false); - } - } -@@ -55,7 +56,9 @@ public class TntBlock extends Block { - - @Override - protected void neighborChanged(BlockState state, Level level, BlockPos pos, Block neighborBlock, @Nullable Orientation orientation, boolean movedByPiston) { -- if (level.hasNeighborSignal(pos) && prime(level, pos, () -> org.bukkit.craftbukkit.event.CraftEventFactory.callTNTPrimeEvent(level, pos, org.bukkit.event.block.TNTPrimeEvent.PrimeCause.REDSTONE, null, null))) { // CraftBukkit - TNTPrimeEvent -+ if (!fun.bm.lophine.carpet.config.modules.GeneralCompatConfig.tntIgnoreRedstoneSignal -+ && level.hasNeighborSignal(pos) -+ && prime(level, pos, () -> org.bukkit.craftbukkit.event.CraftEventFactory.callTNTPrimeEvent(level, pos, org.bukkit.event.block.TNTPrimeEvent.PrimeCause.REDSTONE, null, null))) { // CraftBukkit - TNTPrimeEvent - level.removeBlock(pos, false); - } - } -@@ -74,7 +77,7 @@ public class TntBlock extends Block { - if (level.getGameRules().get(GameRules.TNT_EXPLODES)) { - PrimedTnt primedTnt = new PrimedTnt(level, pos.getX() + 0.5, pos.getY(), pos.getZ() + 0.5, explosion.getIndirectSourceEntity()); - int fuse = primedTnt.getFuse(); -- primedTnt.setFuse((short)(level.random.nextInt(fuse / 4) + fuse / 8)); -+ primedTnt.setFuse(level.random.nextInt(Math.max(fuse / 4, 1)) + fuse / 8); - level.addFreshEntity(primedTnt); - } - } -@@ -148,4 +151,10 @@ public class TntBlock extends Block { - protected void createBlockStateDefinition(StateDefinition.Builder builder) { - builder.add(UNSTABLE); - } -+ -+ private static boolean shouldPrimeFromRedstone(Level level, BlockPos pos) { -+ return !fun.bm.lophine.carpet.config.modules.GeneralCompatConfig.tntDoNotUpdate -+ && !fun.bm.lophine.carpet.config.modules.GeneralCompatConfig.tntIgnoreRedstoneSignal -+ && level.hasNeighborSignal(pos); -+ } - } -diff --git a/net/minecraft/world/level/block/entity/HopperBlockEntity.java b/net/minecraft/world/level/block/entity/HopperBlockEntity.java -index 474ca0ce0d3095fc586f8f9e9a584ed4f7c91471..07c2b0fb1fa2dd299687d389d7fe91c5fd5b5b91 100644 ---- a/net/minecraft/world/level/block/entity/HopperBlockEntity.java -+++ b/net/minecraft/world/level/block/entity/HopperBlockEntity.java -@@ -513,6 +513,7 @@ public class HopperBlockEntity extends RandomizableContainerBlockEntity implemen - for (int i = 0; i < blockEntity.getContainerSize(); i++) { - ItemStack item = blockEntity.getItem(i); - if (!item.isEmpty()) { -+ boolean noItemCost = fun.bm.lophine.carpet.config.modules.GeneralCompatConfig.hopperNoItemCost && org.leavesmc.leaves.util.WoolUtils.getWoolColorAtPosition(level, pos.above()) != null; - int count = item.getCount(); - // CraftBukkit start - Call event when pushing items into other inventories - ItemStack original = item.copy(); -@@ -544,15 +545,23 @@ public class HopperBlockEntity extends RandomizableContainerBlockEntity implemen - int origCount = event.getItem().getAmount(); // Spigot - ItemStack itemStack = HopperBlockEntity.addItem(blockEntity, attachedContainer, org.bukkit.craftbukkit.inventory.CraftItemStack.asNMSCopy(event.getItem()), opposite); - // CraftBukkit end -+ int moved = origCount - itemStack.getCount(); - -- if (itemStack.isEmpty()) { -+ if (moved > 0) { - attachedContainer.setChanged(); -+ if (noItemCost) { -+ blockEntity.setItem(i, original); -+ return true; -+ } -+ } -+ -+ if (itemStack.isEmpty()) { - return true; - } - - item.setCount(count); - // Spigot start -- item.shrink(origCount - itemStack.getCount()); -+ item.shrink(moved); - if (count <= level.spigotConfig.hopperAmount) { - // Spigot end - blockEntity.setItem(i, item); -diff --git a/net/minecraft/world/level/block/piston/PistonBaseBlock.java b/net/minecraft/world/level/block/piston/PistonBaseBlock.java -index 31907239431e83515b4e9c8b53dc1ee4d6f29e4d..6fed824d42e034f9a315c8ab74e05ba36c427787 100644 ---- a/net/minecraft/world/level/block/piston/PistonBaseBlock.java -+++ b/net/minecraft/world/level/block/piston/PistonBaseBlock.java -@@ -369,7 +369,7 @@ public class PistonBaseBlock extends DirectionalBlock { - - for (int i1 = toPush.size() - 1; i1 >= 0; i1--) { - // Paper start - fix a variety of piston desync dupes -- boolean allowDesync = io.papermc.paper.configuration.GlobalConfiguration.get().unsupportedSettings.allowPistonDuplication; -+ boolean allowDesync = !fun.bm.lophine.carpet.config.modules.GeneralCompatConfig.tntDupingFix; - BlockPos blockPos2; - BlockPos oldPos = blockPos2 = toPush.get(i1); - BlockState blockState1 = allowDesync ? level.getBlockState(oldPos) : null; -diff --git a/net/minecraft/world/level/block/state/BlockBehaviour.java b/net/minecraft/world/level/block/state/BlockBehaviour.java -index 0de2f6627fb6295d7927ac3e37026f4daf68cfd3..727abe6be81fdaa0753035bbf89ffeadf9c839b8 100644 ---- a/net/minecraft/world/level/block/state/BlockBehaviour.java -+++ b/net/minecraft/world/level/block/state/BlockBehaviour.java -@@ -828,6 +828,9 @@ public abstract class BlockBehaviour implements FeatureElement, org.leavesmc.lea - } - - public Vec3 getOffset(BlockPos pos) { -+ if (fun.bm.lophine.carpet.config.modules.GeneralCompatConfig.bambooModelNoOffset && (this.getBlock() == Blocks.BAMBOO || this.getBlock() == Blocks.BAMBOO_SAPLING)) { -+ return Vec3.ZERO; -+ } - BlockBehaviour.OffsetFunction offsetFunction = this.offsetFunction; - return offsetFunction != null ? offsetFunction.evaluate(this.asState(), pos) : Vec3.ZERO; - } -diff --git a/net/minecraft/world/level/chunk/LevelChunk.java b/net/minecraft/world/level/chunk/LevelChunk.java -index 8e56c2237f9d49d0b09b62c0933283f77aaae679..64bd2da70f4a0beb0efbb8a74478eef95ca013f2 100644 ---- a/net/minecraft/world/level/chunk/LevelChunk.java -+++ b/net/minecraft/world/level/chunk/LevelChunk.java -@@ -406,10 +406,11 @@ public class LevelChunk extends ChunkAccess implements DebugValueSource, ca.spot - boolean flag = !blockState.is(block); - boolean flag1 = (flags & Block.UPDATE_MOVE_BY_PISTON) != 0; - boolean flag2 = (flags & Block.UPDATE_SKIP_BLOCK_ENTITY_SIDEEFFECTS) == 0; -+ boolean skipInteractionUpdates = fun.bm.lophine.carpet.InteractionUpdateCompatHelper.shouldSkipUpdates(); - // Leaves start - behaviour 1.21.1- - if (!fun.bm.lophine.config.modules.experiment.RedStoneConfig.oldBlockRemoveBehaviour) { - if (flag && blockState.hasBlockEntity() && !state.shouldChangedStateKeepBlockEntity(blockState)) { -- if (!this.level.isClientSide() && flag2) { -+ if (!this.level.isClientSide() && flag2 && !skipInteractionUpdates) { - BlockEntity blockEntity = this.level.getBlockEntity(pos); - if (blockEntity != null) { - blockEntity.preRemoveSideEffects(pos, blockState); -@@ -421,6 +422,7 @@ public class LevelChunk extends ChunkAccess implements DebugValueSource, ca.spot - - if ((flag || block instanceof BaseRailBlock) - && this.level instanceof ServerLevel serverLevel -+ && !skipInteractionUpdates - && ((flags & Block.UPDATE_NEIGHBORS) != 0 || flag1)) { - blockState.affectNeighborsAfterRemoval(serverLevel, pos, flag1); - } -@@ -432,7 +434,10 @@ public class LevelChunk extends ChunkAccess implements DebugValueSource, ca.spot - if (!section.getBlockState(i, i1, i2).is(block)) { - return null; - } else { -- if (!this.level.isClientSide() && (flags & Block.UPDATE_SKIP_ON_PLACE) == 0 && (!this.level.getCurrentWorldData().captureBlockStates || block instanceof net.minecraft.world.level.block.BaseEntityBlock)) { // CraftBukkit - Don't place while processing the BlockPlaceEvent, unless it's a BlockContainer. Prevents blocks such as TNT from activating when cancelled. // Folia - region threading -+ if (!this.level.isClientSide() -+ && !skipInteractionUpdates -+ && (flags & Block.UPDATE_SKIP_ON_PLACE) == 0 -+ && (!this.level.getCurrentWorldData().captureBlockStates || block instanceof net.minecraft.world.level.block.BaseEntityBlock)) { // CraftBukkit - Don't place while processing the BlockPlaceEvent, unless it's a BlockContainer. Prevents blocks such as TNT from activating when cancelled. // Folia - region threading - state.onPlace(this.level, pos, blockState, flag1); - } - -diff --git a/net/minecraft/world/level/dimension/end/DragonRespawnAnimation.java b/net/minecraft/world/level/dimension/end/DragonRespawnAnimation.java -index 48187686126139a4229f454a3d4427f6593fa71b..562a27a051e1a5059e5bb81b64dc4c6def9f8673 100644 ---- a/net/minecraft/world/level/dimension/end/DragonRespawnAnimation.java -+++ b/net/minecraft/world/level/dimension/end/DragonRespawnAnimation.java -@@ -53,18 +53,18 @@ public enum DragonRespawnAnimation { - endCrystal.setBeamTarget(new BlockPos(endSpike.getCenterX(), endSpike.getHeight() + 1, endSpike.getCenterZ())); - } - } else { -- int i2 = 10; -+ if (!fun.bm.lophine.carpet.config.modules.GeneralCompatConfig.preventEndSpikeRespawn) { -+ for (BlockPos blockPos : BlockPos.betweenClosed( -+ new BlockPos(endSpike.getCenterX() - 10, endSpike.getHeight() - 10, endSpike.getCenterZ() - 10), -+ new BlockPos(endSpike.getCenterX() + 10, endSpike.getHeight() + 10, endSpike.getCenterZ() + 10) -+ )) { -+ level.removeBlock(blockPos, false); -+ } - -- for (BlockPos blockPos : BlockPos.betweenClosed( -- new BlockPos(endSpike.getCenterX() - 10, endSpike.getHeight() - 10, endSpike.getCenterZ() - 10), -- new BlockPos(endSpike.getCenterX() + 10, endSpike.getHeight() + 10, endSpike.getCenterZ() + 10) -- )) { -- level.removeBlock(blockPos, false); -+ level.explode( -+ null, endSpike.getCenterX() + 0.5F, endSpike.getHeight(), endSpike.getCenterZ() + 0.5F, 5.0F, Level.ExplosionInteraction.BLOCK -+ ); - } -- -- level.explode( -- null, endSpike.getCenterX() + 0.5F, endSpike.getHeight(), endSpike.getCenterZ() + 0.5F, 5.0F, Level.ExplosionInteraction.BLOCK -- ); - SpikeConfiguration spikeConfiguration = new SpikeConfiguration(true, ImmutableList.of(endSpike), new BlockPos(0, 128, 0)); - Feature.END_SPIKE - .place( -diff --git a/net/minecraft/world/level/levelgen/feature/SpikeFeature.java b/net/minecraft/world/level/levelgen/feature/SpikeFeature.java -index 70bb9a6742d236bb2f8f9b6bc43e973838ec5842..d111e19380372fd46059cddbaa1d3343deeb6918 100644 ---- a/net/minecraft/world/level/levelgen/feature/SpikeFeature.java -+++ b/net/minecraft/world/level/levelgen/feature/SpikeFeature.java -@@ -66,6 +66,10 @@ public class SpikeFeature extends Feature { - } - - private void placeSpike(ServerLevelAccessor level, RandomSource random, SpikeConfiguration config, SpikeFeature.EndSpike spike) { -+ if (fun.bm.lophine.carpet.config.modules.GeneralCompatConfig.preventEndSpikeRespawn && config.getCrystalBeamTarget() != null) { -+ return; -+ } -+ - int radius = spike.getRadius(); - - for (BlockPos blockPos : BlockPos.betweenClosed( -diff --git a/net/minecraft/world/level/redstone/NeighborUpdater.java b/net/minecraft/world/level/redstone/NeighborUpdater.java -index 32488203ff074b675f5c8831d157b1d91bae48a9..2c5f4006cf56921bee30223bf76b18a8e2ef56c6 100644 ---- a/net/minecraft/world/level/redstone/NeighborUpdater.java -+++ b/net/minecraft/world/level/redstone/NeighborUpdater.java -@@ -34,6 +34,9 @@ public interface NeighborUpdater { - static void executeShapeUpdate( - LevelAccessor level, Direction direction, BlockPos pos, BlockPos neighborPos, BlockState neighborState, @Block.UpdateFlags int flags, int recursionLeft - ) { -+ if (fun.bm.lophine.carpet.config.modules.GeneralCompatConfig.totallyNoBlockUpdate) { -+ return; -+ } - BlockState blockState = level.getBlockState(pos); - if ((flags & Block.UPDATE_SKIP_SHAPE_UPDATE_ON_WIRE) == 0 || !blockState.is(Blocks.REDSTONE_WIRE)) { - BlockState blockState1 = blockState.updateShape(level, level, pos, direction, neighborPos, neighborState, level.getRandom()); -@@ -48,6 +51,9 @@ public interface NeighborUpdater { - - static void executeUpdate(Level level, BlockState state, BlockPos pos, Block neighborBlock, @Nullable Orientation orientation, boolean movedByPiston, BlockPos sourcePos) { - // Paper end - Add source block to BlockPhysicsEvent -+ if (fun.bm.lophine.carpet.config.modules.GeneralCompatConfig.totallyNoBlockUpdate) { -+ return; -+ } - try { - // CraftBukkit start - org.bukkit.event.block.BlockPhysicsEvent event = new org.bukkit.event.block.BlockPhysicsEvent(org.bukkit.craftbukkit.block.CraftBlock.at(level, pos), org.bukkit.craftbukkit.block.data.CraftBlockData.fromData(state), org.bukkit.craftbukkit.block.CraftBlock.at(level, sourcePos)); // Paper - Add source block to BlockPhysicsEvent diff --git a/1todos/server/src/lophine/protocol/CarpetLoggerProtocol.java b/1todos/server/src/lophine/protocol/CarpetLoggerProtocol.java deleted file mode 100644 index b71f9cf..0000000 --- a/1todos/server/src/lophine/protocol/CarpetLoggerProtocol.java +++ /dev/null @@ -1,302 +0,0 @@ -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> PLAYER_SUBSCRIPTIONS = new ConcurrentHashMap<>(); - private static volatile Map configuredSubscriptions = null; - - public static void refreshConfiguredDefaults(boolean initial) { - Map 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 configuredLoggers) { - List 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 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 subscriptions) { - List 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 buildCounterLines(MinecraftServer server, String option) { - List 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 parseConfiguredDefaults(List configuredLoggers) { - LinkedHashMap 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 -> ""; - }; - } -} diff --git a/1todos/server/src/lophine/protocol/tiscm/TISCMProtocol.java b/1todos/server/src/lophine/protocol/tiscm/TISCMProtocol.java deleted file mode 100644 index 96f7996..0000000 --- a/1todos/server/src/lophine/protocol/tiscm/TISCMProtocol.java +++ /dev/null @@ -1,201 +0,0 @@ -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> 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 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 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 packets = CLIENT_SUPPORTED_PACKETS.get(player.getStringUUID()); - return packets != null && packets.contains(packet); - } - - private static ListTag stringList(List 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 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 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 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 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 CODEC = StreamCodec.composite( - ByteBufCodecs.STRING_UTF8, - TISCMPayload::packetId, - ByteBufCodecs.COMPOUND_TAG, - TISCMPayload::nbt, - TISCMPayload::new - ); - } -}