<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>插件设计 on Axium.钰</title><link>https://blog.axiumyu.xyz/tags/%E6%8F%92%E4%BB%B6%E8%AE%BE%E8%AE%A1/</link><description>Recent content in 插件设计 on Axium.钰</description><generator>Hugo -- gohugo.io</generator><language>en</language><lastBuildDate>Sat, 03 Oct 2026 01:01:31 +0800</lastBuildDate><atom:link href="https://blog.axiumyu.xyz/tags/%E6%8F%92%E4%BB%B6%E8%AE%BE%E8%AE%A1/index.xml" rel="self" type="application/rss+xml"/><item><title>浅谈我的 Paper 插件设计思想</title><link>https://blog.axiumyu.xyz/post/202610030101/</link><pubDate>Sat, 03 Oct 2026 01:01:31 +0800</pubDate><guid>https://blog.axiumyu.xyz/post/202610030101/</guid><description>&lt;img src="https://blog.axiumyu.xyz/" alt="Featured image of post 浅谈我的 Paper 插件设计思想" /&gt;&lt;p&gt;今天写点不一样的，简单说说看我是怎么写插件的，以及我是为什么会这么写插件的&lt;/p&gt;
&lt;p&gt;其实写这个东西是因为，我此前专门尝试开过一个和 ds 的对话，看看我能不能把我自己设计一个插件的全过程写出来，让 ds 一步步顺着我的思路复刻出一个一样的设计&lt;/p&gt;
&lt;p&gt;结果上大体是实现了的，所以现在我尝试把我做服务器两年来，写插件一年半以来几乎所有的设计方式写出来给你看看，并且我不会只告诉你原则，我会把我此前踩过的坑也给你看看，我也不是一帆风顺就能到今天的设计风格的&lt;/p&gt;
&lt;hr&gt;

 &lt;blockquote&gt;
 &lt;p&gt;&lt;em&gt;1. 在做任何东西之前，问问你自己：我真的需要它吗？&lt;/em&gt;&lt;/p&gt;

 &lt;/blockquote&gt;
&lt;p&gt;需求分析永远是任何解决方案的第一步，不要觉得它很无用，觉得“哎，我自己写的东西给我自己用嘛，我肯定说得清楚需求，写文档还多此一举”&lt;/p&gt;
&lt;p&gt;我可以非常明确地告诉你，曾经我也是这么想的，然后做出来的东西到现在来看，能跑，但有很多潜在的问题&lt;/p&gt;
&lt;p&gt;一方面，现在我相信大多数的独立开发者，都有 vibe coding 的经历，甚至可能是重度 vibe coder，因为一个人开发，要做的事情真的很多，你不可能为了修一个 bug 浪费一下午吧，你的那么多想法都没来得及实现，就埋在这些代码细节里，和 bug 斗智斗勇，然后忘记了你本来有的设计&lt;/p&gt;
&lt;p&gt;你用 ai 写代码，你总得告诉 ai 你要写什么东西吧，你把你的设计需求整理出来，然后发给 ai，或者你连完整的设计文档都可以让 ai 来写，你只需要告诉 ai 一个想法，然后在讨论中完善这个想法，问 ai 有哪些你没有考虑到的部分&lt;/p&gt;
&lt;p&gt;这最后一步是关键的：你必须要让多个人/多个 ai 来同时看你的设计文档，每一个人/ai 都能给你挑出不同的问题，有些可能是扯淡，有些可能是你接受的约束，也有些可能是你真实没有考虑到的问题；这两种不管哪个情况都是有价值的&lt;/p&gt;
&lt;p&gt;举个例子就是，我此前让 astra 写的一个插件，功能是让玩家可以消耗某些资源，然后在铁砧上打上不兼容，超出等级上限的附魔，或者取下某个附魔变成独立的附魔书&lt;/p&gt;
&lt;p&gt;这是一个相当简单的插件，但我不会因为它的功能简单就放弃写文档，我还是会写，并且迭代了 4 版文档&lt;/p&gt;

 &lt;blockquote&gt;
 &lt;p&gt;可以在 &lt;a class="link" href="https://blog.axiumyu.xyz/docs/OverEnchanted%e9%9c%80%e6%b1%82%e6%96%87%e6%a1%a3v4.md" &gt;这里&lt;/a&gt; 看到这个最终的设计文档，当然，文档本身也是 ai 写的，我只负责提出想法，和让 ai 自己迭代版本，提出问题让我决定，它变成了现在的样子，它可能不是最好的，但是对我来说已经足够了&lt;/p&gt;

 &lt;/blockquote&gt;
&lt;p&gt;你可能会说，哦，我是传统派，我不用 vibe coding 或者任何 ai 辅助编码，ok 没问题，因为它还有另一方面的原因：把需求写出来，其实也是为自己澄清，给自己一个梳理的机会，有些需求你需要把它写下来之后，你才会发现需求里本身就是有问题的，有没有描述清楚的部分&lt;/p&gt;
&lt;p&gt;你自己一个人是很难考虑到所有人的需求的，这点我也深有体会，所以就算你不使用 ai 辅助编程，我也希望你能够把你的解决方案讲给你的朋友，其他开发者，或者你信任的玩家，甚至是其他 ai，把你的方案发给他们，也许你会得到更多的建议，有些开发者可能会反对你的设计，不要急着为自己辩护，先想想他们说的有没有道理，他们说这些话，是建立在什么的前提之上，你能否满足这个前提，有些建议确实是难听但中肯的，这时候一定要重新审视你的需求&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;补充一点：&lt;/p&gt;
&lt;p&gt;这个插件并不是一开始就这么简单的，一开始比这复杂多了，因为走了不少弯路，那时候我还没有那么强的需求分析意识，所以就顺着感觉写了，然后写出来就变成 &lt;a class="link" href="https://modrinth.com/plugin/over-enchant" target="_blank" rel="noopener"
 &gt;这个插件&lt;/a&gt; 这样，非常复杂，有一大堆材料列表，还要一个自定义方块，还要世界高度小于 -64，材料都放都放不下，再加一个自定义界面，虽然最后确实能跑，但写的什么烂玩意，我相信你如果去看这个插件源码，一定会感叹“我去，这人写的啥玩意啊，跟石一样，看来我的插件也做得不差嘛”&lt;/p&gt;
&lt;p&gt;没毛病，好吧，我也是这么想的，一方面代码是当时的 gpt 写的，另一方面是逻辑也实在是没法看，就是因为没写需求分析&lt;/p&gt;
&lt;p&gt;所以前阵子重写插件列表时，第一个想到的就是重写这个插件，这次一定要把需求搞清楚了再写&lt;/p&gt;
&lt;hr&gt;

 &lt;blockquote&gt;
 &lt;p&gt;&lt;em&gt;2. 你想用这个设计来解决什么问题？这个问题的本质是什么？&lt;/em&gt;&lt;/p&gt;

 &lt;/blockquote&gt;
&lt;p&gt;其实比需求分析更应该优先完成的，是问题的本质&lt;/p&gt;
&lt;p&gt;为什么说问题的本质比解决问题更关键？因为大多数时候，你看到的问题，仅仅是一系列因果作用的最终环节，而这个问题本身甚至还要等到这个环节的潜在问题到达一定程度时，你才会意识到，甚至有时候还要再等到你意识到一段时间后，才会想着要去解决它，这时候问题可能已经非常严重了&lt;/p&gt;
&lt;p&gt;fail-fast 原则也是在说这个，如果你觉得这个地方可能出问题，那么就让它尽早出问题，不要将带有潜在问题的东西一路向下传递，等到最后爆发时，你才去解决问题，那么你需要一层层顺着调用往上走，才能发现问题的根本，而我们现在不是在 debug，而是在设计前期，同样也需要用这个思路，尽可能发现潜在问题的本质和根源&lt;/p&gt;
&lt;p&gt;还是拿刚才那个插件说，你点开 modrinth 的画廊页面，会发现我为了实现这么一个功能，硬是加了十几种材料需求&lt;/p&gt;
&lt;p&gt;&lt;img src="https://blog.axiumyu.xyz/20261003_Pasted%20image%2020261003122659_7486545050470514026.webp"
	width="617"
	height="958"
	loading="lazy"
	
		alt="旧的 gitbook 公告，上面写了材料列表"
	
 
	
		class="gallery-image" 
		data-flex-grow="64"
		data-flex-basis="154px"
	
&gt;&lt;/p&gt;
&lt;p&gt;我为什么要在超限系统里加上这么多材料消耗呢，原因在于，当时服务器的玩法循环里，有很多资源是收集了，却没有用处，于是我想给这些资源加上一个合理的出口，加到哪呢？&lt;/p&gt;
&lt;p&gt;👆🤓诶，给你加到附魔上吧，正好服务器有那么多附魔，而且原版的附魔等级都被提升了，那么再提升一点也无所谓吧&lt;/p&gt;
&lt;p&gt;ok，一拍脑瓜，开始实现&lt;/p&gt;
&lt;p&gt;但我当时没有想到的是，我只看到了怎么消耗多余的资源，却没有想过这些资源，它们为什么会多余下来？真正的问题，有没有可能其实是资源循环设计得不合理？而不是给资源加一个出口就能解决的事情&lt;/p&gt;
&lt;p&gt;所以超限附魔消耗材料的问题，本质上是解决资源循环的问题，附魔本来就已经有那么多的扩展了，现在再加一个，不感觉有很多重复吗&lt;/p&gt;
&lt;p&gt;实际怎么样呢，实装之后，我发现这个系统几乎只有我在使用，而另一些玩家则发现需要的材料实在是太多而且有些过于昂贵，或者很难收集，因此没有使用&lt;/p&gt;
&lt;p&gt;那我呢，就因为这样，作为玩家的我，就解决了自己的资源循环问题吗？并没有，实际上是，这样的资源需求倒逼我去收集更多的稀缺资源，然后疯狂刷龙只为了获取更多龙息和龙蛋，在末地乱飞只为了找龙头&lt;/p&gt;
&lt;p&gt;然后还去星界刷了半天的凋零骷髅，只为了能获得更多的下界之星&lt;/p&gt;
&lt;p&gt;所以你看，我做的都是无用功，因为这个问题本质上并没有被解决，资源循环问题是整个成长曲线，架构设计的问题，不是靠增加一个出口就能解决的问题，就像我曾经试图解决生电与经济系统的问题一样，不是说生电有无限的产出，然后加一个无限的消耗，就能解决的&lt;/p&gt;
&lt;p&gt;所以上面的结论还要再扩展，超限附魔在试图解决的是资源的循环问题，而这个问题本质是成长曲线和世界架构的不合理，因此超限附魔本来就不该承载这个功能，它反而会引入你不想要的游戏循环&lt;/p&gt;
&lt;p&gt;你想到一个设计，然后你去实现它，这本身没有错，问题在于，你想到的这个用于解决问题的设计，很有可能解决的不是问题的因，而是问题的果&lt;/p&gt;
&lt;p&gt;重新定义问题比什么都重要&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;我再举一个例子，是服务器的领地系统，这个也是我前两篇文章里讨论过的，具体可以去看 &lt;a class="link" href="https://blog.axiumyu.xyz/post/202609101933" &gt;这篇&lt;/a&gt; ，这是一个典型的“因为解决的问题错位而导致整个解决方案从一开始就没有必要存在“的案例&lt;/p&gt;
&lt;p&gt;我为什么最后得出这个结论，我依然是从需求分析和问题的重新定义开始的，领地插件究竟是什么？它在保护什么？Minecraft 里到底有没有”所有权“的概念？领地插件实际上在做的事是什么？它应该做的事是什么？&lt;/p&gt;
&lt;p&gt;就这样我用一个个问题不断去逼近，探索这个解决方案，这个问题赖以存在的最深的根基，最后你会发现，这样一个看上去默认的解决方案，实际上蕴含了许多你可能完全没有审视过的前提和取舍，而其中有些可能并不是你想要的，或者在你的环境下不满足的&lt;/p&gt;
&lt;p&gt;我当然不是要说领地插件本身就是错误的，我只是说，在我的环境下，我倾向于认为它没有必要存在，而我提出的 ProRegion 插件模型，更是因为没有做好需求分析的结果，大伙应该从我的经历里吸取一点教训就是：绝对不要因为一个功能看上去很优雅很美观很强大就去实现它，所有设计都要围绕需求展开，一个设计的存在合理性如果没有真实需求支撑，那么它就应该被删掉&lt;/p&gt;
&lt;p&gt;这其实也是那个 YAGNI 原则的体现&lt;/p&gt;
&lt;p&gt;&lt;img src="https://blog.axiumyu.xyz/20261004_141730535_p0_15647228409596096391.webp"
	width="1360"
	height="1920"
	loading="lazy"
	
		alt="141730535_p0.jpg"
	
 
	
		class="gallery-image" 
		data-flex-grow="70"
		data-flex-basis="170px"
	
&gt;&lt;/p&gt;
&lt;hr&gt;

 &lt;blockquote&gt;
 &lt;p&gt;&lt;em&gt;3. 原生优先 | Native-First&lt;/em&gt;&lt;/p&gt;

 &lt;/blockquote&gt;
&lt;p&gt;你写的是 Minecraft 插件对吧，那么你知道 Minecraft 有哪些东西吗？&lt;/p&gt;
&lt;p&gt;哦，我说的不是游戏内容，而是游戏机制，以及更重要的，机制的深层理解&lt;/p&gt;
&lt;p&gt;为什么我这么说，因为 Minecraft 本质上也是一个巨大的代码库，我们所做的只不过是在这个代码库上加一点小小的补丁而已，从这个角度来理解，无论是 mod，插件，或者服务端 patch，都是一样的“补丁”，有的补丁在运行前注入，有的补丁在运行时注入，有的来自于别人，有的来自于你自己&lt;/p&gt;
&lt;p&gt;因此，弄明白这个我们一直在往上打补丁的庞然大物到底是什么，非常重要&lt;/p&gt;
&lt;p&gt;为什么？因为你如果深挖下去就会发现，有时候很多机制，mojang 已经替你做了大半了&lt;/p&gt;
&lt;p&gt;你会可能觉得，“插件开发嘛，就是调 API”，于是在 API 之上再搭一层自己的实现。看起来能跑，实际上是在用代码重新发明原版已经做好的东西，而且做得更差、更脆弱、更不兼容。&lt;/p&gt;
&lt;p&gt;举例子就是箱子锁，传统的箱子锁是怎样的，我想大家都清楚，用牌子点箱子，然后生成一个箱子锁，第一行写什么比如&lt;code&gt;[Private]&lt;/code&gt; ，然后第二行写自己 id，后面再写朋友的 id&lt;/p&gt;
&lt;p&gt;这样做有问题吗，没有的兄弟，一点问题都没有，所有功能都正常，能成功保护玩家的物品&lt;/p&gt;
&lt;p&gt;但是，仅仅这样，就够了吗？&lt;/p&gt;
&lt;p&gt;或者说，你觉得这样就满足了吗？&lt;/p&gt;
&lt;p&gt;嗯，你可能会说，我觉得够了，我不需要更好的方案，啊那行，不过我还是希望你能看看，我是如何做的，明明市面上已经有那么多箱子锁插件了，我为什么还要重新做一个&lt;/p&gt;
&lt;p&gt;箱子锁这个东西原版其实早就有了，所有的容器类方块实体都拥有一个标签：lock，这个标签是原版的 NBT，它的功能就是箱子锁&lt;/p&gt;
&lt;p&gt;但是别急，我还没说完，这个 lock 的功能是不完整的，它现在只有一个功能：只允许手持与该字段的物品谓词匹配的物品打开，它的功能仅仅只有这一项&lt;/p&gt;
&lt;p&gt;因此，如何从这一项功能中，推出完整的箱子锁功能呢？&lt;/p&gt;
&lt;p&gt;首先一点是你很容易想到的：环境保护，原版的箱子锁，被挖开，被炸掉，就没用了，原版不保护这个，因为它本身更像是为了地图制作者在制作冒险地图时准备的功能，因此这是插件需要补齐的第一个功能，实现起来也很简单，如果箱子有锁，禁止破坏，禁止爆炸就行了&lt;/p&gt;
&lt;p&gt;有了一个可靠的锁还不够，还需要有钥匙才行，而箱子锁识别的谓词，不能做到因人而异，因此因人而异的特性必须要做到钥匙上，换句话说钥匙本身必须是天然防篡改，不可复制的&lt;/p&gt;
&lt;p&gt;原版里没有有符合这两个特点的物品呢？&lt;/p&gt;
&lt;p&gt;有的，答案就是：成书&lt;/p&gt;
&lt;p&gt;成书的署名天然是不可更改的，因此可以用来做身份标识，而成书的标题则可以作为要是标签，用来区分不同钥匙&lt;/p&gt;
&lt;p&gt;成书还有一个更妙的点：复制等级（generation），这意味着你如果把它加入钥匙体系中，你甚至就天然拥有了一个密钥分发管理机制，如果我把一个箱子的 lock 设置为匹配 generation 小于 2，那么你可以自己拥有一份复制等级 0 的钥匙，也可以再复制一份密钥等级为 1 的钥匙，然后发给你信任的朋友，朋友拿到之后，可以用它来开你的箱子，但他们绝不可能将其再复制一份发给别人，因为他们再复制一次，密钥等级会变成 2，这就无法开锁了&lt;/p&gt;
&lt;p&gt;这样你不需要写任何一行代码，直接白嫖原版的能力，就获得了一套密钥分层管理机制&lt;/p&gt;
&lt;p&gt;你需要做的仅仅是，为玩家提供一个创建/移除，升级/降级容器锁的机制即可，而这可以通过简单的命令或者物理交互完成&lt;/p&gt;
&lt;p&gt;于是整个插件你只需要写几个监听器用于保护上锁的箱子即可，再写两个命令或者监听器用于管理锁，完全不需要扫描箱子周围的方块，不需要读取箱子的任何数据用来实现解锁，不需要写一套密钥分发机制&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;当然这一套机制不是没有问题的，它依赖于玩家的游戏 id 保持一致，不过我认为这在我的场景下是完全可以接受的前提，我相信还是有相当的插件依然在使用玩家名称作为依赖而非 uuid，还要考虑到外置登陆等情况，有时候玩家 id 保持不可变反而是更好的选择&lt;/p&gt;
&lt;p&gt;你可能会觉得这样的操作不够直观，比如说无法凭外观分辨出箱子是否有锁，但我觉得这不是在箱子上加一个木牌来显示是否有锁的理由，如果你认为这不够直观，应该想办法从外观的角度来解决，比如加材质；要知道告示牌和箱子本身都是方块实体，并非纯粹方块，一两个上锁的箱子，你可能看不出常规实现方式和我的方式的区别，但是如果是几百个甚至上千个，区别就明显了&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;箱子锁的例子可能你会觉得没什么，但下面这个例子我估计你如果不这么做就根本没办法达到目的：给乐魂加上放置物品和容器的能力的插件，功能很简单，类似于机械动力/瓦尔基里等 mod 的功能，让一个移动中的实体拥有方块交互能力&lt;/p&gt;
&lt;p&gt;我最初的设计是，让一个方块展示实体直接以某种方式挂在实体身上，骑乘，tp 都不行，然后想到了用材质包直接改快乐恶魂材质，这是最直接的办法&lt;/p&gt;
&lt;p&gt;然后又想到，那改材质得把所有的方块都画一遍吗，不太可能吧&lt;/p&gt;
&lt;p&gt;所以最后想到，应该用客户端 mod 改渲染，直接渲染一个方块模型在实体的上方，然后就这么做了&lt;/p&gt;
&lt;p&gt;但效果不太好，我不会写 mod 所以让 ai 写的，改天再改改，这个不重要，关键还是在怎么让实体存放，并且真实能打开箱子界面&lt;/p&gt;
&lt;p&gt;我首先想到了用快乐恶魂未使用的四个装备栏来存放物品，然后打开时就打开这个物品的方块界面&lt;/p&gt;
&lt;p&gt;但是我准备深入时发现，Paper API 并没有提供操作物品的方块实体数据的方式，我本来想自己去 patch 服务端来实现，但是我再一想，DataComponentType 是对原版数据组件的封装，而别的类型之所以好封装，是因为它们结构固定，而不管是物品谓词，方块实体数据，都是变化很大的，不确定物品的类型根本没法确定结构&lt;/p&gt;
&lt;p&gt;所以这条路也不通，那怎么办呢&lt;/p&gt;
&lt;p&gt;你有没有想到，这个插件的功能，就是让玩家打开一个时刻在工作的，绑定到实体的方块界面？那么，这个方块，有没有可能是一个真实存在于某处的方块？&lt;/p&gt;
&lt;p&gt;如果你想到了这一点，那么事情就变得很简单了，我只要让实体绑定几个具体的方块，然后让玩家交互时打开那个方块的界面就好了&lt;/p&gt;
&lt;p&gt;那么这些方块放在哪里？三个维度都会被玩家访问到甚至修改，最简单的办法就是创建一个空世界，然后划定一个区块，打开区块里面的方块即可&lt;/p&gt;
&lt;p&gt;我就是这么做的，把所有的方块都存在一个后端世界的 0,0 区块里，对我来说这个区块已经足够了，况且世界高度也扩展了很多，从 -255 到 1023，足够放东西了&lt;/p&gt;
&lt;p&gt;这样你几乎白嫖了原版的一切，状态管理，持久化，唯一要自己做的就是记录哪些格子被使用了，绑定关系都可以放在装备的物品里面&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;当然，Native-First 原则并不意味着是教条式地遵循原版的东西，它应该描述为“先看原版有没有提供类似的能力，并评价它是否足够好，能否为自己节省时间和精力，并且自己能否接受这个方案的缺点和约束，如果可以，就用它，如果不够好，就在理解它的基础上，用更好的方案替代&lt;/p&gt;
&lt;p&gt;举例子就是书与笔，在 1216 之前书与笔就是最好的”玩家输出大段文本“的原生选择，但在 1216 之后，这个选择已经变成了 Dialog，这时候继续坚持用书与笔，反而显得不那么好了&lt;/p&gt;
&lt;hr&gt;

 &lt;blockquote&gt;
 &lt;p&gt;&lt;em&gt;3. 前后端分离&lt;/em&gt;&lt;/p&gt;

 &lt;/blockquote&gt;
&lt;p&gt;哎你看这个人又在扯了，minecraft 哪来的前后端，不都是 C/S 吗&lt;/p&gt;
&lt;p&gt;这句话是“&lt;strong&gt;行为与外观解耦&lt;/strong&gt;“的另一种更宽泛的说法，为什么我们需要行为与外观解耦，我举一个非常经典的例子：Lore&lt;/p&gt;
&lt;p&gt;&lt;img src="https://blog.axiumyu.xyz/20261004_Pasted%20image%2020261004130723_6342190270101494970.webp"
	width="1906"
	height="996"
	loading="lazy"
	
		alt="misode.io，非常好用的数据包生成器网站"
	
 
	
		class="gallery-image" 
		data-flex-grow="191"
		data-flex-basis="459px"
	
&gt;&lt;/p&gt;
&lt;p&gt;这些附魔在我服务器的作用等价于 Lore，但我为什么不直接写 Lore，而是要写附魔呢？&lt;/p&gt;
&lt;p&gt;Lore 的本质是什么？它是一个 &lt;code&gt;List&amp;lt;Component&amp;gt;&lt;/code&gt;，是有序的，索引就是下标&lt;/p&gt;
&lt;p&gt;附魔的本质是什么？它是一个 &lt;code&gt;Map&amp;lt;NamespacedKey, Int&amp;gt;&lt;/code&gt; ，是无序的，索引是 &lt;code&gt;NamespacedKey&lt;/code&gt;，&lt;/p&gt;
&lt;p&gt;你会很容易发现，附魔的名字是可以被资源包随意更改的，资源包只需要加一行翻译键，就可以更改一个物品上显示的附魔名称&lt;/p&gt;
&lt;p&gt;但 Lore 做不到这一点，Lore 的显示就是底层数据，而且更糟糕的是，Lore 的显示并非“所见即所得”，一行在玩家看来完全一致的 Lore，其底层的 AST 可能是完全不同的，这会在跨版本等要求物品一致性的场合下带来很多问题&lt;/p&gt;
&lt;p&gt;而附魔可以，因为它的底层是一个 NamspacedKey，纯字符串，没有那些看不见的东西，一个东西是 minecraft:sharpness，它就永远是这个值，不会因为版本升级而变成什么别的，而附魔显示成什么，则是玩家的资源包决定的，原版默认资源包在中文下会显示成“锋利”，但你也可以通过强制下发资源包，让它显示别的值，这在玩家看来就是一次”下载资源包 - 发现物品变了“的”物品热重载“，而即使它看上去变了，底层依然是一致的，它甚至可以是因人而异的&lt;/p&gt;
&lt;p&gt;那么我为什么能用附魔来替代 Lore 呢，还有一个重要的原因是，附魔的显示依然是一个 Component，而且它和 Lore 的位置是紧邻的，一个只有一行 Lore 的物品，和一个只有一个附魔的物品，看上去是几乎一致的，如果不加某些客户端 mod，看上去就是完全一致的&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;然后是 Lore 的另一个问题：更新&lt;/p&gt;
&lt;p&gt;插件如果要修改 Lore 怎么做？只能把 lore 整个拿下来，然后替换其中的一行或者几行，或者增减几行，然后整个 set 回去，如果有多个插件都要修改里面的不同内容怎么办？一个插件如果修改了再放回去，并发问题怎么处理？数据丢了怎么办？怎么识别哪几行是自己添加的？&lt;/p&gt;
&lt;p&gt;这些都是问题，Lore 的 key 是下标整数，而下标是可变的，今天你的 lore 在第一行，明天可能就在第二行了，你怎么修改，你是不是只能通过逐行遍历来实现，每一行都是一个 Component 而不是 String，你还得&lt;code&gt;toPlainText()&lt;/code&gt;，然后再用前缀匹配或者什么别的方式，改起来很脆弱&lt;/p&gt;
&lt;p&gt;但是呢，附魔没有这些问题，你可以直接向物品增加或者删除一个附魔，不需要知道它有没有其他的附魔，你放进去一个 minecraft:sharpness 的附魔，不管过多久，它都还是那个名字，不会因为别的插件增减了几个附魔就变成别的值（当然，消失了还是有可能的，但是绝不会出现今天是 minecraft:sharpness，明天就变成 minecraft:protection）&lt;/p&gt;
&lt;p&gt;这两个问题导致了在大多数场景下，附魔其实是比 Lore 可靠得多的选择&lt;/p&gt;
&lt;p&gt;但还有两个东西才是决定性的：原版的能力边界和 paper api 的边界&lt;/p&gt;
&lt;p&gt;我们都知道 Minecraft 在 1.21 添加了数据驱动魔咒，而 paper api 很快也在 Bootstrap 里添加了注册附魔的能力，所以附魔除了通过数据包实现，也可以不依赖数据包，在 bootstrap 里添加（我个人还是习惯用数据包）&lt;/p&gt;
&lt;p&gt;附魔可以选择生效的物品，如果你担心这样的附魔会被玩家意外获取，那么只需要将生效的物品换成一个玩家永远不可能获得的物品即可，比如命令方块，基岩，屏障等等，原版框架里还是会有很多的，要么再把 cost 提到 999，实在担心就把所有这些占位附魔像我一样都集中到一两个命名空间内，然后用插件统一监听并取消事件，这些都是非常廉价的解决手段，比在插件里写一大堆判断 Lore 的逻辑简单得多&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;还有一个附加的好处是，它对数值更新天然存在优势&lt;/p&gt;
&lt;p&gt;附魔除了显示 NamespacedKey 对应的 Component，还会显示等级，这是一个 0-255 的整数，那么你很容易想到，如果我希望做一个“攻击力：5”的占位附魔，而这个攻击力数值一定是整数且在 0-255 之间，那么你想要给物品增加攻击力时，你只需要简单增加这个附魔的等级即可&lt;/p&gt;
&lt;p&gt;你还可以发挥出更多的玩法，比如玩家在磨石上祛除附魔时，你可以监听这个事件，比如去除某种状态，标记，甚至当玩家没有某种权限/能力时拒绝操作，或者不监听，直接将附魔的有无作为唯一状态来源，这些都是 Lore 做起来不方便的&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;另外一个非常冷门的点是，（JEI）mod 兼容性&lt;/p&gt;
&lt;p&gt;这需要搭配上一条原则来使用，我们都知道一个数据组件叫 item_model，作用是选择物品的外观，这时候你可以做到的是：让一个绿宝石的外观变成纸，或者反过来等等&lt;/p&gt;
&lt;p&gt;那么你一不小心把这些特殊物品的 Material 设置为了附魔书，然后又一不小心给他的 stored_enchantments 里加上了唯一的占位 lore 附魔，又一不小心将附魔的命名空间设置为了你的服务器名字，而玩家一不小心关掉了高级提示框（F3+H，显示物品 id）&lt;/p&gt;
&lt;p&gt;而 JEI 对附魔书的处理是特殊的，JEI 通常会在物品的最下方显示物品的 mod，如果是原版则只显示“Minecraft”，但在附魔书上，JEI 显示的是附魔的命名空间（因为附魔书大家都知道是原版的嘛，这肯定不用说，要看的应该是上面的附魔来自于哪个 mod&lt;/p&gt;
&lt;p&gt;但这有一个限制是，附魔书上必须只有唯一的附魔（据我所知，262 似乎没有这个限制，具体我也没试过怎么回事）&lt;/p&gt;
&lt;p&gt;那么你将会得到一个足以以假乱真的 mod 物品：&lt;/p&gt;
&lt;p&gt;&lt;img src="https://blog.axiumyu.xyz/20261004_Pasted%20image%2020261004140515_590211871547926169.webp"
	width="484"
	height="244"
	loading="lazy"
	
		alt="物品材质使用了 Waystone mod 的传送石"
	
 
	
		class="gallery-image" 
		data-flex-grow="198"
		data-flex-basis="476px"
	
&gt;&lt;/p&gt;
&lt;p&gt;下面没有显示“Minecraft”，而是 waystone&lt;/p&gt;
&lt;p&gt;就算不使用自定义材质也可以：&lt;/p&gt;
&lt;p&gt;&lt;img src="https://blog.axiumyu.xyz/20261004_Pasted%20image%2020261004140952_7163225299035129073.webp"
	width="174"
	height="100"
	loading="lazy"
	
		alt="Pasted image 20261004140952.png"
	
 
	
		class="gallery-image" 
		data-flex-grow="174"
		data-flex-basis="417px"
	
&gt;&lt;/p&gt;
&lt;p&gt;当然这个很业余，没啥特别的好处也&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;这个方案也不是没有坏处的，坏处也显而易见：附魔是注册表，不能热注册，所有需要动态生成/无法事先获得所有状态空间/状态空间极其庞大的文本，都无法使用这个方案&lt;/p&gt;
&lt;p&gt;而数值更新则是：仅能显示 0-255 的整数，如果要负数，小数，超过范围的整数，都无法使用这个方案，并且在超过 10 的数值，需要玩家使用客户端 mod 或者资源包来替换默认的 enchantment.level.xx 显示&lt;/p&gt;
&lt;p&gt;这些缺点我也都清楚，但他们在我的场景下几乎不是问题，如果你要用这个方案，你也一样需要接受这几点权衡&lt;/p&gt;
&lt;p&gt;&lt;img src="https://blog.axiumyu.xyz/20261004_150339860_p0_10482416665481586736.webp"
	width="1272"
	height="1920"
	loading="lazy"
	
		alt="150339860_p0.jpg"
	
 
	
		class="gallery-image" 
		data-flex-grow="66"
		data-flex-basis="159px"
	
&gt;&lt;/p&gt;
&lt;hr&gt;

 &lt;blockquote&gt;
 &lt;p&gt;&lt;em&gt;4. 数据与消费解耦&lt;/em&gt;&lt;/p&gt;

 &lt;/blockquote&gt;
&lt;p&gt;称号插件是最直接的例子，传统的称号插件我想大多数人应该都用过，但是我还是要自己写&lt;/p&gt;
&lt;p&gt;这个插件与其说是“称号插件”，不如说只是一个“文本提供器”&lt;/p&gt;
&lt;p&gt;这个插件只做一件事：提供一个 placeholder api 变量，插件注册一个 papi expansion，将%ptxt_selected%解析为玩家当前选择的文本，并提供了管理员命令来增删改查称号和玩家授予关系，还有一个玩家命令用于玩家切换选择的文本，除此之外，什么都不做&lt;/p&gt;
&lt;p&gt;它完全不碰任何“显示”，这个文本显示在哪里？我不管，我只管提供文本，你显示在名字前面，它可能就是一个称号，显示在剧情文本中，那可能就是一个剧情对话&lt;/p&gt;
&lt;p&gt;因为它不负责显示，所以它几乎兼容所有插件，不会与任何插件冲突&lt;/p&gt;
&lt;p&gt;这个其实类似于之前说的“前后端分离”，所以具体我就不展开了&lt;/p&gt;
&lt;hr&gt;

 &lt;blockquote&gt;
 &lt;p&gt;&lt;em&gt;5. 做减法比做加法容易&lt;/em&gt;&lt;/p&gt;

 &lt;/blockquote&gt;
&lt;p&gt;我知道有很多人不喜欢幻翼，也有很多人不喜欢下界合金模板，甚至不喜欢下界合金&lt;/p&gt;
&lt;p&gt;但是，你就为了这一点停在没有幻翼，没有下界合金的版本吗？&lt;/p&gt;
&lt;p&gt;那你想要 1.20 的樱花树，甚至 26.2 的硫方怪怎么办？难道自己在旧版本上重新搭一套自定义方块/物品，同时实现 1.20 的樱花树所有相关逻辑吗？&lt;/p&gt;
&lt;p&gt;所以很显然，你要做的是从原版里移除那些你不想要的东西，比如模板，比如幻翼，这些都比自己重新实现新版本里想要的东西来得简单得多&lt;/p&gt;
&lt;p&gt;而且，至少从 1.21.11 附近开始，minecraft 的新版本是越来越优化的，至少未来的发展方向是这样，我承认有些不上不下的版本确实表现很糟糕，也有一些破坏性更新，其实我自己也有时候被这些搞的很烦，妈的改什么世界时钟改得乱七八糟 time 命令都用不了了，插件也要重写&lt;/p&gt;
&lt;p&gt;但是我会因为这个不用 26.2 吗，不会的，那是因噎废食，新版本更新的内容是一方面，更多的还是机制，代码逻辑，bug 修复这类幕后的，难以被拿到更新内容里单独讲一个章节的东西，而这些带来的作用，往往也是难以察觉的，我们平时玩 mod 服，玩单人档，都会加上不少优化 mod，可能感受不到，但是一到插件服，基于原版，这种感受就会很明显了，你要始终记得，你所写的插件是为了原版而写的，尽管 paper 及其下游分支都会加上不少的优化 patch，但是你不能完全依赖这些，因为这不是你可以控制的&lt;/p&gt;
&lt;p&gt;这条其实是 Native-First 的延伸，原版的能力只有在更新的版本才能得到最大化扩展&lt;/p&gt;
&lt;p&gt;举例子就是之前的称号插件，这个插件在 1219 之前也可以做，但我为什么没有在那时候用？因为那时候的文本组件没有 sprite 这一新功能，无法显示图片，要么只能用各种模拟手段去逼近，因此虽然能用，但能力受限，而在 12111 或者 262，这个插件的功能也许不会比常见的称号插件差&lt;/p&gt;
&lt;p&gt;&lt;img src="https://blog.axiumyu.xyz/20261004_120206567_p0_17577307799235507550.webp"
	width="1920"
	height="1270"
	loading="lazy"
	
		alt="120206567_p0.jpg"
	
 
	
		class="gallery-image" 
		data-flex-grow="151"
		data-flex-basis="362px"
	
&gt;&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;这五条原则是我在写了一年半插件之后总结出来的，虽然大多数其实是最近半年里才提出的，但是这并不意味着前面的那一年就什么收获都没有，踩过的坑，重写的插件，废弃的旧代码，都是在为了这几条原则铺路&lt;/p&gt;
&lt;p&gt;其实有很多更加隐蔽的原则是很难被直接像这样写成一段话的，但有一点可以补充的是，插件开发者一定要有玩家和服主/管理的双重视角，有些东西在开发者看来是好的，但使用者却感受不到，或者作为开发者觉得是理所当然的，但使用者却不这么觉得，这种视角的错位往往会造成更加隐蔽，难以排查的问题，因此我始终是强调多用插件使用者的视角去做设计&lt;/p&gt;
&lt;p&gt;当然我知道你可能并不觉得这几条原则是正确的，我也不会说这几条原则是绝对正确的，没有绝对正确的东西，只有适合你的东西，这篇文章也希望读者能批判性地看待，也不要因为认同我的观点而在所有地方都强行使用，我嘛，只不过是一个随处可见的普通人而已，甚至不觉得自己算一个合格的开发者，毕竟我不会写数据库，不会搞那些什么缓存，连接池之类的，我也不想维护那些玩意；相信读者里肯定有不少的高级架构师和工程师能写出比我好得多的代码，所以文章里就不放具体的代码片段来献丑了，要是觉得我这个普通人说的一些观点对你还算有帮助的话，就关注一下我的网站或者公众号吧，感谢你能读到这里&lt;/p&gt;</description></item></channel></rss>