首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >Bukkit NMS 开发实践 —— 创建你自己的自定义实体(适用于 1.16.3 – 1.16.5 版本)

Bukkit NMS 开发实践 —— 创建你自己的自定义实体(适用于 1.16.3 – 1.16.5 版本)

作者头像
HikariLan贺兰星辰
发布于 2022-10-27 01:55:56
发布于 2022-10-27 01:55:56
1.7K00
代码可运行
举报
文章被收录于专栏:HikariLan's BlogHikariLan's Blog
运行总次数:0
代码可运行

本文最后更新于 494 天前,其中的信息可能已经有所发展或是发生改变。

Bukkit NMS 开发实践 —— 创建你自己的自定义实体(适用于 1.16.3 – 1.16.5 版本)

什么是 NMS?

NMS 是 net.minecraft.server 包的简写,是 CraftBukkit 服务端及其下游服务端的底层实现,其代码包含 Mojang 发布的 Vanilla 服务端代码和 SpigotMC 添加的、用于与 BukkitAPI 进行交互的代码。在开发者无法借助 BukkitAPI 完成所需要的功能时,开发者我常常使用 NMS 进行开发。NMS 开发是底层行为,同时跨版本兼容性较差,除非必须使用,否则还请尽量使用 BukkitAPI。NMS 仅存在于编译后的服务端内部,不属于 BukkitAPI 内容。各版本的 NMS 包名一般均为 net.minecraft.server.v版_本_R号,如 net.minecraft.server.v1_16_R3。NMS 包内为扁平结构,没有二级包。NMS 包内类名为 Spigot 定义的反混淆名;方法、字段名一部分为 Spigot 定义的反混淆名,一部分为原混淆名;方法参数名一般为原混淆名。本教程旨在教授 Bukkit 开发者以 NMS 使用方法,拓展 Bukkit 开发者的开发视野。

自 1.17 后,SpigotMC 开始提供 Mojang 混淆表版本的 Spigot 服务端,这意味着大大简化了开发难度 —— 不需要再对照混淆表一个一个看 NMS 方法名,而可以像 Forge 或是 Fabric 开发者一样使用各自的反混淆代码直接进行开发 —— 只需要使用 Spigot 提供的 SpecialSource 工具将 Mojang Mapping 转换回 obf 版本即可发布。

如何使用 NMS?

要想使用 NMS,您必须手动导入编译好的 CraftBukkit/Spigot 服务端核心,这样才能获取其中内置的 NMS。对于 Paper 及其下游服务端来说,不应该直接导入服务端核心本体,而应该导入运行一次服务端后生成的 patched_x.x.x.jar 文件。

教程:创建自定义实体

很显然,BukkitAPI 没有向我们提供自定义实体的功能,甚至,实体的类型是确定的,不能更改的。因此,要想自定义实体,必须使用 NMS。当然,我们并不能创建 Forge 或是 Fabric 意义上完全自定义模型的实体。但是,我们能够通过继承原版存在的实体,创建一个新的实体类型,为这个新的实体类型指定一些交互。本例中,我们将会通过创建一个会在夜间燃烧、不做任何交互、拥有 Boss 血条的巨人僵尸来演示这一过程。

继承已有实体

让我们创建 EntityCustomGiantZombie 类,继承 net.minecraft.server.v1_16_R3.EntityGiantZombie 类:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
public class EntityCustomGiantZombie extends EntityGiantZombie {}

接下来,初始化该实体,实现超类构造器:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
public EntityCustomGiantZombie(EntityTypes<? extends EntityGiantZombie> var0, World var1) {
        super(var0, var1);
}

注意,此处的 World 不是我们熟识的 org.bukkit.World 接口,而是 net.minecraft.server.v1_16_R3.World 抽象类,因此不能一概而论。

当然,我们可以通过以下代码实现 Bukkit World 和 NMS World 的互转:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
//Bukkit World to NMS World
org.bukkit.World bukkitWorld = nmsWorld.getWorld();
// NMS World to Bukkit World
net.minecraft.server.v1_16_R3.World nmsWorld = ((CraftWorld) bukkitWorld).getHandle();

其实,调用 net.minecraft.server.v1_16_R3.World#getHandle() 返回的并非 org.bukkit.World 接口,而是 org.bukkit.craftbukkit.v1_16_R3.CraftWorld 类,其为 org.bukkit.World 在 CraftBukkit 服务端中的内部实现,因此可以直接转换到 World 接口。事实上,nmsWorld#getWorld() 方法返回的也是 CraftWorld 类。

要想生成该实体,则应该调用 WorldServer#addEntity(Entity, SpawnReason) 方法初始化实体,然后使用 Entity#setPositionRotation(double, double, double, float, float) 传送实体到出生位置。为了简便流程,我们可以创建一个可传入 Bukkit Location,并可以自动设置实体出生位置的构造函数:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
public EntityCustomGiantZombie(Location loc) {
        this(EntityTypes.GIANT, ((CraftWorld) loc.getWorld()).getHandle());
        setPositionRotation(loc.getX(), loc.getY(), loc.getZ(), loc.getYaw(), loc.getPitch());
    }

然后,在适当的位置初始化该实体,比如,某一个 Bukkit EventListener 中:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
((CraftWorld) e.getPlayer().getWorld()).getHandle().addEntity(new EntityCustomGiantZombie(e.getPlayer().getLocation()), CreatureSpawnEvent.SpawnReason.CUSTOM);

这样,你就能看到一个由你自定义的巨人僵尸实体了!

添加 Boss 血条

接下来,我们尝试向这个自定义实体添加 Boss 血条。

添加 Boss 血条大概需要有三步操作:

  1. 当玩家进入追踪视野时显示 Boss 血条
  2. 当玩家离开追踪视野时隐藏 Boss 血条
  3. 当怪物受到攻击时令 Boss 血条相应减少血量

首先,我们需要定义一个 Boss 血条。在 EntityCustomGiantZombie 类中添加以下字段:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
private final BossBattleServer bossBar;

并在底层构造器中初始化这个 Boss 血条:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
bossBar = new BossBattleServer(new ChatComponentText("Boss 血条示例").a(EnumChatFormat.GOLD), BossBattle.BarColor.BLUE, BossBattle.BarStyle.NOTCHED_12);
        bossBar.setDarkenSky(true);
    }

这初始化了一个血条名为金色的 “Boss 血条示例”,血条颜色为蓝色的,1/12 比例风格的,在玩家显示 Boss 血条时时天空变暗的 Boss 血条。

然后,我们需要覆盖 void b()void c() 两个方法,这两个方法在 MCP 中描述如下:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
/**
    * Add the given player to the list of players tracking this entity. For instance, a player may track a boss in order
    * to view its associated boss bar.
    */
   public void addTrackingPlayer(ServerPlayerEntity player) {
   }

   /**
    * Removes the given player from the list of players tracking this entity. See {@link Entity#addTrackingPlayer} for
    * more information on tracking.
    */
   public void removeTrackingPlayer(ServerPlayerEntity player) {
   }

这正是我们需要的,可以动态显示和隐藏 Boss 血条的方法。覆盖这些方法,并添加一些内容:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
    @Override
    public void b(EntityPlayer entityplayer) {
        super.b(entityplayer);
        this.bossBar.addPlayer(entityplayer);
    }

    @Override
    public void c(EntityPlayer entityPlayer) {
        super.c(entityPlayer);
        this.bossBar.removePlayer(entityPlayer);
    }

最后,覆盖 void tick() 方法,该方法一看名字就知道是干什么的了:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
    @Override
    public void tick() {
        super.tick();
        this.bossBar.setProgress(getHealth() / getMaxHealth());
    }

其中 bossBar.setProgress(float) 接受一个单精度浮点数,为血条剩余的血量百分比。

需要注意的是,一定要调用 super.tick(),否则该怪物完全被冻结,不会产生任何交互。

让怪物在夜间燃烧

要想让怪物在夜间燃烧,则需要在每 tick 检测怪物是否处于夜间环境,如果是,则使怪物燃烧。因此,在 tick() 方法键入以下代码:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
if (world.isNight()) setOnFire(1, false);

其中 setOnFire(int, boolean) 的第一个参数为燃烧的 tick 数,由于是 1 tick 检测一次,因此我们在这里填写 1;第第二个参数为是否触发 BukkitAPI 的 EntityCombustEvent 事件,为了避免事件被多次调用,这里我们填写 false

这样,怪物就会在夜间燃烧了。

自定义怪物行为

要想自定义怪物行为,我们需要为怪物添加 PathfinderGoal,因为我们不希望保留怪物原本的行为,因此我们需要刷新怪物的 goalSelector(行为选择器) 和 targetSelector(攻击目标选择器)。他们均为 PathfinderGoalSelector 类型的对象。经过研究该对象我们可以发现,PathfinderGoal 对象被包装为 PathfinderGoalWrapped 对象后,存储于private final Set<PathfinderGoalWrapped> d 字段中,由于该字段是 private final 的,因此我们需要通过反射修改该字段。在怪物的底层构造器中键入以下代码:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
try {
            Field dField = PathfinderGoalSelector.class.getDeclaredField("d");
            dField.setAccessible(true);
            dField.set(goalSelector, Sets.newLinkedHashSet());
            dField.set(targetSelector, Sets.newLinkedHashSet());
        } catch (NoSuchFieldException | IllegalAccessException noSuchFieldException) {
            noSuchFieldException.printStackTrace();
        }

这样,我们便得到了一个没有任何行为的怪物。如果我们还需要为怪物添加行为,只需要为 goalSelector 或是 targetSelector 添加继承了 PathfinderGoal 类的对象即可。NMS 中本身就包含了大量的 PathfinderGoal,大家可自行探索。

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2021-6-16 1,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
暂无评论
推荐阅读
编辑精选文章
换一批
我的世界Java版开服教程(Ubuntu)
Linux开服也很简单,内存占用小,推荐使用,下面使用Ubuntu18.04.1演示
Magneto
2022/09/14
6.5K0
我的世界Java版开服教程(Ubuntu)
BukkitNMS开发中蕴含的混淆技术 发布于
Spigot的NMS是对net-minecraft-server包(也是nms缩写的由来)的一个综合性反射工具,即便读者可能不知道Minecraft是什么或者从未参与过Minecraft伺服器的插件开发工作,但我仍会为每一位读者详细介绍这其中所蕴含的一些技术和实现原理。读者需要知道的是:Spigot 更专注于 Minecraft 的插件开发和服务器功能扩展,而不是提供一个完整的企业级应用开发框架,因此虽然它不像Spring那样专业但是两者仍然存在着许多相似性很高的技术原理。
DioxideCN
2023/10/21
6800
【腾讯云的1001种玩法】搭建属于自己的Minecraft服务器
该文章介绍了如何在Minecraft中制作一个简单的腐竹服务器,包括安装Java、配置SSH免密登录、使用Nginx反向代理、配置Docker以及如何使用Kubernetes部署和管理Minecraft服务器。同时,文章还介绍了如何利用流量监控工具来实时查看服务器的网络流量情况,并通过Shell脚本定时清理系统日志文件,以确保服务器安全稳定地运行。
陈润泽
2017/03/03
14K3
【腾讯云的1001种玩法】搭建属于自己的Minecraft服务器
聊聊 PaperAPI 提供的自定义生物 AI 系统
灵感:https://www.mcbbs.net/thread-1285618-1-1.html(原文发布于 https://izzel.io/2021/12/19/living-things)
HikariLan贺兰星辰
2022/10/27
1.2K0
一条龙服务: 开服从入门到精通(Minecraft-Server-Starting-Guide)
本文原文发布于 GitHub,完整版可见于 此处 。因为使用了一些非标准 Markdown 语法因此在本页面可能显示不完全,在此处发布仅仅只是为了归档原文。
HikariLan贺兰星辰
2022/10/27
4.4K0
Minecraft配置文件参数说明(JAVA服务器篇)
JAVA版Minecraft服务器启动后会生成配置文件server.properties,该文件位于minecraft_server/ 根目录下。
云惑雨问
2025/03/25
6130
Minecraft配置文件参数说明(JAVA服务器篇)
从零构建自己的MCP Server
在游戏开发与自定义服务器领域,Minecraft Coder Pack(MCP)一直是开发者修改和扩展Minecraft的核心工具。然而,构建一个完整的MCP Server(Modded Coded Protocol Server)需要跨越多个技术领域,包括逆向工程、网络协议解析、自定义逻辑实现等。本文将从零开始,逐步讲解如何构建一个功能完整的MCP Server,涵盖环境搭建、协议解析、自定义逻辑开发、性能优化等关键环节,并附代码示例与调试技巧。
Michel_Rolle
2025/03/03
3.6K0
记ipv6 MineCraft 开服
针对2021年网络特色 MineCraft 开服教程 针对版本 (1.13.2 - 1.16.5) 注意:1.17需要最新的java版本,本教程的java下载地址都是java8,并非最新版本,请移步官网下载。
赤月未咲
2023/03/17
3.1K0
​​钉钉自定义机器人简单使用
年前公司的需求里面有用到钉钉机器人,使用之后发现真的非常简单,不得不感叹阿里的牛逼,这篇文章总结了一下个人使用钉钉机器人的经验,同时介绍个人据此构建一个工具类来方便后续直接“开箱即用”,希望对于读者有所启发。
阿东
2021/08/16
4.2K0
​​钉钉自定义机器人简单使用
MyBatis-Plus 分页查询以及自定义sql分页
物理分页:相当于执行了limit分页语句,返回部分数据。物理分页只返回部分数据占用内存小,能够获取数据库最新的状态,实施性比较强,一般适用于数据量比较大,数据更新比较频繁的场景。
全栈程序员站长
2022/09/13
8.9K0
Allure报告开发自定义插件
allure 官网文档 https://docs.qameta.io/allure/
全栈程序员站长
2022/09/17
9760
Allure报告开发自定义插件
Minecraft反代(跨服)服务端搭建从入门到精通(For BungeeCord & Velocity)
本文旨在通过一站式的教程,教会读者如何对目前市面上流行的反向代理服务端(跨服服务端)进行安装和配置。
HikariLan贺兰星辰
2022/10/27
5.6K0
实体识别(1) -实体识别任务简介
命名实体识别(Named Entity Recognition,简称NER) , 是指识别文本中具有特定意义的词(实体),主要包括人名、地名、机构名、专有名词等等,并把我们需要识别的词在文本序列中标注出来。
致Great
2023/08/25
8090
实体识别(1) -实体识别任务简介
ColyseusJS 轻量级多人游戏服务器开发框架 - 中文手册(下)
快速上手多人游戏服务器开发。后续会基于 Google Agones,更新相关 K8S 运维、大规模快速扩展专用游戏服务器的文章。拥抱☁️原生? Cloud-Native! 系列 ColyseusJS
为少
2021/05/27
2.8K0
CMI | 常用命令及其命令作用
/actionbarmsg [指定玩家/all] [消息] 给指定玩家或所有人发送一条actionbar消息 /afk (玩家名) (理由) 将自己或他人切换为AFK模式.可说明理由 /afkcheck [玩家名] 检查玩家的AFK状态 /air [玩家名] [空气值] 设置指定玩家的空气值 /alert [玩家名] (理由) 警报玩家,当拥有特定权限的玩家或管理员上线时收到提示信息 需要拥有权限节点 command.alert.info.inform 才能收到消息 /aliaseditor (新指令别名)
BreezeCloud
2022/10/04
7K0
ChatGPT 高级数据分析用于自定义 Matplotlib 测井图
ChatGPT 的代码解释器,现在更名为高级数据分析,已经发布一段时间了。它于2023年7月6日推出,是由OpenAI开发的插件,允许用户上传数据并对其进行分析。这可以包括清理数据、创建可视化图表和总结数据。
磐创AI
2024/07/01
3050
ChatGPT 高级数据分析用于自定义 Matplotlib 测井图
使用opennlp自定义命名实体
opennlp的自定义命名实体的标注,给以了一定定制空间,方便开发者定制各自领域特殊的命名实体,以提高特定命名实体分词的准确性。
code4it
2018/09/17
1.4K0
HarmonyOS 开发实践 —— 基于Text的自定义字体
在windowStage.loadContent中进行字体的注册(可通过font.registerFont进行注册),这样注册完成后,整个应用内都可以使用。
小帅聊鸿蒙
2024/11/26
3630
(二十九)c#Winform自定义控件-文本框(二)
GitHub:https://github.com/kwwwvagaa/NetWinformControl
冰封一夏
2019/09/11
6310
【命名实体识别】训练端到端的序列标注模型
导语 PaddlePaddle提供了丰富的运算单元,帮助大家以模块化的方式构建起千变万化的深度学习模型来解决不同的应用问题。这里,我们针对常见的机器学习任务,提供了不同的神经网络模型供大家学习和使用。本周推文目录如下: 3.12:【命名实体识别】 训练端到端的序列标注模型 3.13:【序列到序列学习】 无注意力机制的神经机器翻译 3.14:【序列到序列学习】 使用Scheduled Sampling改善翻译质量 3.15:【序列到序列学习】 带外部记忆机制的神经机器翻译 3.16:【序列到序列学习】 生成
用户1386409
2018/03/15
2.4K0
【命名实体识别】训练端到端的序列标注模型
推荐阅读
相关推荐
我的世界Java版开服教程(Ubuntu)
更多 >
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档