帖子

Memorial Edition

查看: 111|回复: 1

[插件开发教程] Bukkit 与 PaperMC 技术分析:从基础到动态命令注册与插件开发

[复制链接]

Lv.8 考古家

人气
16 点
金粒
2370 粒
宝石
3 颗
爱心
1 颗
钻石
1243 颗
贡献
1 点

猪灵勋章

发表于 昨天 14:09 | 显示全部楼层 |阅读模式

马上注册,结交更多好友,享用更多功能,让你轻松玩转社区。

您需要 登录 才可以下载或查看,没有账号?立即注册

x

Bukkit 与 PaperMC 技术分析:从基础到动态命令注册与插件开发

一、PaperMC 是什么?

PaperMC 是一个基于 Spigot 的高性能 Minecraft 服务器实现,继承了 Bukkit 和 Spigot 的插件生态,同时通过性能优化和扩展的 API 提供了更强大的功能。PaperMC 是目前主流的 Minecraft 服务器实现之一,广泛用于社区服务器。

PaperMC 的核心特点

  1. 性能优化:PaperMC 优化了区块加载、实体处理和内存管理,通过异步任务支持减少延迟,特别适合高负载服务器。
  2. 兼容性:完全兼容 Bukkit 和 Spigot 插件,同时支持 Paper 独有的 API 功能。
  3. API 扩展paper-api 提供高级功能,如异步事件处理、改进的实体管理和对 NMS(Net Minecraft Server)的直接访问(通过 paperweight-userdev)。
  4. 开源与社区支持:PaperMC 是开源项目,活跃社区持续更新以适配最新 Minecraft 版本(如 1.21.8-R0.1-SNAPSHOT)。

PaperMC 与 Spigot 的关系

PaperMC 是 Spigot 的硬分叉,在 Spigot 的基础上增加了性能优化和额外功能。Spigot 则是 Bukkit 的改进版,解决了原版 Bukkit 的性能瓶颈问题。因此,PaperMC 是 Bukkit 生态的最新演进,推荐用于现代插件开发。

二、Bukkit 是什么?

Bukkit 是一个开源的 Minecraft 服务器插件开发框架,提供标准化的 API,允许开发者通过 Java 创建插件,扩展服务器功能,如添加命令、修改游戏机制或管理玩家数据。

Bukkit 的核心特点

  1. 模块化插件系统:通过继承 JavaPlugin,开发者可以创建独立的插件模块。
  2. 事件驱动模型:Bukkit 提供事件系统,插件可监听和响应游戏事件(如玩家加入、方块破坏)。
  3. 跨版本兼容性:Bukkit API 尽量保持向后兼容,减少版本差异对插件的影响。
  4. 社区生态:Bukkit 拥有庞大的插件社区,开发者可通过 BukkitDev 等平台分享和获取插件。

Bukkit 的局限性

  • 性能问题:原版 Bukkit 在高负载下表现不如 Spigot 或 PaperMC。
  • API 限制:无法直接访问 NMS,限制了某些高级功能的实现。
  • 停止维护:Bukkit 官方项目于 2014 年停止更新,目前由 Spigot 和 PaperMC 社区维护。

三、开发 Bukkit 插件需要注意什么?

开发 Bukkit 插件需要 Java 基础和对 Minecraft 服务器架构的理解。以下是关键注意事项:

1. 开发环境搭建

  • IDE:推荐 IntelliJ IDEA,PaperMC 官方文档以其为例。
  • 构建工具:使用 Maven 或 Gradle 管理依赖。例如,Maven 的 pom.xml 配置:
    <dependencies>
      <dependency>
          <groupId>io.papermc.paper</groupId>
          <artifactId>paper-api</artifactId>
          <version>1.21.8-R0.1-SNAPSHOT</version>
          <scope>provided</scope>
      </dependency>
    </dependencies>

    <scope>provided</scope> 确保 API 由服务器提供,不打包到插件 JAR。

  • Java 版本:Minecraft 1.21.8 需要 Java 17 或更高版本。

2. 插件配置

传统上,插件需要在 plugin.yml 定义基本信息和命令:

name: MyPlugin
version: 1.0
main: com.example.myplugin.MyPlugin
api-version: 1.21
commands:
  hello:
    description: Sends a hello message
    usage: /<command>
    permission: myplugin.hello

然而,现代开发中可以通过反射动态注册命令(见下文)。

3. 动态命令注册

为实现更灵活的命令系统,可通过反射获取 CommandMap 动态注册命令,而不依赖 plugin.yml。以下是优化后的实现:

import org.bukkit.Bukkit;
import org.bukkit.command.Command;
import org.bukkit.command.CommandExecutor;
import org.bukkit.command.CommandMap;
import org.bukkit.command.CommandSender;
import org.bukkit.plugin.java.JavaPlugin;

import java.lang.reflect.Field;

public class MyPlugin extends JavaPlugin {
    @Override
    public void onEnable() {
        try {
            // 获取 CommandMap
            final Field bukkitCommandMap = Bukkit.getServer().getClass().getDeclaredField("commandMap");
            bukkitCommandMap.setAccessible(true);
            CommandMap commandMap = (CommandMap) bukkitCommandMap.get(Bukkit.getServer());

            // 创建自定义命令
            Command myCommand = new Command("hello") {
                @Override
                public boolean execute(CommandSender sender, String commandLabel, String[] args) {
                    // 委托给自定义的 CommandExecutor
                    return new PermissionCommand().onCommand(sender, this, commandLabel, args);
                }
            };

            // 设置命令属性
            myCommand.setDescription("Sends a hello message");
            myCommand.setUsage("/<command>");
            myCommand.setPermission("myplugin.hello");
            myCommand.setPermissionMessage("You do not have permission to use this command!");

            // 注册命令
            commandMap.register("myplugin", myCommand);
            getLogger().info("Command /hello registered successfully!");
        } catch (NoSuchFieldException | IllegalAccessException e) {
            getLogger().severe("Failed to register command: " + e.getMessage());
            e.printStackTrace();
        }
    }
}

// 自定义命令执行器
class PermissionCommand implements CommandExecutor {
    @Override
    public boolean onCommand(CommandSender sender, Command command, String label, String[] args) {
        if (!sender.hasPermission("myplugin.hello")) {
            sender.sendMessage(command.getPermissionMessage());
            return true;
        }
        sender.sendMessage("Hello, world!");
        return true;
    }
}
动态注册的优点
  • 灵活性:无需修改 plugin.yml,可在运行时动态添加命令。
  • 模块化:适合需要动态生成命令的插件(如根据配置文件生成命令)。
  • 可扩展性:便于实现自定义命令系统。
注意事项
  • 反射风险:反射访问 CommandMap 依赖服务器实现,可能因 PaperMC 更新而失效。建议检查服务器版本兼容性。
  • 权限管理:动态注册命令时需手动设置权限(如 setPermission),并在 CommandExecutor 中检查权限。
  • 错误处理:确保捕获反射相关异常,避免插件崩溃。
  • 命令冲突:通过 commandMap.register("myplugin", myCommand) 指定插件前缀(如 myplugin),避免与其他插件命令冲突。

4. 事件监听与数据存储

  • 事件监听:通过 Listener 接口和 @EventHandler 注解处理事件:

    import org.bukkit.event.EventHandler;
    import org.bukkit.event.Listener;
    import org.bukkit.event.player.PlayerJoinEvent;
    
    public class MyPlugin extends JavaPlugin implements Listener {
      @Override
      public void onEnable() {
          getServer().getPluginManager().registerEvents(this, this);
      }
    
      @EventHandler
      public void onPlayerJoin(PlayerJoinEvent event) {
          event.getPlayer().sendMessage("Welcome!");
      }
    }
  • 数据存储:使用 getConfig() 管理 config.yml,或通过 PersistentDataContainer 存储自定义数据:
    player.getPersistentDataContainer().set(new NamespacedKey(this, "key"), PersistentDataType.STRING, "value");

5. 开发注意事项

  • 线程安全:避免在主线程执行耗时操作,使用 Bukkit.getScheduler().runTaskAsynchronously
  • 权限检查:始终验证命令或事件的权限。
  • 调试:在本地 PaperMC 服务器测试插件,确保兼容目标版本(1.21.8)。
  • 文档化:为动态命令提供清晰的文档,说明用法和权限。

四、插件交互的底层逻辑与数据流动

1. 交互流程

  1. 插件加载:服务器启动时加载 plugin.yml 和主类,调用 onEnable 初始化插件(包括动态命令注册)。
  2. 命令处理
    • 玩家输入命令(如 /hello),服务器通过 CommandMap 分发到对应的 Command 对象。
    • Command.execute 调用自定义的 CommandExecutor 处理逻辑。
  3. 事件处理:服务器触发事件(如 PlayerJoinEvent),分发到注册的监听器。
  4. 数据存储:插件通过文件(config.yml)、数据库或 PersistentDataContainer 持久化数据。

2. 数据流动

  • 客户端到服务器:玩家动作(如输入命令、破坏方块)通过数据包发送到服务器。
  • 服务器处理:PaperMC 解析数据包,触发事件或命令,调用插件逻辑。
  • 插件处理:插件通过 API 修改服务器状态(如发送消息、更改玩家数据)。
  • 服务器到客户端:服务器将更新后的状态通过数据包发送回客户端。
  • 内部数据:插件通过配置文件或数据库存储数据,跨会话保持状态。

3. 确保数据流动的注册

  • 命令:通过 CommandMap 注册动态命令。
  • 事件:通过 getServer().getPluginManager().registerEvents 注册监听器。
  • 定时任务:使用 Bukkit.getScheduler() 安排异步或同步任务。
  • 权限:为命令设置权限,并在 CommandExecutor 中检查。
  • 配置文件:通过 saveDefaultConfiggetConfig 管理设置。

五、插件兼容性设计

1. 理解 api-version

plugin.yml 中,api-version 指定插件针对的 Bukkit API 版本,例如:

api-version: 1.21
  • 作用api-version 告诉服务器插件依赖的 API 版本,服务器会检查是否兼容。
  • 兼容性:PaperMC 通常向后兼容较新的 API 版本,但不支持较旧的版本(如 1.6.4)。如果 api-version 设置为 1.21,插件可能无法在 1.6.4 的服务器上运行。

2. 兼容 1.6.4 的可能性

将 1.21.8 的插件兼容到 1.6.4(2013 年发布,API 版本约 1.6)几乎不可行,原因如下:

  • API 变更:Bukkit API 在 1.6.4 到 1.21.8 之间发生了重大变化,许多方法被弃用或重构。例如,1.6.4 不支持现代的异步事件、新的实体 API 或 PersistentDataContainer
  • Minecraft 核心变更:Minecraft 从 1.6.4 到 1.21.8 引入了新方块、实体和游戏机制,底层 NMS 代码完全不同,导致插件无法直接适配。
  • PaperMC 约束:PaperMC 仅支持较新的 Minecraft 版本(通常为最近几年的版本)。1.6.4 仅支持原版 Bukkit 或早期 Spigot,PaperMC 的优化和 API(如 paper-api)无法在 1.6.4 上运行。
  • 依赖问题paper-api 1.21.8 依赖 Java 17,而 1.6.4 的服务器通常运行在 Java 6 或 7 上,存在 JVM 兼容性问题。
可行方案
  • 条件编译:使用反射或条件逻辑检测服务器版本,针对不同版本调用不同方法。例如:
    if (Bukkit.getVersion().contains("1.6")) {
      // 1.6.4 兼容代码
    } else {
      // 现代版本代码
    }

    但这需要为每个版本维护大量代码,增加开发复杂性。

  • 多版本插件:发布多个插件版本,分别针对 1.6.4 和 1.21.8,分别使用对应的 API 和依赖。
  • 限制功能:在 1.6.4 上仅实现基本功能,避免使用新 API。
  • 使用兼容层:某些插件框架(如 ProtocolLib)提供跨版本兼容性,但仍无法完全弥合 1.6.4 到 1.21.8 的差距。
实际建议

兼容 1.6.4 成本极高,且用户群体有限。建议:

  • 专注于 1.13+(现代化 API 的起点,如扁平化更新)。
  • plugin.yml 中明确 api-version: 1.13,并测试插件在 1.13 到 1.21.8 的兼容性。
  • 如果必须支持 1.6.4,开发独立的轻量级插件,基于原版 Bukkit API。

六、Bukkit 为我们解决了什么问题?

Bukkit 提供了一个抽象的开发框架,解决了以下问题:

  1. 简化开发:通过 API 屏蔽 NMS 复杂性,开发者无需直接操作数据包。
  2. 事件驱动:事件系统允许轻松响应游戏行为。
  3. 插件隔离:确保插件互不干扰,运行在独立上下文。
  4. 跨版本支持:尽量减少版本差异的影响(但不包括极端版本如 1.6.4 到 1.21.8)。
  5. 生态支持:提供文档和社区资源,降低开发门槛。

七、学习 Bukkit API 的建议(基于已有 Java 基础)

您提到已有良好的 Java 基础,这为学习 Bukkit API 提供了坚实的基础。以下是针对您的学习路径建议:

1. 掌握 Bukkit/Paper API 核心

  • 事件系统:深入理解 org.bukkit.event 包,学习常见事件(如 PlayerJoinEventBlockBreakEvent)和优先级机制。
  • 命令系统:熟悉传统命令注册和动态命令注册(如 CommandMap)。
  • 玩家与世界管理:学习 org.bukkit.entity.Playerorg.bukkit.Worldorg.bukkit.inventory 包,掌握玩家操作、方块修改和库存管理。
  • 数据持久化:熟练使用 config.ymlPersistentDataContainer 存储数据。

2. 学习 PaperMC 独有功能

  • 异步支持:PaperMC 提供异步事件和任务(如 runTaskAsynchronously),学习如何优化性能。
  • NMS 访问:通过 paperweight-userdev 访问底层代码,适合高级功能(如自定义实体)。
  • Paper API:探索 io.papermc.paper 包中的扩展功能,如改进的粒子效果和异步区块加载。

3. 实践与项目

  • 简单插件:实现基础功能(如欢迎消息、自定义命令)。
  • 复杂插件:尝试开发 GUI 界面、数据库集成或自定义游戏机制。
  • 开源贡献:参与 PaperMC 或现有插件的 GitHub 项目,学习代码结构和最佳实践。

4. 调试与优化

  • 本地服务器:搭建 PaperMC 1.21.8 测试服务器,熟悉调试流程。
  • 性能分析:使用 PaperMC 的 /timings 命令分析插件性能,避免阻塞主线程。
  • 日志管理:通过 getLogger() 记录详细日志,便于调试。

5. 社区与资源

  • 文档:阅读 PaperMC 官方文档(docs.papermc.io)和 API 参考(jd.papermc.io)。
  • 社区:加入 SpigotMC 论坛、PaperMC Discord 或 BukkitDev,获取最新教程和解决方案。
  • 开源代码:学习 GitHub 上热门插件的代码(如 EssentialsX、WorldEdit)。

6. 高级主题

  • 反射与 NMS:深入学习反射(如动态命令注册)和 NMS 操作,处理复杂需求。
  • 多版本兼容:掌握如何使用反射或条件逻辑支持多个 Minecraft 版本。
  • 插件框架:探索第三方框架(如 CommandAPI、InventoryGUI),简化开发。

7. 推荐学习步骤

  1. 搭建开发环境,运行一个简单的 “Hello World” 插件。
  2. 实现动态命令注册和事件监听。
  3. 学习数据持久化和异步任务。
  4. 开发一个中等复杂度的插件(如简单的经济系统)。
  5. 研究 PaperMC 独有功能,尝试优化性能或使用 NMS。

八、总结

PaperMC 作为 Bukkit 和 Spigot 的优化版本,提供高性能和扩展 API,是现代 Minecraft 插件开发的首选。动态命令注册通过反射和 CommandMap 实现,提供了灵活性,但需注意反射风险和权限管理。插件兼容性受限于 API 和 Minecraft 版本差异,兼容 1.6.4 到 1.21.8 几乎不可行,建议聚焦 1.13+。基于您的 Java 基础,学习 Bukkit API 应注重核心功能、Paper 扩展、实践项目和社区资源,逐步深入高级主题如 NMS 和多版本兼容。

Lv.9 牧场主

人气
977 点
金粒
72 粒
宝石
2 颗
爱心
44 颗
钻石
1501 颗
贡献
8 点

Java正版勋章Windows 10正版勋章

发表于 昨天 23:13 | 显示全部楼层
关于反射定义命令,我有2点看法:

1. Paper API中有通过brigadier定义命令的API,可以在onEnable()中设置命令,而不再需要折腾[paper-]plugin.yml,只不过需要的版本比较新,印象中这一API在1.21.7才稳定。
2. Paper生态中有一个Gradle插件叫 paperweight,借助它可以直接引入NMS作为依赖,不再需要反射。并且由于Paper从1.20.5开始彻底转向官方映射表(MojMaps),和Bukkit API版本相绑定的NMS包名不复存在,因此在不接触非public类/字段/方法的情况下,不再有使用Class.forName进行反射引用等hack的必要性。

我自己的插件PaperSpeed Zero,就是使用了Paper提供的brigadier API以及paperweight-userdev插件进行开发(可能因为我写模组写的比较多,所以对NMS内容比较熟悉)。尽管插件代码由Kotlin而非Java编写,对于不熟悉Kotlin语言的开发者可能存在一定阅读难度,但如果楼主有意了解该插件有关内容,亦可站内私信或邮箱(后者更即时)联系。
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

尔辈不能究物理。

Archiver|小黑屋| MCBBS纪念版 ( 新ICP备2024014954号|兵公网安备66010002000149号 )|隐私政策| 手机版

GMT+8, 2025-8-1 22:46 , Processed in 0.416133 second(s), 23 queries , Redis On.

"Minecraft"以及"我的世界"为美国微软公司的商标 本站与微软公司没有从属关系

© 2010-2025 MCBBS纪念版 版权所有 本站内原创内容版权属于其原创作者,除作者或版规特别声明外未经许可不得转载

返回顶部