Minecraft Forge 1.19.2:海绵放置时添加方块更新Tick延迟
解决海绵吸水时服务器过载与方块更新延迟问题
问题分析
你的代码在放置海绵时通过同步while循环批量替换水方块,会导致两个核心问题:
- 主线程被长时间阻塞,服务器无法处理其他任务,引发界面冻结
- 一次性执行大量
setBlock操作,服务器来不及同步更新,出现方块延迟
解决方案:分批次延迟处理
核心思路是将批量方块操作拆分为多轮,每轮处理一部分方块,通过服务器调度任务延迟1Tick执行下一轮,避免阻塞主线程,同时让服务器有时间同步方块状态。
修改后的代码
import net.minecraft.core.BlockPos; import net.minecraft.world.entity.Entity; import net.minecraft.world.level.Level; import net.minecraft.world.level.block.Blocks; import net.minecraft.world.level.block.state.BlockState; import net.minecraftforge.event.entity.EntityPlaceEvent; import java.util.HashSet; import java.util.LinkedList; import java.util.Queue; import java.util.Set; public class SpongeHandler { // 每批处理的方块数量,可根据服务器性能调整(建议8-15) private static final int BATCH_SIZE = 8; public static void spongePlace(EntityPlaceEvent event) { Entity entity = event.getEntity(); Level level = entity.level; if (level.isClientSide()) return; BlockState placedBlock = event.getState(); // 用is()判断方块类型,比直接比较BlockState更可靠 if (!placedBlock.is(Blocks.SPONGE)) return; BlockPos spongePos = event.getPos(); int spongeY = spongePos.getY(); // 待处理方块队列 Queue<BlockPos> pendingBlocks = new LinkedList<>(); // 已处理/待处理位置集合,避免重复操作 Set<BlockPos> processedPositions = new HashSet<>(); pendingBlocks.add(spongePos); processedPositions.add(spongePos); // 延迟1Tick启动处理,让服务器先完成海绵放置事件 level.getServer().tell(() -> processSpongeBatch(level, pendingBlocks, processedPositions, spongeY), 1); } private static void processSpongeBatch(Level level, Queue<BlockPos> pendingBlocks, Set<BlockPos> processedPositions, int spongeY) { if (pendingBlocks.isEmpty()) return; BlockState waterState = Blocks.WATER.defaultBlockState(); int processedCount = 0; // 处理当前批次的方块 while (!pendingBlocks.isEmpty() && processedCount < BATCH_SIZE) { BlockPos currentPos = pendingBlocks.poll(); processedCount++; // 将当前方块替换为空气 level.setBlock(currentPos, Blocks.AIR.defaultBlockState(), 2); // 检查四个水平方向的邻居(仅处理同一Y层的水方块) BlockPos[] neighbors = { currentPos.east(), currentPos.west(), currentPos.south(), currentPos.north() }; for (BlockPos neighbor : neighbors) { // 仅处理未被记录、且为水方块的同一Y层位置 if (neighbor.getY() == spongeY && level.getBlockState(neighbor).equals(waterState) && processedPositions.add(neighbor)) { pendingBlocks.add(neighbor); } } } // 若还有未处理方块,延迟1Tick调度下一批处理 if (!pendingBlocks.isEmpty()) { level.getServer().tell(() -> processSpongeBatch(level, pendingBlocks, processedPositions, spongeY), 1); } } }
关键优化点
- 分批次处理:每轮最多处理
BATCH_SIZE个方块,避免一次性占用过多主线程资源 - 延迟调度:每批处理后延迟1Tick执行下一轮,给服务器留出时间处理其他任务,解决更新节奏问题
- 去重机制:用
Set记录已处理/待处理位置,避免重复操作同一方块 - 可靠的方块判断:用
is()判断方块类型,用equals()比较BlockState,避免引用比较的潜在问题 - 异步主线程调度:使用
level.getServer().tell()在主线程内延迟执行,保证方块操作的线程安全性(Minecraft方块操作必须在主线程)
调整建议
- 根据服务器硬件性能调整
BATCH_SIZE:性能好的服务器可适当提高数值(如15),性能一般的服务器降低数值(如5),平衡处理速度与流畅度 - 若需要更长的延迟(如2Tick),可将调度时的延迟参数从
1改为对应数值
内容的提问来源于stack exchange,提问作者Cavalry
相关产品推荐
相关产品推荐

