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 段只能覆盖已注册模式的数值字段。teams 和 display-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.GameModeGameMode 是所有游戏模式的抽象基类,采用模板方法模式。子类通过构造函数设置数据字段,通过覆写行为钩子定义模式专属逻辑。
数据字段
| 字段 | 类型 | 可变 | 说明 |
|---|---|---|---|
id | String | 否 | 模式唯一标识,如 "4t" |
displayNameKey | String | 否 | 语言文件 key,如 "mode-4t" |
teamKeys | List<String> | 否 | 该模式使用的队伍 ID 列表 |
minPlayers | int | 是 | 最少玩家数(可通过 config 覆盖) |
maxPlayers | int | 是 | 最多玩家数(可通过 config 覆盖) |
countdownTime | int | 是 | 倒计时秒数(可通过 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] |
| minPlayers | 2 |
| maxPlayers | 16 |
| countdownTime | 30 |
访问模式的方式
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 字段中。
扩展指南
添加新模式
- 创建新的模式子类,继承
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 模式专属的初始逻辑
}
}- 在
BedWars.onEnable()中注册新模式:
java
gameModeRegistry.register(new FourTeamMode());
gameModeRegistry.register(new EightTeamMode()); // 新增
gameModeRegistry.applyConfigOverrides(this);- 在
zh_cn.yml/en_us.yml添加模式显示名称 - 在
team.yml中确保新模式用到的队伍都有定义 - 在地图编辑 UI 中为地图激活新模式
- 确保地图 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 表示游戏继续
}
}添加新队伍
- 在
team.yml中添加新队伍定义 - 在
zh_cn.yml/en_us.yml中添加队伍名称和编辑 UI 语言 key - 在
EditManager的getTeamWoolMaterial()/getTeamBedMaterial()中添加新队伍颜色映射 - 在
SetupPlayerTeamsTask的getBukkitColorFromHex()中添加新队伍颜色映射 - 地图编辑 UI 中会自动显示新队伍(已通过遍历 team.yml 实现)