Skip to content

BedWars 多模式架构文档

概述

BedWars 模块支持多游戏模式(如 4 队模式、8 队模式等),通过 /bw join <模式ID> 加入指定模式的房间。没有传模式参数时使用默认模式。

架构核心原则:

  • 模式由代码定义:模式通过继承 GameMode 抽象类以编程方式注册,config.yml 只能覆盖数据字段(minPlayers、maxPlayers、countdownTime),不能创建新模式
  • 地图声明模式:地图 config 中 modes 字段声明该地图支持哪些模式,默认为空(不参与任何模式)
  • 安全默认值:未配置 modes 的地图不参与任何模式,防止配置错误影响全局

配置文件说明

1. config.yml — 模式数据覆盖

yaml
Language: zh_cn
room-prefix: "bedwars-"

# 默认游戏模式(/bw join 不指定模式时使用)
# 模式必须通过代码注册,这里只指定默认使用哪个模式ID
default-mode: "4t"

# 模式数据覆盖(可选)
# 只能覆盖已通过代码注册的模式的数据字段(min-players, max-players, countdown-time)
# 不能通过配置创建新模式,模式的行为由代码决定
# teams 和 display-name-key 在代码中定义,不建议在此覆盖
modes:
  4t:
    min-players: 2
    max-players: 16
    countdown-time: 30

重要modes 段只能覆盖已注册模式的数值字段。teamsdisplay-name-key 在模式子类的构造函数中定义,config.yml 不解析这两个字段。

2. team.yml — 队伍视觉配置

team.yml 是全局队伍定义,包含所有可能的队伍的视觉属性(颜色、方块、床材质等)。它是「队伍属性字典」,不是「模式队伍列表」。

yaml
team:
  - teamid: "red"
    name: "team-red"
    wool: "red_wool"
    terracotta: "red_terracotta"
    glass: "red_stained_glass"
    bed: "red_bed"
    color: "#FF5555"
  # ... 共 8 个队伍(red, blue, green, yellow, cyan, pink, orange, gray)...

模式通过代码中的 teamKeys 字段选择使用哪些队伍,team.yml 提供这些队伍的视觉数据。

3. 地图 config.yml — 地图模式声明

yaml
name: Temple
name_zh: 庙宇

# 该地图支持的模式列表(必须通过编辑 UI 手动设置)
# 留空或缺失 = 不参与任何模式(安全默认)
modes:
  - 4t

waiting-lobby:
  x: 0.5
  y: 103.0
  ...

teams:
  red:
    spawn: { x: 0.45, y: 63.0, z: -70.6, ... }
    bed: { x: 0.57, y: 63.5, z: -59.6, ... }
  blue:
    spawn: { x: 71.5, y: 63.0, z: 0.4, ... }
    bed: { x: 60.5, y: 63.0, z: 0.6, ... }
  green:
    spawn: { x: 0.6, y: 63.0, z: 71.5, ... }
    bed: { x: 0.4, y: 63.0, z: 60.6, ... }
  yellow:
    spawn: { x: -70.5, y: 63.0, z: 0.5, ... }
    bed: { x: -59.6, y: 63.0, z: 0.4, ... }
  # 8t 模式时还可以有 cyan, pink, orange, gray

关键规则

  • 地图可以定义超过模式需要的队伍 spawn/bed(如 4t 地图可以有 8 个队伍的位置)
  • 加载时只会加载模式 teamKeys 列表中的队伍
  • modes 字段通过编辑 UI 的「模式设置」按钮设置,默认为空

4. 语言文件 — 模式显示名称

yaml
# zh_cn.yml
mode-4t: "4v4v4v4"
mode-8t: "8队混战"

# en_us.yml
mode-4t: "4v4v4v4"
mode-8t: "8 Team Brawl"

模式的显示名称通过 displayNameKey 引用语言 key,在模式子类构造函数中定义。


核心类说明

GameMode(抽象基类)

cn.enderrealm.bedwars.mode.GameMode

GameMode 是所有游戏模式的抽象基类,采用模板方法模式。子类通过构造函数设置数据字段,通过覆写行为钩子定义模式专属逻辑。

数据字段

字段类型可变说明
idString模式唯一标识,如 "4t"
displayNameKeyString语言文件 key,如 "mode-4t"
teamKeysList<String>该模式使用的队伍 ID 列表
minPlayersint最少玩家数(可通过 config 覆盖)
maxPlayersint最多玩家数(可通过 config 覆盖)
countdownTimeint倒计时秒数(可通过 config 覆盖)

行为钩子

所有钩子都有安全的默认实现,子类可按需覆写:

钩子方法默认行为说明
onGameStart(GameManager)空操作游戏开始时调用,用于给初始物品、设置特殊规则
setupGenerators(GameManager)创建 GeneratorManager 并启动设置资源生成器,可覆写以自定义资源经济
setupShops(GameManager)空操作设置商店内容,可覆写以加载不同商店配置
setupEvents(GameManager)创建 EventManager 并启动事件链启动游戏事件系统,可覆写以定制事件序列
onPlayerDeath(GameManager, Player)空操作玩家死亡时调用
onPlayerRespawn(GameManager, Player)空操作玩家复活时调用
checkWinCondition(GameManager)返回 null检查胜利条件,返回获胜队伍 ID 或 null(继续游戏)
allowBlockBreak(GameManager, Player, Block)返回 false是否允许破坏方块
allowBlockPlace(GameManager, Player, Block)返回 true是否允许放置方块

GameModeRegistry(注册表)

cn.enderrealm.bedwars.mode.GameModeRegistry

模式注册表。模式通过 register() 方法以编程方式注册,config.yml 只能覆盖已注册模式的数据字段。

方法说明
register(GameMode)注册一个游戏模式(代码调用)
applyConfigOverrides(BedWars)用 config.yml 覆盖已注册模式的数据字段
getMode(id)按 ID 获取模式
getDefaultMode()获取默认模式(回退到第一个已注册模式)
getDefaultModeId()获取默认模式 ID
getAllModes()获取所有模式
hasMode(id)检查模式是否存在
getModeIds()获取所有模式 ID 列表

FourTeamMode(四队模式实现)

cn.enderrealm.bedwars.mode.FourTeamMode

当前唯一的具体模式实现,继承 GameMode。不覆写任何行为钩子,全部走默认逻辑。

属性
id"4t"
displayNameKey"mode-4t"
teamKeys[red, blue, green, yellow]
minPlayers2
maxPlayers16
countdownTime30

访问模式的方式

java
// 从任意游戏逻辑中
GameMode mode = gameManager.getRoom().getGameMode();

// 从插件主类
GameMode mode = plugin.getGameModeRegistry().getMode("4t");

// 判断当前模式
if (mode != null && "4t".equals(mode.getId())) {
    // 4t 模式专属逻辑
}

模式注册流程

BedWars.onEnable()
  → new GameModeRegistry()                          // 创建空注册表,默认 modeId = "4t"
  → gameModeRegistry.register(new FourTeamMode())   // 代码注册模式
  → gameModeRegistry.applyConfigOverrides(this)     // 用 config.yml 覆盖数据字段
      → 读取 default-mode(默认 "4t")
      → 遍历 modes 段,对已注册模式覆盖 min-players/max-players/countdown-time
      → 未注册的模式 ID 静默跳过

设计原则:模式的行为由代码决定,配置只影响数据。添加新模式必须编写 Java 代码。


游戏初始化流程

/bw join 4t
  → JoinCommand: 解析 modeId = "4t"
  → modeRegistry.hasMode("4t") 验证模式存在
  → RoomManager.getAvailableRoom("4t") 或 createRoom("4t")
    → getCompatibleMaps("4t")  // 只选 modes 列表含 "4t" 的地图
    → 创建世界 bedwars-4t-1
    → new Room(plugin, world, waitingLoc, mapFolder, gameMode)

Room.startGame()
  → new GameManager(plugin, room)
  → gameManager.initGame()
    → GameInitTask(异步初始化任务链)

GameInitTask 执行顺序:
  1. [异步] LoadTeamLocationsTask
     → 读地图 config.yml 的 teams 段
     → 过滤: 只加载 gameMode.getTeamKeys() 中的队伍
     → 地图有 8 个队伍位置但 4t 只加载 red/blue/green/yellow

  2. [异步] AssignTeamsTask
     → teamSpawns.keySet() 只有 4 个队伍
     → 玩家均匀分配到这 4 个队

  3. [同步] SetupPlayerTeamsTask → 设置颜色/装备
  4. [同步] InitTeamBedsTask → 放 4 个床
  5. [同步] TeleportPlayersTask → 传送到 4 个出生点

  6. [同步] mode.setupGenerators(gm)   ← 模式钩子
     → 默认: 创建 GeneratorManager,加载产矿机配置并启动
     → 子类可覆写以自定义资源经济

  7. [同步] mode.setupShops(gm)        ← 模式钩子
     → 默认: 空操作

  8. [同步] mode.onGameStart(gm)       ← 模式钩子
     → 默认: 空操作
     → 子类可覆写以给初始物品、设置特殊规则

  9. [同步] mode.setupEvents(gm)       ← 模式钩子
     → 默认: 创建 EventManager,加载事件配置并启动事件链
     → 子类可覆写以定制事件序列

  10. [同步] gameManager.initGameData()
     → 初始化床状态(全部存活)和玩家数据

编辑 UI — 模式设置

在编辑 UI 中有一个指南针按钮(slot 34)用于打开模式选择界面:

  • 绿色染色玻璃板 = 已启用的模式,点击取消
  • 红色染色玻璃板 = 未启用的模式,点击激活
  • 底部有返回按钮

模式选择状态保存在地图 config 的 modes 字段中。


扩展指南

添加新模式

  1. 创建新的模式子类,继承 GameMode
java
public class EightTeamMode extends GameMode {
    public EightTeamMode() {
        super(
            "8t",                                          // 模式ID
            "mode-8t",                                     // 语言文件key
            Arrays.asList("red", "blue", "green",         // 队伍列表
                "yellow", "cyan", "pink", "orange", "gray"),
            4,                                             // 最少玩家
            32,                                            // 最多玩家
            45                                             // 倒计时秒数
        );
    }

    // 可选:覆写行为钩子以自定义模式逻辑
    @Override
    public void onGameStart(GameManager gm) {
        // 8t 模式专属的初始逻辑
    }
}
  1. BedWars.onEnable() 中注册新模式:
java
gameModeRegistry.register(new FourTeamMode());
gameModeRegistry.register(new EightTeamMode());  // 新增
gameModeRegistry.applyConfigOverrides(this);
  1. zh_cn.yml / en_us.yml 添加模式显示名称
  2. team.yml 中确保新模式用到的队伍都有定义
  3. 在地图编辑 UI 中为地图激活新模式
  4. 确保地图 config 中有新模式涉及队伍的 spawn/bed 位置

添加模式独有机制

通过覆写 GameMode 的行为钩子:

java
public class CrazyMode extends GameMode {
    public CrazyMode() {
        super("crazy", "mode-crazy",
            Arrays.asList("red", "blue", "green", "yellow"),
            2, 16, 30);
    }

    @Override
    public void onGameStart(GameManager gm) {
        // 给所有玩家钻石装备
        for (Player p : gm.getRoom().getPlayers()) {
            p.getInventory().addItem(new ItemStack(Material.DIAMOND_SWORD));
        }
    }

    @Override
    public boolean allowBlockBreak(GameManager gm, Player player, Block block) {
        return true; // 允许破坏任何方块
    }

    @Override
    public String checkWinCondition(GameManager gm) {
        // 自定义胜利条件:第一个击杀 10 人的队伍获胜
        // ...
        return null; // 返回 null 表示游戏继续
    }
}

添加新队伍

  1. team.yml 中添加新队伍定义
  2. zh_cn.yml / en_us.yml 中添加队伍名称和编辑 UI 语言 key
  3. EditManagergetTeamWoolMaterial() / getTeamBedMaterial() 中添加新队伍颜色映射
  4. SetupPlayerTeamsTaskgetBukkitColorFromHex() 中添加新队伍颜色映射
  5. 地图编辑 UI 中会自动显示新队伍(已通过遍历 team.yml 实现)