You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Minecraft Forge 1.19.2:海绵放置时添加方块更新Tick延迟

解决海绵吸水时服务器过载与方块更新延迟问题

问题分析

你的代码在放置海绵时通过同步while循环批量替换水方块,会导致两个核心问题:

  1. 主线程被长时间阻塞,服务器无法处理其他任务,引发界面冻结
  2. 一次性执行大量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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.22 02:17:37