Skip to content

房间系统

概述

房间系统是灾难玩法的核心框架,负责管理游戏房间的创建、玩家加入/退出、游戏流程控制等。每个房间是一个独立的游戏实例,拥有自己的世界、玩家集合和游戏状态。

核心类

RoomManager(房间管理器)

路径cn.enderrealm.disaster.room.RoomManager

房间管理器是全局单例,负责管理所有房间实例。

java
public class RoomManager {
    private final disaster plugin;
    private final Map<String, Room> rooms;  // 房间ID -> 房间实例
    private final Map<Room, GameManager> gameManagers;  // 房间 -> 游戏管理器
    private String roomPrefix;  // 房间世界前缀
    private int nextRoomId;     // 下一个房间ID
}

主要职责

  1. 房间生命周期管理

    • 创建新房间(复制地图模板)
    • 查找可用房间
    • 清理空房间
    • 卸载和删除房间世界
  2. 地图模板管理

    • plugins/disaster/worlds/ 目录加载地图模板
    • 随机选择地图
    • 复制世界文件
  3. 游戏管理器关联

    • 注册房间与游戏管理器的关联
    • 获取房间的游戏管理器

核心方法

java
// 创建新房间
public Room createRoom() {
    // 1. 从 worlds 目录获取地图模板
    File worldsFolder = new File(plugin.getDataFolder(), "worlds");
    File[] mapFolders = worldsFolder.listFiles(File::isDirectory);
    
    // 2. 随机选择一个地图模板
    File mapFolder = mapFolders[(int) (Math.random() * mapFolders.length)];
    
    // 3. 创建新的世界名称
    String worldName = roomPrefix + nextRoomId++;
    
    // 4. 复制地图文件并加载世界
    copyWorldFiles(mapFolder, serverWorldFolder);
    World world = new WorldCreator(worldName).createWorld();
    
    // 5. 创建房间实例
    return new Room(plugin, world, waitingLocation, mapFolder);
}

// 查找可用房间
public Room getAvailableRoom() {
    for (Room room : rooms.values()) {
        if (!room.isFull() && room.isWaiting() && !room.isStarted()) {
            return room;
        }
    }
    return null;
}

// 加入房间
public void joinRoom(Player player) {
    Room room = getAvailableRoom();
    if (room == null) {
        room = createRoom();
    }
    if (room != null) {
        room.addPlayer(player);
    }
}

Room(房间类)

路径cn.enderrealm.disaster.room.Room

房间类代表一个游戏房间实例,管理房间内的玩家和游戏状态。

java
public class Room {
    private final World world;              // 房间世界
    private final Set<Player> players;      // 房间内玩家
    private Location waitingLocation;       // 等待位置
    private final disaster plugin;          // 插件实例
    private boolean isWait;                 // 是否等待中
    private boolean isStart;                // 是否已开始
    private int minPlayers;                 // 最小玩家数
    private int maxPlayers;                 // 最大玩家数
    private int countdownTime;              // 倒计时时间
    private BukkitTask countdownTask;       // 倒计时任务
    private String mapName;                 // 地图名称
    private File mapFolder;                 // 地图文件夹
}

房间状态

状态描述
isWait = true, isStart = false等待玩家加入
isWait = false, isStart = true游戏进行中
isWait = false, isStart = false游戏已结束

玩家管理

java
// 玩家加入房间
public void addPlayer(Player player) {
    players.add(player);
    player.teleport(waitingLocation);
    
    // 清空玩家背包
    player.getInventory().clear();
    
    // 添加恢复效果
    player.addPotionEffect(new PotionEffect(PotionEffectType.REGENERATION, 30, 2, false, false));
    
    // 添加退出物品(粘液球)
    ItemStack quitItem = new ItemStack(Material.SLIME_BALL);
    player.getInventory().setItem(8, quitItem);
    
    // 显示房间信息
    showRoomInfo(player);
    
    // 广播加入消息
    for (Player p : players) {
        p.sendMessage(plugin.getLanguageManager().getText("player-join-message", p,
                player.getName(), String.valueOf(players.size()), String.valueOf(maxPlayers)));
    }
    
    // 检查是否达到最小玩家数
    checkAndStartCountdown();
}

// 玩家离开房间
public void removePlayer(Player player) {
    players.remove(player);
    
    // 广播离开消息
    for (Player p : players) {
        p.sendMessage(plugin.getLanguageManager().getText("player-leave-message", p,
                player.getName(), String.valueOf(players.size()), String.valueOf(maxPlayers)));
    }
}

倒计时系统

java
private void startCountdown() {
    final int[] timeLeft = {countdownTime};
    
    countdownTask = new BukkitRunnable() {
        @Override
        public void run() {
            // 检查玩家数量是否足够
            if (players.size() < minPlayers) {
                cancel();
                countdownTask = null;
                for (Player p : players) {
                    p.sendMessage(plugin.getLanguageManager().getText("countdown-cancelled", p));
                }
                return;
            }
            
            // 倒计时结束,开始游戏
            if (timeLeft[0] <= 0) {
                cancel();
                countdownTask = null;
                startGame();
                return;
            }
            
            // 显示倒计时
            for (Player p : players) {
                p.sendTitle("", plugin.getLanguageManager().getText("countdown-subtitle", p, 
                        String.valueOf(timeLeft[0])), 0, 20, 10);
                
                // 最后5秒显示主标题并播放音效
                if (timeLeft[0] <= 5) {
                    String titleColor = timeLeft[0] <= 3 ? "§c" : "§a";
                    p.sendTitle(titleColor + timeLeft[0], "", 0, 20, 10);
                    p.playSound(p.getLocation(), "ui.button.click", 1.0f, 1.0f);
                }
            }
            
            timeLeft[0]--;
        }
    }.runTaskTimer(plugin, 20L, 20L); // 每秒执行一次
}

游戏开始

java
private void startGame() {
    isWait = false;
    isStart = true;
    
    // 取消 ActionBar 任务
    if (roomInfoTask != null) {
        roomInfoTask.cancel();
        roomInfoTask = null;
    }
    
    // 清空玩家背包
    for (Player p : players) {
        p.getInventory().clear();
        p.sendMessage(plugin.getLanguageManager().getText("game-started", p));
    }
    
    // 创建游戏管理器并开始游戏
    GameManager gameManager = new GameManager(plugin, world, players, mapName, mapFolder);
    plugin.getRoomManager().registerGameManager(this, gameManager);
    gameManager.startGame();
}

地图配置

地图文件结构

plugins/disaster/worlds/
├── map_name_1/
│   ├── config.yml          # 地图配置
│   ├── region/             # 地图区域文件
│   └── ...                 # 其他世界文件
├── map_name_2/
│   ├── config.yml
│   └── ...

配置文件格式

yaml
# 地图名称
name: "Map Name"

# 等待大厅位置
waiting-lobby:
  x: 0.0
  y: 65.0
  z: 0.0
  yaw: 0.0
  pitch: 0.0

# 出生点列表
spawn-points:
  - x: 10.0
    y: 65.0
    z: 10.0
    yaw: 0.0
    pitch: 0.0
  - x: -10.0
    y: 65.0
    z: -10.0
    yaw: 180.0
    pitch: 0.0

# 地图范围(用于灾难生成)
map-scope:
  point1:
    x: -50.0
    y: 0.0
    z: -50.0
  point2:
    x: 50.0
    y: 255.0
    z: 50.0

ActionBar 显示

房间系统使用 ActionBar 显示房间信息,每秒更新一次:

java
private void showRoomInfo(Player player) {
    String mapInfo = plugin.getLanguageManager().getText("room-info", player, 
            mapName, String.valueOf(players.size()), String.valueOf(maxPlayers));
    
    if (isWait) {
        // 常驻显示 ActionBar
        roomInfoTask = new BukkitRunnable() {
            @Override
            public void run() {
                for (Player p : players) {
                    p.spigot().sendMessage(net.md_5.bungee.api.ChatMessageType.ACTION_BAR, 
                            net.md_5.bungee.api.chat.TextComponent.fromLegacyText(
                            plugin.getLanguageManager().getText("room-info", p, 
                            mapName, String.valueOf(players.size()), String.valueOf(maxPlayers))));
                }
            }
        }.runTaskTimer(plugin, 0L, 20L);
    }
}

相关文档