完整演示:维护插件生成
本文展示一个完整的插件生成案例,从需求描述到最终 JAR,演示 AI 如何处理多文件项目和自动修正错误。
需求描述
请帮我完成一个玩家进服后被踢出并提示正在维护的插件,
并且支持用 /setNotice 命令设置踢出时的提示需求分析
这个需求包含以下功能点:
- 事件监听:监听玩家加入服务器事件
- 踢出玩家:在玩家加入时立即踢出
- 配置系统:提示消息可配置
- 命令系统:提供命令修改配置
- 权限控制:命令需要权限
这是一个典型的多文件项目,需要:
- 主类(注册事件和命令)
- 事件监听器类
- 命令处理器类
- 配置文件(plugin.yml、config.yml)
- Maven 构建配置(pom.xml)
第一阶段:Planner 规划
用户点击"生成项目"后,系统调用 Planner 分析需求。
Planner 输入
{
"userPrompt": "请帮我完成一个玩家进服后被踢出并提示正在维护的插件,并且支持用 /setNotice 命令设置踢出时的提示",
"coreType": "PAPER",
"version": "1.20.6"
}Planner 输出
{
"projectName": "MaintenanceKicker",
"packageName": "com.example.maintenancekicker",
"javaVersion": "17",
"files": [
{
"path": "pom.xml",
"role": "Maven 构建配置,定义依赖和编译参数",
"order": 1,
"depends": []
},
{
"path": "src/main/resources/plugin.yml",
"role": "插件描述文件,声明插件信息、命令和权限",
"order": 2,
"depends": []
},
{
"path": "src/main/resources/config.yml",
"role": "默认配置文件,存储踢出提示消息",
"order": 3,
"depends": []
},
{
"path": "src/main/java/com/example/maintenancekicker/listener/JoinListener.java",
"role": "玩家加入事件监听器,踢出玩家并显示提示",
"order": 4,
"depends": []
},
{
"path": "src/main/java/com/example/maintenancekicker/command/SetNoticeCommand.java",
"role": "命令处理器,处理 /setNotice 命令并更新配置",
"order": 5,
"depends": []
},
{
"path": "src/main/java/com/example/maintenancekicker/MaintenanceKicker.java",
"role": "插件主类,负责初始化和注册事件、命令",
"order": 6,
"depends": ["JoinListener.java", "SetNoticeCommand.java"]
}
]
}关键点:
- Planner 自动识别需要 6 个文件
- 每个文件声明
depends依赖,服务端按拓扑排序确定生成顺序 - 主类
MaintenanceKicker.java依赖 JoinListener 和 SetNoticeCommand,排在最后生成 - 工具类/监听器/命令处理器先生成,主类最后整合调用
第二阶段:FileGen 逐文件生成
系统按拓扑排序后的 order 顺序逐个生成文件。注意主类排在最后,因为它依赖 JoinListener 和 SetNoticeCommand。
文件 1:pom.xml
FileGen 输入:
角色:Maven 构建配置,定义依赖和编译参数
上下文:
- 项目名:MaintenanceKicker
- 包名:com.example.maintenancekicker
- 核心:PAPER
- 版本:1.20.6
- Java 版本:17
已生成文件的可用 API:无FileGen 输出:
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.example</groupId>
<artifactId>MaintenanceKicker</artifactId>
<version>1.0.0</version>
<packaging>jar</packaging>
<properties>
<maven.compiler.source>17</maven.compiler.source>
<maven.compiler.target>17</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<repositories>
<repository>
<id>papermc</id>
<url>https://repo.papermc.io/repository/maven-public/</url>
</repository>
</repositories>
<dependencies>
<dependency>
<groupId>io.papermc.paper</groupId>
<artifactId>paper-api</artifactId>
<version>1.20.6-R0.1-SNAPSHOT</version>
<scope>provided</scope>
</dependency>
</dependencies>
</project>reChecker 审查:● 通过
summaryExtract 提取摘要:
{
"description": "Maven 构建配置,定义了 Paper 1.20.6 依赖和 Java 17 编译参数"
}文件 2:plugin.yml
FileGen 输入(已注入 pom.xml 的摘要):
已生成文件的可用 API:
【pom.xml】
职责:Maven 构建配置,定义了 Paper 1.20.6 依赖和 Java 17 编译参数FileGen 输出:
name: MaintenanceKicker
version: 1.0.0
main: com.example.maintenancekicker.MaintenanceKicker
api-version: 1.20
author: AI Generated
commands:
setnotice:
description: 设置维护提示消息
usage: /setnotice <消息>
permission: maintenancekicker.setnotice
permissions:
maintenancekicker.setnotice:
description: 允许修改维护提示
default: opreChecker 审查:● 通过
summaryExtract 提取摘要:
{
"description": "插件描述文件,main 类为 com.example.maintenancekicker.MaintenanceKicker",
"commands": ["setnotice"],
"configKeys": ["maintenancekicker.setnotice"]
}文件 3:config.yml
FileGen 输出:
# 维护提示消息
notice: "服务器正在维护,请稍后再试"reChecker 审查:● 通过
summaryExtract 提取摘要:
{
"description": "默认配置文件,存储踢出提示消息",
"configKeys": ["notice"]
}文件 4:JoinListener.java(事件监听器)
注意:在新的拓扑排序下,监听器和命令处理器先于主类生成。
FileGen 输入(已注入前 3 个文件的摘要):
已生成文件的可用 API:
【pom.xml】
职责:Maven 构建配置,定义了 Paper 1.20.6 依赖和 Java 17 编译参数
【plugin.yml】
职责:插件描述文件,main 类为 com.example.maintenancekicker.MaintenanceKicker
注册命令:setnotice
【config.yml】
职责:默认配置文件,存储踢出提示消息
配置键:noticeFileGen 输出:
package com.example.maintenancekicker.listener;
import org.bukkit.event.EventHandler;
import org.bukkit.event.Listener;
import org.bukkit.event.player.PlayerJoinEvent;
import org.bukkit.plugin.java.JavaPlugin;
public class JoinListener implements Listener {
private final JavaPlugin plugin;
public JoinListener(JavaPlugin plugin) {
this.plugin = plugin;
}
@EventHandler
public void onPlayerJoin(PlayerJoinEvent event) {
String notice = plugin.getConfig().getString("notice", "服务器正在维护");
event.getPlayer().kickPlayer(notice);
}
}reChecker 审查(含跨文件一致性检查):● 通过
summaryExtract 提取摘要:
{
"className": "JoinListener",
"implements": ["Listener"],
"publicMethods": [
{ "name": "JoinListener", "params": "JavaPlugin plugin", "returns": "constructor" },
{ "name": "onPlayerJoin", "params": "PlayerJoinEvent event", "returns": "void" }
],
"events": ["PlayerJoinEvent"],
"description": "玩家加入事件监听器,踢出玩家并显示配置中的提示消息"
}文件 5:SetNoticeCommand.java(命令处理器)
FileGen 输入(已注入前 4 个文件的结构化 API 摘要,包括 JoinListener 的类名和方法签名):
已生成文件的可用 API:
...(前 3 个文件摘要省略)
【.../listener/JoinListener.java】
职责:玩家加入事件监听器,踢出玩家并显示配置中的提示消息
类名:JoinListener implements Listener
公开方法:
- constructor JoinListener(JavaPlugin plugin)
- void onPlayerJoin(PlayerJoinEvent event)
监听事件:PlayerJoinEventFileGen 输出:
package com.example.maintenancekicker.command;
import org.bukkit.command.Command;
import org.bukkit.command.CommandExecutor;
import org.bukkit.command.CommandSender;
import org.bukkit.plugin.java.JavaPlugin;
public class SetNoticeCommand implements CommandExecutor {
private final JavaPlugin plugin;
public SetNoticeCommand(JavaPlugin plugin) {
this.plugin = plugin;
}
@Override
public boolean onCommand(CommandSender sender, Command command, String label, String[] args) {
if (args.length == 0) {
sender.sendMessage("§c用法: /setnotice <消息>");
return true;
}
String notice = String.join(" ", args);
plugin.getConfig().set("notice", notice);
plugin.saveConfig();
sender.sendMessage("§a维护提示已更新为: " + notice);
return true;
}
}reChecker 审查(含跨文件一致性检查):● 通过
summaryExtract 提取摘要:
{
"className": "SetNoticeCommand",
"implements": ["CommandExecutor"],
"publicMethods": [
{ "name": "SetNoticeCommand", "params": "JavaPlugin plugin", "returns": "constructor" },
{ "name": "onCommand", "params": "CommandSender sender, Command command, String label, String[] args", "returns": "boolean" }
],
"commands": ["setnotice"],
"description": "命令处理器,处理 /setnotice 命令并更新配置文件中的提示消息"
}文件 6:MaintenanceKicker.java(主类 — 最后生成)
主类排在最后生成,此时已拥有所有依赖文件的完整 API 摘要,能精确知道 JoinListener 和 SetNoticeCommand 的构造函数签名。
FileGen 输入(注入全部 5 个已生成文件的结构化 API 摘要):
已生成文件的可用 API:
...(配置文件摘要省略)
【.../listener/JoinListener.java】
职责:玩家加入事件监听器
类名:JoinListener implements Listener
公开方法:
- constructor JoinListener(JavaPlugin plugin)
- void onPlayerJoin(PlayerJoinEvent event)
监听事件:PlayerJoinEvent
【.../command/SetNoticeCommand.java】
职责:命令处理器,处理 /setnotice 命令
类名:SetNoticeCommand implements CommandExecutor
公开方法:
- constructor SetNoticeCommand(JavaPlugin plugin)
- boolean onCommand(CommandSender sender, Command command, String label, String[] args)
注册命令:setnotice
你只能调用上面列出的类和方法,不要假设任何未列出的方法或类存在。FileGen 输出:
package com.example.maintenancekicker;
import com.example.maintenancekicker.listener.JoinListener;
import com.example.maintenancekicker.command.SetNoticeCommand;
import org.bukkit.plugin.java.JavaPlugin;
public class MaintenanceKicker extends JavaPlugin {
@Override
public void onEnable() {
saveDefaultConfig();
getServer().getPluginManager().registerEvents(new JoinListener(this), this);
getCommand("setnotice").setExecutor(new SetNoticeCommand(this));
getLogger().info("MaintenanceKicker 已启用");
}
@Override
public void onDisable() {
getLogger().info("MaintenanceKicker 已禁用");
}
}reChecker 审查(含跨文件一致性检查):
{
"is_ok": true,
"reason": "import 正确,JoinListener(JavaPlugin) 和 SetNoticeCommand(JavaPlugin) 构造函数签名与已生成文件一致"
}● 一次通过,无需修正 — 因为主类最后生成,已拥有完整的依赖信息
第三阶段:校验与构建
文件完整性校验
{
"verified": true,
"total": 6,
"generated": 6,
"missing": []
}● 所有文件已生成
上传到 GitHub
系统创建临时分支 build-1710556800000-abc123,使用 Git Tree API 一次性上传所有文件:
MaintenanceKicker/
├── pom.xml
└── src/
└── main/
├── java/
│ └── com/example/maintenancekicker/
│ ├── MaintenanceKicker.java
│ ├── listener/
│ │ └── JoinListener.java
│ └── command/
│ └── SetNoticeCommand.java
└── resources/
├── plugin.yml
└── config.yml触发 Maven 构建
GitHub Actions 执行:
- name: Set up JDK 17
uses: actions/setup-java@v3
with:
java-version: '17'
- name: Build with Maven
run: mvn clean package
- name: Upload artifact
uses: actions/upload-artifact@v3
with:
name: MaintenanceKicker
path: target/*.jar构建日志:
[INFO] Building MaintenanceKicker 1.0.0
[INFO] Compiling 3 source files to target/classes
[INFO] Building jar: target/MaintenanceKicker-1.0.0.jar
[INFO] BUILD SUCCESS● 构建成功
第四阶段:下载和测试
下载 JAR
用户点击"下载 JAR",获得 MaintenanceKicker.zip,解压后得到 MaintenanceKicker-1.0.0.jar。
安装测试
- 将 JAR 放入
plugins目录 - 重启服务器
控制台输出:
[Server] Loading MaintenanceKicker v1.0.0
[MaintenanceKicker] MaintenanceKicker 已启用功能测试
测试 1:玩家进服被踢出
玩家 "Steve" 尝试加入服务器
→ 被踢出,显示:"服务器正在维护,请稍后再试"测试 2:修改提示消息
OP 执行:/setnotice 服务器升级中,预计 30 分钟后开放
→ 控制台显示:"§a维护提示已更新为: 服务器升级中,预计 30 分钟后开放"
→ config.yml 自动更新测试 3:新提示生效
玩家 "Alex" 尝试加入服务器
→ 被踢出,显示:"服务器升级中,预计 30 分钟后开放"● 所有功能正常
技术亮点总结
1. 依赖拓扑排序 + 主类最后生成
- Planner 自动识别需要 6 个文件,并声明
depends依赖关系 - 服务端 Kahn 算法拓扑排序,被依赖的文件先生成
- 主类排在最后,生成时已拥有所有依赖文件的完整 API 摘要
- 本例中主类一次通过审查,无需修正 — 因为它精确知道 JoinListener 和 SetNoticeCommand 的构造函数签名
2. 结构化 API 摘要传递
- 每个文件生成后由 summaryExtract 提取结构化摘要(类名、方法签名、事件、命令等)
- 后续文件的 FileGen Prompt 注入完整的 API 签名块,而非简单的文本截断
- 明确约束"只能调用已列出的 API",杜绝虚空调用
3. reChecker 跨文件一致性检查
- reChecker 接收已生成文件的结构化 API 摘要
- 不仅检查语法错误,还验证跨文件调用是否真的存在
- 双重防线:FileGen 约束 + reChecker 验证
4. 云端构建验证
- Maven 编译验证代码可运行
- 如果有语法错误,构建会失败并返回错误日志
- 三重保障(FileGen 约束 + reChecker 审查 + Maven 构建)确保代码质量