激活拉杆后Minecraft服务器卡顿优化:延迟批量删块方案
Minecraft模组批量删块卡顿优化方案
问题背景
开发模组时,激活拉杆后删除50格半径范围内的方块会导致服务器卡顿。需求是:拉杆激活后先播放声音,等待120秒再执行批量删块操作。
现有代码的核心问题
- 主线程阻塞:在
replaceBlocksInSphere中直接调用sleep(12000)(注:代码中是12秒,和需求的120秒不符),会阻塞服务器主线程,导致整个服务器在等待期间完全无响应。 - 一次性批量操作:所有方块删除操作集中在单帧完成,大量同步IO和世界更新瞬间占用服务器资源,引发卡顿。
- 不必要的对象创建:循环中每次创建新的
BlockPos,增加内存垃圾回收压力。
优化后的代码实现
@Override public void neighborChanged(BlockState blockstate, net.minecraft.world.level.Level world, BlockPos pos, Block neighborBlock, BlockPos fromPos, boolean moving) { super.neighborChanged(blockstate, world, pos, neighborBlock, fromPos, moving); if (world instanceof ServerLevel serverLevel && serverLevel.hasNeighborSignal(pos)) { // 立即播放声音 playSound(serverLevel, pos); strength = -1f; resistance = 3600000f; // 延迟120秒(120000毫秒)执行删块任务,避免阻塞主线程 serverLevel.getServer().getScheduler().scheduleDelayedTask( () -> startBatchBlockRemoval(serverLevel, pos), 120000L, TimeUnit.MILLISECONDS ); } } // 分批次处理方块删除的入口方法 private void startBatchBlockRemoval(ServerLevel world, BlockPos centerPos) { int radius = RADIUS; int radiusSqr = radius * radius; // 计算需要处理的区块范围 int chunkRadius = (int) Math.ceil((double) radius / 16); int centerChunkX = centerPos.getX() >> 4; int centerChunkZ = centerPos.getZ() >> 4; // 收集所有需要删除的方块位置 List<BlockPos> blocksToRemove = new ArrayList<>(); BlockPos.MutableBlockPos mutablePos = new BlockPos.MutableBlockPos(); for (int chunkXOffset = -chunkRadius; chunkXOffset <= chunkRadius; chunkXOffset++) { for (int chunkZOffset = -chunkRadius; chunkZOffset <= chunkRadius; chunkZOffset++) { int chunkX = centerChunkX + chunkXOffset; int chunkZ = centerChunkZ + chunkZOffset; int startX = chunkX << 4; int endX = startX + 16; int startZ = chunkZ << 4; int endZ = startZ + 16; int startY = Math.max(world.getMinBuildHeight(), centerPos.getY() - radius); int endY = Math.min(world.getMaxBuildHeight(), centerPos.getY() + radius); for (int y = startY; y <= endY; y++) { for (int x = startX; x < endX; x++) { for (int z = startZ; z < endZ; z++) { mutablePos.set(x, y, z); if (centerPos.distSqr(mutablePos) <= radiusSqr) { blocksToRemove.add(mutablePos.immutable()); } } } } } } // 分批次处理删除,每批次处理100个方块,间隔1 tick(约50毫秒) batchRemoveBlocks(world, blocksToRemove, 0, 100); } // 递归分批次删除方块 private void batchRemoveBlocks(ServerLevel world, List<BlockPos> blocksToRemove, int startIndex, int batchSize) { if (startIndex >= blocksToRemove.size()) { return; } int endIndex = Math.min(startIndex + batchSize, blocksToRemove.size()); BlockPos.MutableBlockPos mutablePos = new BlockPos.MutableBlockPos(); for (int i = startIndex; i < endIndex; i++) { BlockPos pos = blocksToRemove.get(i); mutablePos.set(pos); // 如果方块不是空气才进行删除,避免无效操作 if (!world.getBlockState(mutablePos).isAir()) { world.setBlock(mutablePos, Blocks.AIR.defaultBlockState(), 3); // 3表示通知客户端更新,比setBlockAndUpdate更轻量,可根据需求调整 } } // 调度下一批次任务,间隔1 tick分散负载 world.getServer().getScheduler().scheduleDelayedTask( () -> batchRemoveBlocks(world, blocksToRemove, endIndex, batchSize), 1L, TimeUnit.MILLISECONDS ); }
优化说明
- 异步延迟任务:用服务器调度器
scheduleDelayedTask替代sleep,既实现120秒延迟,又不会阻塞主线程,保证服务器正常运行。 - 分批次处理:先收集所有待删除方块,再分批次逐步删除,每批次间隔1 tick,分散服务器负载,避免瞬间卡顿。
- 可变BlockPos复用:全程使用
BlockPos.MutableBlockPos减少对象创建,降低内存开销和GC压力。 - 边界范围优化:限制Y轴操作在世界合法建造高度内,避免无效的世界访问。
- 无效操作过滤:删除前检查方块是否为空气,跳过不必要的更新操作。
内容的提问来源于stack exchange,提问作者HLNikNiky
相关产品推荐
相关产品推荐

