从零开始开发你的第一个Bukkit插件:实战入门指南

📅 2026/8/15 10:35:47
从零开始开发你的第一个Bukkit插件:实战入门指南
1. 项目概述从玩家到创造者的第一步如果你和我一样在《我的世界》里从撸树开始到建造红石机器再到搭建自己的服务器那么迟早有一天你会盯着服务器里琳琅满目的插件列表心里冒出一个念头“这个功能要是能这样改一下就好了”或者“为什么没有插件能实现我想要的这个玩法” 这就是插件开发旅程的起点。Bukkit作为Minecraft Java版服务器最经典、最庞大的插件API生态是绝大多数开发者迈出第一步的地方。网上教程很多但很多要么过于陈旧要么一上来就堆砌代码让人看得云里雾里。这个系列教程我想用最贴近实战、最像朋友间分享经验的方式带你从零开始亲手做出你的第一个Bukkit插件并理解每一个文件、每一行代码背后的“所以然”。这个“第一个插件”的目标很纯粹它不需要实现复杂的功能它的核心价值在于帮你打通从“想法”到“一个能被服务器加载并运行”的完整流程。你会亲手创建项目结构编写核心的插件描述文件实现一个最简单的命令并最终打包成JAR文件放进服务器的plugins文件夹。整个过程你会遇到诸如“plugin.yml到底怎么写才对”、“为什么我的插件服务器不认”、“命令怎么注册不生效”这些新手必踩的坑。别担心我会把这些坑都提前标出来并告诉你最稳妥的绕过方法。无论你是刚学Java不久的学生还是有一定编程基础但从未接触过Minecraft开发的爱好者这篇教程都将是你最扎实的起点。我们用的工具会是目前最主流的IntelliJ IDEA因为它对Java和Maven的支持无与伦比能让我们更专注于逻辑本身而不是环境配置的挣扎。2. 开发环境与项目骨架搭建2.1 工具链选择与配置要点工欲善其事必先利其器。对于Bukkit插件开发一套顺手的工具能让你事半功倍。核心工具链就三样JDK、构建工具和IDE。首先是JDK。Bukkit插件基于Java因此你需要安装Java Development Kit。这里有个关键点你的JDK版本必须与目标服务器运行的Java版本兼容。目前绝大多数主流Minecraft服务器1.12都运行在Java 8或Java 11上。为了获得最好的兼容性我强烈建议你直接安装Java 8 (JDK 1.8)。你可以选择Oracle JDK 8或者OpenJDK 8。在IntelliJ IDEA中你需要确保项目SDK指向正确的JDK 8路径。高版本JDK如JDK 17虽然也能编译但在涉及反射等底层操作时可能会遇到意想不到的兼容性问题对于第一个插件求稳是第一要务。其次是构建工具。我们选择Maven而不是手动管理JAR包。Maven能自动处理依赖下载、项目构建和打包是现代化Java项目的标准。你不需要单独安装Maven因为IDEA已经内置了Maven支持。我们需要做的是在项目中创建一个pom.xml文件这是Maven项目的“心脏”。在这个文件里最关键的是声明对Bukkit API的依赖。这里有一个非常重要的细节你必须使用与你目标Minecraft服务器版本匹配的Bukkit API版本。例如如果你的服务器是1.16.5你就应该去找对应1.16.5的Bukkit API。一个可靠的仓库是SpigotMC的Maven仓库。你的pom.xml依赖部分看起来会是这样dependencies dependency groupIdorg.spigotmc/groupId artifactIdspigot-api/artifactId version1.16.5-R0.1-SNAPSHOT/version scopeprovided/scope /dependency /dependencies注意scopeprovided/scope这一行它告诉Maven这个依赖在编译和测试时需要但在最终打包插件JAR时不要包含进去。因为Bukkit API在服务器运行时已经提供了重复打包只会增大插件体积并可能引起冲突。最后是IDE我们选择IntelliJ IDEA Community Edition免费版。它足够强大对Maven和Java的支持是顶级的。安装后你需要创建一个新的Maven项目不要选普通的Java项目。在创建过程中IDEA可能会下载初始依赖包括Maven本身耐心等待即可。注意网络上有些非常古老的教程会教你手动下载craftbukkit.jar或spigot.jar并添加到项目的库中。这种方法早已过时难以管理依赖和版本请务必使用Maven来管理你的项目。2.2 创建项目与理解标准目录结构打开IDEA选择“New Project”在左侧选择“Maven”。在“Project SDK”处选择你安装的JDK 8。GroupId和ArtifactId是Maven项目的坐标你可以简单理解为插件的“姓氏”和“名字”。例如GroupId可以是com.yournameArtifactId可以是FirstPlugin。版本号Version先填1.0-SNAPSHOT即可。点击创建IDEA会为你生成一个标准的Maven项目骨架。项目创建好后在左侧的项目视图中你会看到这样的核心目录结构FirstPlugin ├── src │ ├── main │ │ ├── java // 你的Java源代码放在这里 │ │ └── resources // 资源文件如plugin.yml、config.yml放在这里 │ └── test // 测试代码暂时用不到 └── pom.xml // Maven项目配置文件现在我们需要创建插件最核心的两个文件。首先在src/main/resources目录上右键选择New - File创建一个名为plugin.yml的文件。这个文件是Bukkit服务器识别插件的“身份证”没有它服务器根本不会加载你的插件。接着在src/main/java目录下按照你刚才填写的GroupId创建对应的包package。例如如果GroupId是com.yourname那么就创建com.yourname这个包。在这个包下创建你的主类比如叫Main。这个目录结构是Maven的约定严格遵守它能让你在后续开发、打包时避免很多路径错误。src/main/java下的包结构反映了你的代码组织逻辑而resources目录下的文件在打包时会原封不动地放进JAR包的根目录这正是Bukkit服务器所要求的。3. 插件的心脏plugin.yml 深度解析3.1 基础字段name、version、main 的玄机plugin.yml是一个YAML格式的文件它用键值对的方式来描述你的插件。它的语法非常严格缩进必须使用空格通常是2个不能使用Tab键并且大小写敏感。一个最基础的plugin.yml至少包含以下三个字段name: FirstPlugin version: 1.0.0 main: com.yourname.Main我们来逐一拆解这里面每一个字段都有讲究name (名称)这是你插件的名字。它必须是唯一的不能和服务器里已有的其他插件重名并且应该保持简洁、无空格。通常使用驼峰命名法如MyFirstPlugin或单词连接如first-plugin。这个名字会显示在服务器的插件列表里如/plugins命令的输出。version (版本)插件的版本号。强烈建议使用 语义化版本 规范即主版本号.次版本号.修订号。例如1.0.0。每次你发布一个包含新功能的更新就增加次版本号如1.1.0如果只是修复bug就增加修订号如1.0.1。这有助于服务器管理员理解更新的性质。切忌使用像1.0这样缺少一位的版本号某些服务器可能无法正确解析。main (主类)这是整个插件最重要的配置项也是新手最容易出错的地方。它指定了插件启动时服务器应该去加载哪个Java类。这个值必须是你主类的完整限定名也就是包名加类名。例如你的主类Main放在com.yourname包下那么这里就必须写com.yourname.Main。写错了服务器会抛出“Could not load FirstPlugin.jar in folder plugins”或“main class not found”之类的错误。实操心得我强烈建议你在plugin.yml里把name字段的值直接复制作为你项目ArtifactId和最终打包JAR文件名的基础。这样可以最大限度地减少因名称不一致导致的混淆。例如name: FirstPlugin项目ArtifactId也是FirstPlugin最后打出的包就叫FirstPlugin-1.0.0.jar。3.2 进阶字段描述、作者、依赖与加载顺序除了上述三个必填字段plugin.yml还提供了一系列可选字段来完善你的插件信息和管理插件间关系name: FirstPlugin version: 1.0.0 main: com.yourname.Main description: This is my very first Bukkit plugin! author: YourName website: https://yourwebsite.com api-version: 1.16 depend: [Vault] softdepend: [WorldEdit] load: POSTWORLDdescription、author、website这些是展示性信息会显示在/plugins或/version PluginName命令中方便管理员了解插件用途和作者。api-version指定插件所依赖的Bukkit API版本。这应该与你pom.xml中引入的API版本主要部分一致如1.16。设置这个可以防止插件被加载到不兼容的服务器版本上。depend (硬依赖)一个插件名称的列表。你的插件必须依赖这些插件才能运行。如果服务器没有安装列表中的某个插件你的插件将不会被加载。例如经济插件通常硬依赖Vault因为需要它的API来处理经济系统。使用硬依赖要非常谨慎除非绝对必要。softdepend (软依赖)一个插件名称的列表。你的插件可以与这些插件协同工作但没有它们也能正常运行。你需要在代码中检查这些插件是否存在。例如你的插件可能为WorldEdit提供一些扩展功能但没有WE也不影响核心功能。load (加载顺序)定义插件相对于服务器启动阶段的加载时机。可选值有STARTUP服务器启动时最早加载和POSTWORLD世界加载完成后加载默认值。大多数插件使用POSTWORLD即可因为这时世界数据已经就绪。如果你的插件需要修改世界生成等非常底层的逻辑才考虑使用STARTUP。理解并合理运用这些字段能让你的插件更专业与其他插件的集成更顺畅。4. 主类与插件生命周期管理4.1 继承JavaPlugin与重写关键方法现在让我们把目光转向Java代码。在之前创建的com.yourname.Main类中你需要让它继承Bukkit API中的org.bukkit.plugin.java.JavaPlugin类。这是所有Bukkit插件的基石。package com.yourname; import org.bukkit.plugin.java.JavaPlugin; public class Main extends JavaPlugin { Override public void onEnable() { // 当插件被启用时此方法被调用 getLogger().info(我的第一个插件已启用); } Override public void onDisable() { // 当插件被禁用时此方法被调用 getLogger().info(我的第一个插件已禁用。); } }这个简单的类已经是一个功能完整的插件了我们来分析一下extends JavaPlugin这行代码宣告了你的类是一个Bukkit插件。onEnable()这是插件生命周期的起点。当服务器加载插件或管理员使用/reload命令不推荐使用后这个方法会被调用。你应该在这里进行所有的初始化工作注册事件监听器、注册命令、读取配置文件、建立数据库连接、启动定时任务等。getLogger().info()用于向控制台输出一条信息日志这是调试和告知管理员插件状态的重要手段。onDisable()这是插件生命周期的终点。当服务器关闭或插件被卸载时这个方法会被调用。你应该在这里进行所有的清理工作保存数据、关闭数据库连接、取消所有定时任务、释放资源等。确保清理干净避免内存泄漏或数据损坏。服务器加载插件的流程是这样的找到JAR - 读取plugin.yml- 根据main字段找到主类 - 创建主类的实例 - 调用onEnable()。如果onEnable()方法执行过程中抛出了未捕获的异常整个插件加载就会失败。4.2 onLoad() 方法与初始化陷阱除了onEnable和onDisable还有一个不那么常用但有时很关键的方法onLoad()。Override public void onLoad() { // 在插件被启用之前此方法被调用 getLogger().info(插件正在加载...); }onLoad()的调用时机早于onEnable()。它发生在插件实例被创建之后但在任何事件监听器或命令注册之前。那么什么时候该用onLoad()呢主要场景是当你需要进行一些必须在插件启用前完成的、非常底层的初始化或者需要与其他插件的onLoad()阶段进行交互时。例如某些世界管理插件可能在onLoad()阶段就注册世界生成器。对于你的第一个插件以及绝大多数功能型插件你完全不需要重写onLoad()方法。把所有初始化逻辑放在onEnable()里就足够了。这里有一个重要的注意事项在onLoad()阶段Bukkit的许多服务如调度器Scheduler可能还没有完全准备好。因此绝对不要在onLoad()方法中尝试访问玩家、世界、注册事件或执行任何需要Bukkit核心服务已就绪的操作否则很可能导致错误。如果你不确定就别用它。5. 实现第一个服务器命令5.1 命令执行器的创建与注册一个没有交互的插件是缺乏灵魂的。让我们为插件添加一个最简单的命令/hello。当玩家或控制台执行这个命令时插件会回复一条消息。首先你需要创建一个命令执行器CommandExecutor。在Bukkit中任何类只要实现了org.bukkit.command.CommandExecutor接口就可以处理命令。这个接口只有一个方法需要实现boolean onCommand(CommandSender sender, Command command, String label, String[] args)。我们通常在主类中实现这个接口。package com.yourname; import org.bukkit.command.Command; import org.bukkit.command.CommandSender; import org.bukkit.plugin.java.JavaPlugin; public class Main extends JavaPlugin implements CommandExecutor { // 实现接口 Override public void onEnable() { getLogger().info(我的第一个插件已启用); // 注册命令。参数1命令名不带斜杠参数2命令执行器this指主类本身 this.getCommand(hello).setExecutor(this); } Override public boolean onCommand(CommandSender sender, Command command, String label, String[] args) { // 当有人执行 /hello 命令时此方法被调用 sender.sendMessage(§a你好世界这是我的第一个Bukkit插件命令。); return true; // 返回true表示命令处理成功 } // ... onDisable 方法 }代码解释implements CommandExecutor让主类具备处理命令的能力。this.getCommand(hello).setExecutor(this)这是在onEnable()中进行的命令注册。getCommand(hello)会获取一个PluginCommand对象然后我们调用setExecutor(this)将这个命令的处理权交给当前主类实例。这一步必须做否则命令不会生效。onCommand方法这是命令处理的核心。CommandSender sender命令的发送者。可以是玩家Player、控制台ConsoleCommandSender或命令方块。Command command被执行的命令对象。String label用户实际输入的命令别名如果你为命令设置了别名这里可能是别名。String[] args命令参数数组。例如/hello arg1 arg2则args [arg1, arg2]。sender.sendMessage(...)向命令发送者发送一条消息。§a是Minecraft的颜色代码代表绿色。return true;返回true告诉Bukkit命令已成功处理。如果返回falseBukkit会自动向发送者显示该命令的用法usage这个用法需要在plugin.yml中定义。5.2 在plugin.yml中声明命令与权限仅仅在代码中注册命令执行器还不够你必须在plugin.yml文件中声明这个命令否则服务器不会认可它getCommand(hello)会返回null导致注册失败和空指针异常。我们需要在plugin.yml中添加一个commands节点name: FirstPlugin version: 1.0.0 main: com.yourname.Main description: This is my very first Bukkit plugin! author: YourName commands: hello: description: 打个招呼 usage: /command aliases: [hi, greet] permission: firstplugin.hello permission-message: §c你没有权限使用这个命令hello:命令的名称必须和代码中getCommand(hello)里的字符串完全一致。description命令的简短描述会显示在/help等地方。usage命令的使用方法。当onCommand返回false时Bukkit会向玩家发送这个字符串。command是一个占位符会被实际输入的命令或别名替换。aliases命令的别名列表。玩家输入/hi或/greet同样可以触发这个命令。permission执行此命令所需的权限节点。这是一个字符串你可以自由定义通常以插件名开头如firstplugin.hello。permission-message当发送者没有权限时将收到这条消息。现在一个完整的命令系统就建立了plugin.yml声明命令的存在和属性onEnable()中注册执行逻辑onCommand方法处理具体交互。6. 配置文件 config.yml 的生成与使用6.1 默认配置的创建与自动生成一个良好的插件应该允许管理员通过配置文件来自定义其行为而不是把参数硬编码在代码里。Bukkit提供了非常方便的org.bukkit.configuration.file.FileConfiguration类来管理YAML配置文件。首先我们需要创建一个默认的配置文件。在src/main/resources目录下新建一个文件命名为config.yml。在这个文件里你可以定义所有可配置的选项及其默认值。# FirstPlugin 配置文件 messages: welcome: §a欢迎使用本插件 goodbye: §c再见 settings: broadcast-join: true max-players: 50然后在插件的onEnable()方法中你需要添加代码来保存这个默认配置如果它不存在的话并加载它。Override public void onEnable() { getLogger().info(我的第一个插件已启用); this.getCommand(hello).setExecutor(this); // --- 配置文件处理 --- // 1. 保存默认配置将JAR内的config.yml复制到插件数据文件夹仅当文件不存在时 this.saveDefaultConfig(); // 2. 将配置文件加载到内存得到一个FileConfiguration对象 // 之后可以通过 this.getConfig() 来获取这个对象 }saveDefaultConfig()这个方法非常关键。它会在插件的数据文件夹通常位于服务器根目录的plugins/YourPluginName/下寻找config.yml。如果找不到它就会将你放在resources目录下的那个默认config.yml复制过去。这样管理员第一次安装你的插件时就会自动生成一个带有所有默认值的配置文件无需手动创建。6.2 配置的读取、修改与热重载生成了配置文件接下来就要在代码中读取和使用它。我们修改之前的onCommand方法让问候语从配置文件中读取Override public boolean onCommand(CommandSender sender, Command command, String label, String[] args) { // 从配置文件中读取消息 String welcomeMessage this.getConfig().getString(messages.welcome); // 如果配置项不存在提供一个默认值 if (welcomeMessage null) { welcomeMessage §a默认欢迎信息; } sender.sendMessage(welcomeMessage); return true; }this.getConfig()返回的就是我们之前加载的FileConfiguration对象。getString(messages.welcome)使用点分隔的路径来获取嵌套的配置值。YAML的层次结构在这里表现得非常直观。你还可以让管理员在游戏内重载配置而无需重启服务器。这通常通过一个命令来实现例如/yourplugin reload。实现步骤如下在onCommand方法中判断命令和参数。调用this.reloadConfig()。这个方法会丢弃内存中当前的配置并从数据文件夹的config.yml文件重新加载。通知管理员重载完成。Override public boolean onCommand(CommandSender sender, Command command, String label, String[] args) { if (label.equalsIgnoreCase(yourplugin)) { if (args.length 0 args[0].equalsIgnoreCase(reload)) { // 检查权限 if (!sender.hasPermission(firstplugin.reload)) { sender.sendMessage(§c你没有重载插件的权限。); return true; } // 重载配置 this.reloadConfig(); sender.sendMessage(§a插件配置已重载); return true; } } // ... 其他命令处理逻辑 return false; }注意事项reloadConfig()是一个比较“重”的操作它会重新读取整个文件。对于大型配置文件或高频操作频繁重载可能影响性能。更高级的做法是只监听配置文件的变化例如使用Java的WatchService但作为第一个插件简单的重载命令已经足够。7. 项目构建、打包与服务器测试7.1 使用Maven进行打包与依赖管理代码写好了配置文件也齐了现在是时候把它们打包成一个可以扔进服务器plugins文件夹的JAR文件了。这就是Maven大显身手的时候。在IDEA的右侧边栏找到并打开Maven工具窗口如果没看到可以在顶部菜单栏选择 View - Tool Windows - Maven。展开你的项目你会看到一系列Maven生命周期Lifecycle命令。我们只需要关注两个clean清理之前构建生成的文件如target目录。package编译代码运行测试如果有并将项目打包成JAR文件。直接双击packageMaven就会开始工作。你可以在底部的 “Run” 或 “Build” 工具窗口看到构建日志。如果一切顺利最后会显示BUILD SUCCESS。打包好的JAR文件位于项目的target目录下文件名通常是你的ArtifactId-版本号.jar例如FirstPlugin-1.0.0.jar。这个JAR文件是“可执行”的对于Bukkit服务器来说它里面包含了你编译好的所有.class文件在com/yourname/等目录下。META-INF/MANIFEST.MF文件由Maven生成包含JAR元信息。你放在src/main/resources下的所有文件plugin.yml,config.yml它们会被放在JAR包的根目录。关键检查点在把JAR包放入服务器之前我习惯先用压缩软件如7-Zip打开它快速检查两样东西根目录下是否有plugin.yml文件。plugin.yml里的main类路径是否正确并且这个类确实存在于JAR包中对应的路径下。 很多新手遇到的“插件加载失败”问题根源都在这两步。7.2 服务器部署、调试与日志查看现在将生成的FirstPlugin-1.0.0.jar文件复制到你的Minecraft服务器的plugins文件夹中。确保服务器处于关闭状态。启动服务器。在启动日志中你应该能看到类似这样的信息[INFO] [FirstPlugin] Loading FirstPlugin v1.0.0 [INFO] [FirstPlugin] 我的第一个插件已启用这表明你的插件已经被成功加载并启用了。接下来进行功能测试在服务器控制台或游戏内输入/plugins确认FirstPlugin在插件列表中。在游戏内或控制台输入/hello你应该能看到绿色的“你好世界”消息。检查插件数据文件夹plugins/FirstPlugin/确认里面自动生成了config.yml文件并且内容与你设置的默认值一致。修改plugins/FirstPlugin/config.yml文件中的messages.welcome内容然后在游戏内执行你设定的重载命令如果有再次执行/hello看看消息是否更新。调试与日志如果插件没有按预期工作服务器日志是你的第一求助对象。查看logs/latest.log文件或者直接看控制台输出。常见的错误信息包括“Error occurred while enabling FirstPlugin v1.0.0 (Is it up to date?)”通常意味着onEnable()方法中抛出了异常。往下翻日志找到具体的错误堆栈信息Caused by: ...它能精准定位到出错的代码行。“Could not load FirstPlugin.jar in folder plugins”通常是plugin.yml格式错误、缺失或main类路径错误。“No command hello registered”说明命令注册失败检查plugin.yml中是否有commands: hello:的声明以及代码中getCommand(hello)是否在onEnable()中被调用。学会阅读和分析日志是插件开发中最重要的调试技能。8. 进阶第一步事件监听入门8.1 理解Bukkit事件机制命令让插件可以主动响应玩家输入而事件Event则让插件能够被动响应服务器中发生的各种事情例如玩家加入、破坏方块、发送聊天消息等。Bukkit的事件系统是基于“监听器Listener”模式的。整个流程可以这样理解服务器核心或其他插件里发生了某件事比如玩家登录它会创建一个对应的事件对象如PlayerJoinEvent并“抛”出来。所有对此类事件感兴趣的监听器都会收到这个事件对象。监听器执行你编写好的处理代码。处理完毕后事件可能被传递给下一个监听器也可能被“取消”如果事件是可取消的从而阻止事件的默认行为比如取消玩家的加入。要为你的插件添加事件监听你需要做三件事创建一个类来实现org.bukkit.event.Listener接口这个接口是空的只是一个标记。在这个类中编写一个或多个事件处理方法并用EventHandler注解标记它们。在插件的onEnable()方法中向服务器注册这个监听器。8.2 实现一个玩家加入欢迎事件让我们为第一个插件添加一个功能当玩家加入服务器时向他发送一条欢迎消息并在控制台记录。首先创建一个新的类EventListener名字可以自定放在你的主包下。package com.yourname; import org.bukkit.event.EventHandler; import org.bukkit.event.Listener; import org.bukkit.event.player.PlayerJoinEvent; public class EventListener implements Listener { // 1. 实现Listener接口 private final Main plugin; // 持有插件主类的引用方便调用插件方法 public EventListener(Main plugin) { this.plugin plugin; } EventHandler // 2. 用注解标记这是一个事件处理方法 public void onPlayerJoin(PlayerJoinEvent event) { // 从事件对象中获取加入的玩家 org.bukkit.entity.Player player event.getPlayer(); // 向该玩家发送一条私聊欢迎消息 String welcomeMsg plugin.getConfig().getString(messages.welcome, §a欢迎加入服务器); player.sendMessage(welcomeMsg); // 在服务器控制台也记录一下 plugin.getLogger().info(玩家 player.getName() 加入了游戏。); // 你可以修改加入时的系统消息可选 // event.setJoinMessage(§e[] player.getName()); } }代码解释implements Listener声明这个类是事件监听器。EventHandler这是关键注解。它告诉Bukkit下面的onPlayerJoin方法需要被注册为一个事件处理器。方法的参数类型PlayerJoinEvent决定了它监听的是哪种事件。方法名可以任意取但参数必须是具体的事件类型。我们在方法中通过事件对象event获取了玩家然后向他发送消息并记录日志。注意我们通过plugin.getConfig()来读取配置因此需要在构造时传入主类实例。接下来在主类Main的onEnable()方法中注册这个监听器Override public void onEnable() { getLogger().info(我的第一个插件已启用); this.getCommand(hello).setExecutor(this); this.saveDefaultConfig(); // --- 注册事件监听器 --- // 参数1你的监听器实例参数2你的插件实例this this.getServer().getPluginManager().registerEvents(new EventListener(this), this); }getServer().getPluginManager().registerEvents(listener, plugin)是标准的注册方法。第一个参数是监听器对象第二个参数是插件主类对象用于管理监听器的生命周期。现在重新打包插件mvn clean package替换服务器中的旧JAR重启服务器。当一个新玩家加入时他应该会收到你配置的欢迎消息同时控制台也会有对应的日志。至此你的第一个Bukkit插件已经具备了命令交互、配置管理和事件响应的完整能力。这为你打开了一扇门接下来你可以探索更多的事件类型如PlayerQuitEvent,BlockBreakEvent,AsyncPlayerChatEvent实现更复杂、更有趣的服务器功能了。