--- title: "MiniGameFrameWork-DS" description: date: 2026-03-02T22:42:22+08:00 image: math: license: comments: true draft: false build: list: always # Change to "never" to hide the page from the list --- ## 插件需求方案:可扩展的小游戏框架 ### 1. 概述 提供一个轻量级的、面向开发的Minecraft小游戏框架。它**不是开箱即用**的插件,而是要求服主具备一定的开发能力,通过继承框架提供的基类来创建新的游戏类型。插件强制依赖WorldEdit(或其分支)和Residence,用于地图操作和区域保护,并将这两个依赖的使用提取为接口,便于后续替换。 核心目标:允许玩家通过命令创建/加入/离开游戏房间,支持地图模板的克隆与清空,提供游戏生命周期管理(等待、开始、胜利/平局结算、销毁),并允许开发者自由扩展游戏规则。 ### 2. 依赖与环境 - **强制依赖**: - WorldEdit(最新版):用于地图区域的克隆和清空。 - Residence(最新版):用于游戏区域的保护配置。 - **环境假设**: - 存在一个专门用于游戏的**空世界**,所有游戏房间均在此世界内划分区域运行。 - 玩家背包等数据由其他插件(如背包隔离插件)管理,本框架不负责物品归还,但负责玩家状态(如生命值、游戏模式)的保存与恢复。 - 无需动态加载/卸载世界,世界始终保持加载。 ### 3. 核心概念 #### 3.1 游戏类型 (GameType) - 由开发者继承框架提供的基类实现。 - 包含游戏的核心逻辑:胜利条件、玩家状态管理、自定义事件响应等。 - 一个游戏类型可以对应多个模板地图(即不同的场地布局),但共用相同的游戏规则。 #### 3.2 房间 (Room) - 房间是游戏的具体实例,由玩家通过命令创建。 - 每个房间具有唯一的**房间码**(随机生成的大写字母+数字混合字符串)。 - 房间状态简化设计:仅分为 **“等待加入”** 和 **“游戏中”** 两种状态。 - 房间包含: - 关联的游戏类型实例。 - 当前玩家列表。 - 分配的场地坐标区域(由框架自动分配,避免重叠)。 - 克隆后的地图区域对应的Residence领地(由框架自动创建,继承模板保护设置)。 #### 3.3 玩家状态 - 框架负责在玩家进入房间时保存其**进入前的状态**(各项非默认值的属性基值、饥饿值、经验、游戏模式),并在玩家离开房间后恢复。 - 保存方式:内存中的 `Map`,无需持久化。 - 子类游戏可以通过覆写方法决定是否重置某些属性(如生命值)或使用自己的状态管理。 ### 4. 游戏生命周期 #### 4.1 创建房间 - **命令**:`/mg create <游戏类型ID> [地图模板名称]` - 流程: 1. 检查游戏类型是否存在,以及是否有空闲区域可分配。 2. 分配区域坐标(按固定大小的网格分配,避免重叠)。 3. 使用WorldEdit从指定模板区域(预先在模板世界中用WorldEdit选区保存)克隆到分配的区域。 4. 根据模板区域预先配置的Residence保护设置,在克隆后自动创建Residence领地,并应用相同的权限配置(领地所有人为控制台或服主)。 5. 生成唯一房间码,将房间信息注册到框架。 6. 向创建者发送成功消息,包含房间码和传送引导(可点击传送至房间)。 - 如果克隆或领地创建失败,则不创建房间,并向管理员发送错误日志(不自动回滚,由管理员手动处理)。 #### 4.2 加入房间 - **命令**:`/mg join <房间码>` - 流程: 1. 检查房间是否存在且状态为“等待加入”。 2. 检查玩家是否已在其他房间(不允许重复加入)。 3. 检查房间当前人数是否未达上限(上限由游戏类型定义,通常硬编码或通过配置设置)。 4. 将玩家加入房间,保存其进入前状态,传送至房间的等待区域(或出生点)。 5. 向房间内所有玩家广播加入消息。 - **重新加入**:若玩家中途掉线,再次上线后可通过 `/mg rejoin` 重新加入(需房间仍存在且处于游戏中或等待状态),框架会恢复其游戏内状态。 #### 4.3 离开房间 - **命令**:`/mg leave` - 触发条件:玩家主动退出、掉线(由框架监听玩家退出事件)或被踢出。 - 流程: 1. 从房间玩家列表中移除玩家。 2. 恢复玩家进入前的状态(生命值、位置等)。 3. 如果房间内玩家数变为0,则立即**销毁房间**(见4.6)。 4. 否则,更新房间状态(如果游戏未开始,仍允许其他人加入;如果游戏已开始,则游戏继续进行,但可能影响胜利条件)。 #### 4.4 游戏开始 - **触发方式**:房间内玩家达到游戏类型定义的最低人数后,所有玩家会收到一条可点击的**开始游戏消息**。点击即执行确认命令(如 `/mg start`)。 - 当**所有在线玩家**都点击确认后,游戏正式开始: 1. 框架调用游戏类型的 `onGameStart` 方法(由子类实现)。 2. 将所有玩家传送至房间内的初始游戏位置(由地图模板预设)。 3. 房间状态切换为“游戏中”。 - 中途掉线的玩家重新加入后,如果游戏已开始,则直接传送至游戏位置,继续参与(游戏逻辑需处理玩家中途加入的情况)。 #### 4.5 游戏进行与胜利判定 - 游戏逻辑完全由子类实现。 - 框架提供**胜利条件接口**(如 `checkWinCondition()`),子类可实现该方法,或在事件监听中主动调用框架的胜利判定方法。 - 胜利判定逻辑示例(由基类提供默认实现,子类可覆写): - 维护一个玩家状态映射 `Map`,初始为 `null` 表示存活。 - 当玩家淘汰时,子类调用 `setStatus(player, false)`。 - 当玩家满足特殊胜利条件时,子类调用 `setStatus(player, true)`。 - 当某玩家状态被设为 `true`,或映射中仅剩一个 `null` 的玩家时,框架判定该玩家胜利,调用 `win(player)`。 - `win(player)` 方法执行: 1. 调用子类的 `onGameEnd(room, winner)` 钩子,允许子类执行自定义操作(如发送胜利消息、执行命令等)。 2. 向所有玩家发送重置引导消息(可点击执行销毁房间命令)。 3. 玩家点击后,执行销毁房间操作。 #### 4.6 游戏结束与房间销毁 - **触发销毁的条件**: - 房间内玩家数变为0。 - 游戏胜利后,玩家点击重置引导消息(执行销毁命令)。 - 管理员强制销毁命令。 - 服务器关闭(触发所有活跃房间的平局结束,并销毁)。 - **销毁流程**: 1. 调用子类的 `onRoomDestroy(room, reason)` 钩子,用于清理临时数据、取消定时任务等。 2. 使用WorldEdit将房间区域**清空为空气**(或直接覆盖空气)。 3. 移除对应的Residence领地。 4. 从框架的房间注册表中删除该房间。 5. 将房间内所有玩家(如果有)恢复状态并传送回主世界出生点。 ### 5. 扩展机制 开发者通过继承框架提供的基类 `MiniGame` 来创建新的游戏类型。基类设计要点如下: #### 5.1 必须实现的方法/属性 - `id: String`:返回游戏类型的唯一标识符。 - `displayName : Component`:返回显示名称。 - `maxPlayer: Int`:最低开始人数。 - `minPlayer: Int`:最大人数限制。 - `onGameStart(room: Room)`:游戏开始时的逻辑(如传送玩家、初始化)。 - `onGameEnd(room: Room, winner: Player?)`:游戏结束时的自定义操作(`winner` 为 `null` 表示平局)。 - `onRoomDestroy(room: Room, reason: DestoryReason)`:房间销毁前的清理。 - `onPlayerJoin(room: Room, player: Player)`:玩家加入时的处理(可选)。 - `onPlayerLeave(room: Room, player: Player, reason: LeaveResaon)`:玩家离开时的处理(可选)。 #### 5.2 可考虑构建的基类工具方法 - `savePlayerState(player: Player)` / `restorePlayerState(player: Player)`:保存/恢复玩家状态。 - `setPlayerStatus(player: Player, Boolean status)`:设置玩家胜利状态(true=胜利,false=淘汰)。 - `broadcast(room: Room, String message)`:向房间内玩家广播。 - `teleportToSpawn(room: Room, Player player)`:传送至房间出生点。 #### 5.3 游戏类型注册 - 服主需在插件启动时调用 `GameRegistry.register(Class)` 注册游戏类型。 - 框架不提供自动扫描,要求显式注册以保持轻量。 ### 6. 地图与区域管理 - **模板存储**:直接在游戏世界中使用WorldEdit的选区功能定义模板区域(例如用 `//pos1`、`//pos2` 选定,然后通过命令保存为快照或直接复制坐标)。模板不保存为单独文件,而是依赖管理员预先在游戏世界中建设好模板区域,并记录其两个对角坐标。 - **区域分配**:框架维护一个“已分配区域”列表,每个房间占据一个固定大小的矩形区域(由模板尺寸决定)。分配算法从原点开始按网格螺旋顺序分配,确保区域不重叠。 - **克隆操作**: - 使用WorldEdit API将模板区域的方块复制到目标区域。 - 克隆失败时不创建房间。 - **清空操作**: - 使用WorldEdit将房间区域内的所有方块设置为空气。 ### 7. Residence 集成 - **保护配置来源**:管理员需预先在模板区域内用Residence创建好一个领地,并设置好所有需要的权限(如禁止破坏、禁止使用等)。框架在克隆区域后,会调用Residence API在克隆区域创建一个**名为房间码+游戏类型+时间戳的领地**,并将原领地的权限设置**复制**到新领地。 - **领地所有权**:新领地的所有人为控制台(或服主),以确保管理员权限。 - **领地移除**:房间销毁时,通过Residence API移除对应的领地。 ### 8. WorldEdit 集成 - 框架将WorldEdit的操作封装为接口 `WorldEditBridge`,包含: - `cloneRegion(Region from, Region to)`:克隆区域。 - `clearRegion(Region region)`:清空区域。 - 实现类直接调用WorldEdit API,便于未来更换版本或分支。 ### 9. 命令系统 使用Brigadier实现命令,自动注册权限节点。 #### 9.1 玩家命令 - `/mg create <游戏类型> [模板名]`:创建房间。 - `/mg join <房间码>`:加入房间。 - `/mg leave`:离开当前房间。 - `/mg start`:确认开始游戏(仅当房间内玩家都点击后才生效,实际由点击消息触发,也可手动输入)。 #### 9.2 管理员命令 - `/mg list`:列出所有活跃房间(房间码、游戏类型、人数、状态)。 - `/mg info <房间码>`:查看房间详细信息。 - `/mg forcestart <房间码>`:强制开始游戏(忽略未确认玩家)。 - `/mg forceend <房间码>`:强制结束游戏(平局处理)。 - `/mg delete <房间码>`:强制销毁房间。 - `/mg reload`:重载配置(如果有)。 #### 9.3 房间码 - 随机生成6位大写字母+数字组合,例如 `A3F9B2`。 - 生成时检查唯一性。 ### 10. 配置 主配置文件 `config.yml` 包含少量全局设置,例如: ```yaml # 默认最低/最高人数(如果游戏类型未指定,可留空) default-min-players: 2 default-max-players: 8 # 区域分配起始坐标 region-start-x: 0 region-start-z: 0 # 区域间距(防止重叠) region-spacing: 10 # 世界名称 game-world: "world" # 是否启用调试日志 debug: false ``` 游戏类型特有的配置可由子类自行读取(如自定义文件或使用本配置的额外字段),框架不强制规定。 ### 11. 权限 所有命令自动注册对应权限节点,默认仅OP可用。建议节点: - `minigame.create`:创建房间 - `minigame.join`:加入房间 - `minigame.leave`:离开房间 - `minigame.start`:开始游戏(通常仅房间内玩家可用,由逻辑控制) - `minigame.admin`:管理员命令(包含所有管理子命令) ### 12. 错误处理与日志 - 关键操作(房间创建、销毁、游戏开始/结束)输出INFO级别日志。 - 异常(克隆失败、领地创建失败)输出WARNING/ERROR级别日志,并提示管理员手动处理。 - 不自动回滚,由管理员根据日志决定是否需要清理损坏的区域。 ### 13. 其他说明 - 服务器关闭时,框架会遍历所有活跃房间,调用 `onGameEnd(room, null)` 表示平局,然后执行销毁流程(清空区域、移除领地、恢复玩家状态)。不保存任何房间状态到磁盘。 - 玩家状态恢复仅在离开房间或服务器关闭时进行,中途死亡等由子类游戏自行处理。 - 暂不提供队伍系统,可使用原版计分板团队或单玩家队伍;未来版本可考虑扩展。