强曰为道

与天地相似,故不违。知周乎万物,而道济天下,故不过。旁行而不流,乐天知命,故不忧.
文档目录

第 8 章:世界操作

第 8 章:世界操作

掌握世界管理、区块加载、实体生成和结构生成的核心 API。


8.1 世界管理

获取世界

import org.bukkit.Bukkit;
import org.bukkit.World;
import org.bukkit.WorldCreator;

// 获取已加载的世界
World overworld = Bukkit.getWorld("world");
World nether = Bukkit.getWorld("world_nether");
World end = Bukkit.getWorld("world_the_end");

// 获取所有已加载的世界
List<World> worlds = Bukkit.getWorlds();

// 获取世界属性
for (World w : worlds) {
    getLogger().info(w.getName() + ": "
        + w.getEnvironment() + " | "
        + w.getPlayers().size() + " 玩家");
}

动态创建世界

public class WorldManager {

    /**
     * 创建一个新世界
     */
    public static World createWorld(String name, World.Environment env,
                                     long seed, boolean structures) {
        WorldCreator creator = new WorldCreator(name);
        creator.environment(env);
        creator.seed(seed);
        creator.generateStructures(structures);

        // 设置世界类型
        creator.type(WorldType.NORMAL); // NORMAL, FLAT, LARGE_BIOMES

        return creator.createWorld();
    }

    /**
     * 创建超平坦世界
     */
    public static World createFlatWorld(String name) {
        WorldCreator creator = new WorldCreator(name);
        creator.environment(World.Environment.NORMAL);
        creator.type(WorldType.FLAT);
        return creator.createWorld();
    }

    /**
     * 创建虚空世界
     */
    public static World createVoidWorld(String name) {
        WorldCreator creator = new WorldCreator(name);
        creator.environment(World.Environment.NORMAL);
        creator.generator(new VoidChunkGenerator());
        return creator.createWorld();
    }
}

自定义空世界生成器

package com.example.myplugin.worlds;

import org.bukkit.World;
import org.bukkit.generator.ChunkGenerator;
import org.bukkit.generator.WorldInfo;
import org.jetbrains.annotations.NotNull;

import java.util.Random;

public class VoidChunkGenerator extends ChunkGenerator {

    @Override
    public void generateNoise(@NotNull WorldInfo worldInfo,
                               @NotNull Random random,
                               int chunkX, int chunkZ,
                               @NotNull ChunkData chunkData) {
        // 不生成任何方块 = 虚空
    }

    @Override
    public void generateSurface(@NotNull WorldInfo worldInfo,
                                 @NotNull Random random,
                                 int chunkX, int chunkZ,
                                 @NotNull ChunkData chunkData) {
        // 不生成地表
    }

    @Override
    public void generateBedrock(@NotNull WorldInfo worldInfo,
                                 @NotNull Random random,
                                 int chunkX, int chunkZ,
                                 @NotNull ChunkData chunkData) {
        // 不生成基岩
    }

    @Override
    public void generateCaves(@NotNull WorldInfo worldInfo,
                               @NotNull Random random,
                               int chunkX, int chunkZ,
                               @NotNull ChunkData chunkData) {
        // 不生成洞穴
    }
}

卸载和删除世界

/**
 * 卸载世界(不删除文件)
 */
public static boolean unloadWorld(String name) {
    World world = Bukkit.getWorld(name);
    if (world == null) return false;

    // 将玩家传送到主世界
    World mainWorld = Bukkit.getWorlds().get(0);
    for (Player player : world.getPlayers()) {
        player.teleport(mainWorld.getSpawnLocation());
    }

    return Bukkit.unloadWorld(world, true); // true = 保存区块
}

/**
 * 删除世界(包括文件)
 */
public static boolean deleteWorld(File worldFolder) {
    if (!worldFolder.exists()) return false;

    File[] files = worldFolder.listFiles();
    if (files != null) {
        for (File file : files) {
            if (file.isDirectory()) {
                deleteWorld(file);
            }
            file.delete();
        }
    }
    return worldFolder.delete();
}

8.2 区块管理

区块基础

Minecraft 世界以 16×16 的区块(Chunk)为单位加载和卸载。理解区块管理对性能至关重要。

概念说明
Chunk16×16 区块,包含该区域所有方块和实体
区块加载将区块数据从磁盘读入内存
区块卸载将不再需要的区块从内存移除
生成半径服务器自动加载的区块范围

加载区块

// 同步加载区块(阻塞主线程,慎用)
Chunk chunk = world.getChunkAt(100, 200); // 区块坐标

// 异步加载区块(推荐)
world.getChunkAtAsync(100, 200).thenAccept(chunk -> {
    // 区块加载完成(可能在异步线程)
    Bukkit.getScheduler().runTask(plugin, () -> {
        // 回到主线程操作
        doSomethingWithChunk(chunk);
    });
});

// 检查区块是否已加载
Chunk chunk = world.getChunkAt(100, 200);
boolean loaded = chunk.isLoaded();

区块票(Chunk Ticket)

防止区块被自动卸载:

// 添加区块票(保持加载)
chunk.addPluginChunkTicket(plugin);

// 移除区块票
chunk.removePluginChunkTicket(plugin);

// 检查是否有票
boolean hasTicket = chunk.isPluginChunkTicket(plugin);

区块加载事件

public class ChunkListener implements Listener {

    @EventHandler
    public void onChunkLoad(ChunkLoadEvent event) {
        Chunk chunk = event.getChunk();
        // 区块刚加载时触发
    }

    @EventHandler
    public void onChunkUnload(ChunkUnloadEvent event) {
        Chunk chunk = event.getChunk();
        // 区块即将卸载
    }

    @EventHandler
    public void onChunkPopulate(ChunkPopulateEvent event) {
        // 区块首次生成完成
        Chunk chunk = event.getChunk();
        // 可以在这里添加自定义结构
    }
}

8.3 位置与向量

Location

import org.bukkit.Location;
import org.bukkit.World;

// 创建位置
Location loc = new Location(world, 100.5, 64.0, -50.5);

// 从已有位置获取信息
double x = loc.getX();
double y = loc.getY();
double z = loc.getZ();
float yaw = loc.getYaw();    // 水平视角
float pitch = loc.getPitch(); // 垂直视角
World world = loc.getWorld();

// 获取方块位置(整数坐标)
Block block = loc.getBlock();
int bx = block.getX();
int by = block.getY();
int bz = block.getZ();

// 位置运算
Location offset = loc.clone().add(0, 10, 0);     // 向上 10 格
Location forward = loc.clone().add(loc.getDirection().multiply(5)); // 向前 5 格

// 距离计算
double distance = loc1.distance(loc2);
double distanceSquared = loc1.distanceSquared(loc2); // 更高效(不开方)

向量运算

import org.bukkit.util.Vector;

Vector v1 = new Vector(1, 2, 3);
Vector v2 = new Vector(4, 5, 6);

// 加法
Vector sum = v1.clone().add(v2);

// 标量乘法
Vector scaled = v1.clone().multiply(2);

// 标准化(长度为 1)
Vector normalized = v1.clone().normalize();

// 点积
double dot = v1.dot(v2);

// 叉积
Vector cross = v1.clone().crossProduct(v2);

// 应用到实体(击退效果)
Vector direction = player.getLocation().getDirection();
entity.setVelocity(direction.multiply(2).setY(0.5));

8.4 实体生成

基本生成

import org.bukkit.entity.EntityType;

// 在指定位置生成实体
Location loc = player.getLocation();
World world = loc.getWorld();

// 生成僵尸
Zombie zombie = (Zombie) world.spawnEntity(loc, EntityType.ZOMBIE);

// 生成苦力怕
Creeper creeper = (Creeper) world.spawnEntity(loc, EntityType.CREEPER);
creeper.setPowered(true); // 高压苦力怕

// 生成经验球
ExperienceOrb orb = world.spawn(loc, ExperienceOrb.class);
orb.setExperience(100);

使用 Consumer 设置属性

// 使用 spawn 方法的 Consumer 参数(原子操作)
world.spawn(loc, Zombie.class, zombie -> {
    zombie.setCustomName(Component.text("§c精英僵尸"));
    zombie.setCustomNameVisible(true);
    zombie.setHealth(40); // 双倍血量
    zombie.getEquipment().setItemInMainHand(
        new ItemStack(Material.DIAMOND_SWORD)
    );
    zombie.getEquipment().setHelmet(
        new ItemStack(Material.DIAMOND_HELMET)
    );
});

掉落物生成

// 生成掉落物
ItemStack item = new ItemStack(Material.DIAMOND, 3);
world.dropItem(loc, item);

// 不拾取延迟的掉落物
Item itemEntity = world.dropItem(loc, item);
itemEntity.setPickupDelay(40); // 2 秒后才能拾取

// 生成自然掉落效果
world.dropItemNaturally(loc, item); // 有随机偏移

8.5 方块操作

基本方块操作

// 获取方块
Block block = world.getBlockAt(100, 64, -50);

// 获取方块类型
Material type = block.getType();

// 设置方块类型
block.setType(Material.DIAMOND_BLOCK);

// 设置方块(带方块数据)
block.setType(Material.OAK_STAIRS);
BlockData data = block.getBlockData();
// 设置朝向等属性
block.setBlockData(data);

批量方块操作

/**
 * 填充一个立方体区域
 */
public static void fillRegion(World world,
                               int x1, int y1, int z1,
                               int x2, int y2, int z2,
                               Material material) {
    int minX = Math.min(x1, x2);
    int minY = Math.min(y1, y2);
    int minZ = Math.min(z1, z2);
    int maxX = Math.max(x1, x2);
    int maxY = Math.max(y1, y2);
    int maxZ = Math.max(z1, z2);

    for (int x = minX; x <= maxX; x++) {
        for (int y = minY; y <= maxY; y++) {
            for (int z = minZ; z <= maxZ; z++) {
                world.getBlockAt(x, y, z).setType(material);
            }
        }
    }
}

/**
 * 生成球体
 */
public static void createSphere(Location center, int radius, Material material) {
    World world = center.getWorld();
    int cx = center.getBlockX();
    int cy = center.getBlockY();
    int cz = center.getBlockZ();

    for (int x = -radius; x <= radius; x++) {
        for (int y = -radius; y <= radius; y++) {
            for (int z = -radius; z <= radius; z++) {
                if (x * x + y * y + z * z <= radius * radius) {
                    world.getBlockAt(cx + x, cy + y, cz + z).setType(material);
                }
            }
        }
    }
}

8.6 结构生成

放置结构

import org.bukkit.StructureType;
import org.bukkit.generator.structure.Structure;

/**
 * 在指定位置放置结构
 */
public static boolean placeStructure(Location loc) {
    World world = loc.getWorld();
    if (world == null) return false;

    // 使用 StructurePlaceSettings
    Structure structure = world.locateNearestStructure(
        loc, Structure.VILLAGE_PLAINS, 1000, false
    );

    // 手动生成村庄
    return world.generateStructure(loc, Structure.VILLAGE_PLAINS);
}

/**
 * 使用 NMS 或 API 放置结构
 */
public static void placeWithPaste(Location loc) {
    World world = loc.getWorld();
    if (world == null) return;

    // 使用 StructureManager
    var server = Bukkit.getServer();
    // Paper 提供了结构放置 API
}

8.7 粒子与音效

粒子效果

import org.bukkit.Particle;
import org.bukkit.Color;

// 发送粒子
world.spawnParticle(Particle.FLAME, location, 50);       // 50 个火焰粒子
world.spawnParticle(Particle.CLOUD, location, 20, 0.5, 0.5, 0.5); // 带偏移

// 自定义颜色粒子
world.spawnParticle(
    Particle.DUST,
    location,
    30,                         // 数量
    0.5, 0.5, 0.5,            // 偏移
    new Particle.DustOptions(Color.RED, 1.0f) // 红色,大小 1.0
);

// 玩家专用粒子(只对特定玩家可见)
player.spawnParticle(Particle.HEART, player.getLocation(), 5);

音效

import org.bukkit.Sound;

// 在世界播放音效
world.playSound(location, Sound.ENTITY_PLAYER_LEVELUP, 1.0f, 1.0f);

// 只对特定玩家播放
player.playSound(player.getLocation(), Sound.BLOCK_NOTE_BLOCK_PLING, 1.0f, 2.0f);

// 全服播放
for (Player p : Bukkit.getOnlinePlayers()) {
    p.playSound(p.getLocation(), Sound.UI_TOAST_CHALLENGE_COMPLETE, 1.0f, 1.0f);
}

8.8 天气与时间

// 设置时间
world.setTime(0);      // 日出
world.setTime(6000);   // 正午
world.setTime(13000);  // 日落
world.setTime(18000);  // 午夜

// 获取当前时间
long time = world.getTime();
boolean isDay = world.isDayTime();

// 设置天气
world.setStorm(true);      // 开启下雨
world.setThundering(true); // 开启雷暴
world.setWeatherDuration(6000); // 持续 5 分钟

// 清除天气
world.setStorm(false);
world.setThundering(false);
world.setClearWeatherDuration(6000);

// 设置游戏规则
world.setGameRule(GameRule.DO_DAYLIGHT_CYCLE, false);
world.setGameRule(GameRule.DO_MOB_SPAWNING, false);
world.setGameRule(GameRule.KEEP_INVENTORY, true);

8.9 业务场景:多世界竞技场

public class ArenaManager {

    private final Map<String, Arena> arenas = new HashMap<>();

    /**
     * 创建竞技场
     */
    public Arena createArena(String name, Location pos1, Location pos2) {
        // 复制竞技场区域作为模板
        String templateWorld = pos1.getWorld().getName();
        String arenaWorld = "arena_" + name + "_" + System.currentTimeMillis();

        // 创建竞技场世界
        WorldCreator creator = new WorldCreator(arenaWorld);
        creator.copy(Bukkit.getWorld(templateWorld));
        World world = creator.createWorld();

        Arena arena = new Arena(name, world, pos1, pos2);
        arenas.put(name, arena);
        return arena;
    }

    /**
     * 重置竞技场(恢复原始状态)
     */
    public void resetArena(Arena arena) {
        // 卸载当前世界
        unloadWorld(arena.getWorld().getName());

        // 从模板重新复制
        // ...(类似创建流程)
    }

    /**
     * 传送玩家到竞技场
     */
    public void teleportToArena(Player player, Arena arena) {
        Location spawn = arena.getSpawnLocation();
        player.teleport(spawn);

        // 清空背包
        player.getInventory().clear();
        player.setHealth(20.0);
        player.setFoodLevel(20);
    }
}

8.10 性能注意事项

操作风险优化方案
同步加载区块阻塞主线程使用 getChunkAtAsync()
批量修改方块大量事件触发使用 NMS ChunkSnapshot
生成大量实体TPS 下降限制实体密度,分批生成
频繁读写世界I/O 压力使用区块票保持关键区域加载

8.11 扩展阅读


8.12 本章小结

要点内容
世界管理创建、卸载、删除世界,支持自定义生成器
区块管理异步加载、区块票、监听加载/卸载事件
实体生成world.spawnEntity()world.spawn()
方块操作批量修改需要注意性能,使用偏移优化
结构生成使用 API 放置村庄、神殿等结构
粒子音效丰富游戏体验,支持单人/全局播放

下一章: 第 9 章:实体 API — 深入实体操作、自定义生物 AI 和装备系统。