Minecraft Sponge 服务端 Universal Market 插件踩坑记录及修复汉化版本下载

2019-06-28

Minecraft 原版玩腻了~~(龙终于打过了)~~,想玩 Minecraft MOD ,于是乎我装了工业 2 模组。正想着联机,从 mcbbs 里找了半天,感觉靠谱的就没有几个。自己玩又太孤独,那就来搭建一个服吧。这是前言,具体细节不多讲了,总之坑很多,不过好在有前辈们的教程(比如 http://www.mcbbs.net/thread-786074-1-1.html ),路走的还算顺利。本着为人民服务的精神,在这里选取一个坑,总结一下过程,来帮助后来的小伙伴们。

本文迁移自老博客,原始链接为 https://lookas2001.com/minecraft-sponge-%e6%9c%8d%e5%8a%a1%e7%ab%af-universal-market-%e6%8f%92%e4%bb%b6%e8%b8%a9%e5%9d%91%e8%ae%b0%e5%bd%95%e5%8f%8a%e4%bf%ae%e5%a4%8d%e6%b1%89%e5%8c%96%e7%89%88%e6%9c%ac%e4%b8%8b%e8%bd%bd/

简要介绍

Universal Market 是一个寄售市场,有着类似箱子的界面。

MCBBS 转载贴: http://www.mcbbs.net/thread-792152-1-1.html
Github 原始项目: https://github.com/Xwaffle1/UniversalMarket

下载

20190628 更新:修复了热加载不管用的问题,修复了空手将空气添加到市场的bug

UniversalMarket-1.12.2-v1.3-Bug_fix_and_chinese_translation_by_yys_and_Tollainmear_and_lookas2001.jar

UniversalMarket-master-Bug_fix_and_chinese_translation-source.7z

由于这个项目的各种提示文本是内置在代码里的,所以汉化需要重新编译,上面有项目源代码文件,如果不放心的话,可以按照下方的编译过程自己手动编译。汉化部分采用 Tollainmear 提供的翻译。

出现问题

我搭建的服务器版本为 Minecraft 1.12.2 , Forge 1.12.2-14.23.5.2825 , SpongeForge 1.12.2-2825-7.1.6 。在安装了 1.3 版本的 Universal Market 插件( https://github.com/Xwaffle1/UniversalMarket/releases/tag/1.3 )后发现无法正常进入商店,控制台报错如下:

[xx:xx:xx] [Server thread/ERROR] [Sponge]: Error occurred while executing command 'um' for source EntityPlayerMP['lookas2001'/248, l='world', x=302.00, y=64.00, z=229.00]: There's no NBT Data set in the provided container
org.spongepowered.api.data.persistence.InvalidDataException: There's no NBT Data set in the provided container
    at net.minecraft.item.ItemStack.setRawData(ItemStack.java:2534) ~[aip.class:?]
    at com.xwaffle.universalmarket.utils.ItemBuilder.<init>(ItemBuilder.java:38) ~[ItemBuilder.class:?]
    at com.xwaffle.universalmarket.utils.ItemBuilder.<init>(ItemBuilder.java:31) ~[ItemBuilder.class:?]
    at com.xwaffle.universalmarket.market.Market.openMarket(Market.java:247) ~[Market.class:?]
    at com.xwaffle.universalmarket.market.Market.openMarket(Market.java:167) ~[Market.class:?]
    at com.xwaffle.universalmarket.commands.MarketCommand.process(MarketCommand.java:45) ~[MarketCommand.class:?]
    at org.spongepowered.api.command.dispatcher.SimpleDispatcher.process(SimpleDispatcher.java:340) ~[SimpleDispatcher.class:1.12.2-2825-7.1.6]
    at org.spongepowered.common.command.SpongeCommandManager.process(SpongeCommandManager.java:337) [SpongeCommandManager.class:1.12.2-2825-7.1.6]
    at net.minecraft.command.ServerCommandManager.func_71556_a(SourceFile:1156) [dh.class:?]
    at net.minecraft.network.NetHandlerPlayServer.func_147361_d(NetHandlerPlayServer.java:960) [pa.class:?]
    at net.minecraft.network.NetHandlerPlayServer.func_147354_a(NetHandlerPlayServer.java:939) [pa.class:?]
    at net.minecraft.network.play.client.CPacketChatMessage.func_148833_a(SourceFile:37) [la.class:?]
    at net.minecraft.network.play.client.CPacketChatMessage.func_148833_a(SourceFile:9) [la.class:?]
    at org.spongepowered.common.event.tracking.phase.packet.PacketPhaseUtil.onProcessPacket(PacketPhaseUtil.java:193) [PacketPhaseUtil.class:1.12.2-2825-7.1.6]
    at net.minecraft.network.PacketThreadUtil$1.redirect$onProcessPacket$zlk000(SourceFile:539) [hv$1.class:?]
    at net.minecraft.network.PacketThreadUtil$1.run(SourceFile:13) [hv$1.class:?]
    at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:511) [?:1.8.0_74]
    at java.util.concurrent.FutureTask.run(FutureTask.java:266) [?:1.8.0_74]
    at net.minecraft.util.Util.func_181617_a(SourceFile:46) [h.class:?]
    at org.spongepowered.common.SpongeImplHooks.onUtilRunTask(SpongeImplHooks.java:307) [SpongeImplHooks.class:1.12.2-2825-7.1.6]
    at net.minecraft.server.MinecraftServer.redirect$onRun$zjo000(MinecraftServer.java:3970) [MinecraftServer.class:?]
    at net.minecraft.server.MinecraftServer.func_71190_q(MinecraftServer.java:723) [MinecraftServer.class:?]
    at net.minecraft.server.dedicated.DedicatedServer.func_71190_q(DedicatedServer.java:397) [nz.class:?]
    at net.minecraft.server.MinecraftServer.func_71217_p(MinecraftServer.java:668) [MinecraftServer.class:?]
    at net.minecraft.server.MinecraftServer.run(MinecraftServer.java:526) [MinecraftServer.class:?]
    at java.lang.Thread.run(Thread.java:745) [?:1.8.0_74]

这时候我有点慌了,因为我之前没有系统的接触过 Java ,只在研究锐捷网络 ESS/SMP 产品授权验证以及 Moegirl Android App 的时候稍微有所了解。

定位和解决

直接上搜索引擎,没找到...

由于这个项目是开源的~~(Open Source 大法好!)~~,我们可以将代码下载下来慢慢研究。

后来又到原始项目里找 issue 找到了这个 https://github.com/Xwaffle1/UniversalMarket/issues/20 ,通过一些方式联系上了一个已经对前面一个版本的 jar 打完补丁的 dalao Alone,感谢其作品在我没有思路时候对我的启发。

好在有 trace ,我们可以看到最后一个关于这个插件的错误发生在 com.xwaffle.universalmarket.utils.ItemBuilder.<init>(ItemBuilder.java:38) ~[ItemBuilder.class:?] 这里:

我们把 setRawData 这个函数扔到 Sponge Forum 上搜索一下 https://forums.spongepowered.org/t/set-get-modify-subid-of-itemstack/19048/9 ,发现这个函数与在物品上设置一些属性有关,是一个比较 hacky 的做法。

将这段代码对应的函数在项目全局搜索一下:

发现这段代码只跟 Market.java 这个文件相关,打开,查找调用地方。

我们发现实际通过 meta 变量传入设置了 UnsafeDamage (损坏值)的调用点只在这里:

加载了好使的插件后,可以发现这个是支付确认界面的确定按钮。

故推测,这可能是作者想要做的一个类似进度条相关的功能,但是最后可能因为各种原因最后咕咕咕了(人类的本质)。

那就直接把这一行以及相关的两行(即出现 bug 这一行的上面两行)移除掉试一试。

拦路虎——编译

Minecraft 使用 Java 8 作为其运行环境,相应的,其周围 Mod 开发都是在 Java 8 基础上进行的,推荐使用 Java 8 来进行接下来的工作。

我本地用的电脑是 MacBook 开发环境为 MacOS,相对 Windows 系统,在命令行上更加强大一些,周围配套设施也多一些。个人开发的话,平常使用 brew 作为包管理,安装软件快捷迅速。但是 Brew 默认不提供 Java 8 (大概是过时了?),而去 Oracle 官网上下载又是不可能的(下载居然还需要登录),在 StackOverflow 上有这么一个回答 https://stackoverflow.com/a/55774255 ,解决了 Java 8 安装的问题。

看到这个项目使用了 gradle 作为其依赖管理(或者讲构建工具?),依照这我之前用 composer ,npm 的经验,我寻思着应该需要用 brew 安装一下。事实上是不需要的,安装了反而多余。brew 上的 gradle 版本太高,并且项目本身提供了一个包管理命令,即 gradlew 。运行一下会在项目目录以及用户目录生成相应的文件。

于是乎,./gradlew 一下,在下载完必要的文件后,给出了一个提示,按照这个提示,我应该运行 ./gradlew tasks。运行之,找到好像是构建的选项 ./gradlew build 。运行之,傻眼,提示错误:

:compileJava
/xxxxxx/UniversalMarket-master/build/sources/main/java/com/xwaffle/universalmarket/market/Market.java:30: 错误: 程序包org.spongepowered.common.item.inventory.adapter.impl.slots不存在
import org.spongepowered.common.item.inventory.adapter.impl.slots.SlotAdapter;
                                                                 ^
/xxxxxx/UniversalMarket-master/build/sources/main/java/com/xwaffle/universalmarket/market/Market.java:31: 错误: 程序包org.spongepowered.common.item.inventory.query.operation不存在
import org.spongepowered.common.item.inventory.query.operation.InventoryPropertyQueryOperation;
                                                              ^
/xxxxxx/UniversalMarket-master/build/sources/main/java/com/xwaffle/universalmarket/market/Market.java:32: 错误: 程序包org.spongepowered.common.item.inventory.util不存在
import org.spongepowered.common.item.inventory.util.ItemStackUtil;
                                                   ^
/xxxxxx/UniversalMarket-master/build/sources/main/java/com/xwaffle/universalmarket/market/MarketItem.java:10: 错误: 程序包org.spongepowered.common.item.inventory.util不存在
import org.spongepowered.common.item.inventory.util.ItemStackUtil;
                                                   ^
/xxxxxx/UniversalMarket-master/build/sources/main/java/com/xwaffle/universalmarket/utils/ItemBuilder.java:11: 错误: 程序包org.spongepowered.common.item.inventory.util不存在
import org.spongepowered.common.item.inventory.util.ItemStackUtil;
                                                   ^
注: Writing plugin metadata to file:/xxxxxx/UniversalMarket-master/build/classes/main/mcmod.info
5 个错误
:compileJava FAILED

FAILURE: Build failed with an exception.

* What went wrong:
Execution failed for task ':compileJava'.
> Compilation failed; see the compiler error output for details.

* Try:
Run with --stacktrace option to get the stack trace. Run with --info or --debug option to get more log output.

BUILD FAILED

翻找 Sponge 文档,看到官方给出的例子都是基于 IDE 的,但是由于我的 MacBook 空间不够,最后在另外一台电脑上(Windows)安装了Intellij IDEA Community ,然后使用 IDE 提供的构建按钮,又花费了很长的一段时间下载,最后提示了两个错误 "PKIX path building failed" 和 "unable to find valid certification path to requested target" (在 Windows 上使用命令构建不会出现这个问题,但是仍然会出现上述在 MacOS 已经出现的错误),看来不是 IDE 的问题,返回我的 MacBook 。

在前一个步骤中,我观察到了一个事情,为什么其他引入不会出现问题,而这些引入会出现问题,我发现这些包都是以 org.spongepowered.common.xxxx 开头。之前有查看过 build.gradle 这个文件,明明有通过 compile 'org.spongepowered:spongeapi:7.0.0' 引入 SpongeAPI 。在是 IDE 的问题排查完后,我决定去看看 Java 编译器到底在引入一些什么东西。

由于之前有过 gradle 相关的经历,知道 gradle 会在用户目录生成文件夹 ~/.gradle ,去之,然后搜索之 find . | grep sponge 。搜索结果中有一个 ./caches/modules-2/files-2.1/org.spongepowered/spongeapi/7.0.0/ada1f9981de3459b182ee16d6408173ef33a8943/spongeapi-7.0.0.jar 这一看就是 api 对应的包嘛。

解压之(反编译就不用了):

自闭了

哪来的 common 啊!!

回到 build.gradle 目光下移一行,compile files('libs/spongeforge-1.12.2-2555-7.1.0-BETA-2837.jar') 在结合 SpongeCommon 项目的描述,这作者 tm 直接把 SpongeForge 的发布版本当做依赖了啊!而且源代码还把这个目录给删了!!!结合利用偏门 API 的事情来看(Sponge 不推荐插件作者绕过 Sponge 等行为,这个插件作者直接引入了 net.minecraft.nbt.NBTTagCompound),怪不得这个作者在发布 MOD 没在 Ore 上发啊(可能也跟 Sponge 没提供一些 API 有关)。

下载了现在最新的稳定版本 SpongeForge (spongeforge-1.12.2-2825-7.1.6.jar)(老的不想往后翻了),创建 libs 目录扔进去。然后修改一下 build.gradle 里对应的文件名(保持匹配)。再运行一下 ./gradlew build

小宝贝,终于看见你了:

最后经过测试,工作完美。

维护网站需要一定的开销,如果您认可这篇文章,烦请关闭广告屏蔽器浏览一下广告,谢谢!
加载中...

(。・∀・)ノ゙嗨,欢迎来到 lookas 的小站!

这里是 lookas 记录一些事情的地方,可能不时会有 lookas 的一些神奇的脑洞或是一些不靠谱的想法。

总之多来看看啦。