赞
踩
spigot以其轻量化著称,带来好处的同时,一个直接后果就是不提供像forge那样成熟的开发、调试、构建环境。笔者近日想学习一下MC spigot插件开发,遇到了不少坑,甚是令人烦躁。
配置开发环境时,笔者本打算直接使用Minecraft Development插件,结果搞了几遍都是一个接近空白的文件夹。所幸找到了一篇高质量教程,顺利解决了问题:【Minecraft】Bukkit/Spigot插件开发教程---搭建一个开发环境_WYH2004的博客-CSDN博客_spigot插件开发
(顺带一提,这篇教程的作者在B站发布了一些不错的spigot插件开发视频教程。衷心建议感兴趣的小伙伴去看看(我没收钱!))
对于调试,笔者认识的几个开发者都是把插件构建出来,放到服务器上直接看运行效果,等于没有实时调试,对萌新小白极其不友好。在中文互联网检索如何配置断点调试,答案寥寥;spigot wiki上的方案也解决不了,最后结合stackoverflow上一个回答才搞定。此外,我没有找到解决断点时间过长导致客户端断开连接问题的方案。
笔者总结自己的摸坑教训,感觉不如针对这些问题写一篇中文教程,供各位想要入坑spigot插件开发的MC爱好者参考。
笔者教程使用的JAVA版本为jdk1.8.0_181(JAVA16及以下均可),API使用Spigot 1.16.5(bukkit也行,paper不提供API,不能用),IDEA版本为Community 2021.3,调试服务端使用paper 1.16.5(可以兼容bukkit/spigot,同时加载速度快几倍)。
教程包含以下部分:
1、如何建立spigot服务端实时调试环境
2、一个避免断点调试时间过长导致客户端断开连接的方案
(此处假设你已搭建好了开发环境:构建工件(artifact)可以将插件本体与plugins.yml编译为.jar。不知道怎么做的,可参考上文引用教程)
一共需要做三件事:(1)配置一个调试服务端;(2)配置一个新工件;(3)设置调试选项。
这一步的服务端之后将用于调试插件,并不一定与开发的API依赖库相同,可以跑Bukkit/Spigot插件即可。笔者此处选择了加载速度较快的paper。
我选择在本地部署调试服务端,这样操作完全与开服相同。知道如何开服的小伙伴可以直接跳下一节。想在远程部署调试服务端的,请参考spigot wiki 的相关教程。
首先,我们去paper官网整一个服务端下来。
paper目前只支持1.17+,如果与笔者一样需要下载1.16.5及更早的版本,需要回答问题以表明自己理解paper将不会提供技术支持、论坛讨论;分别选择No 和 It will be closed即可。
之后,把下载的.jar服务端(例如paper-1.16.5.jar)放在一个固定路径、纯英文路径的文件夹里,启动。笔者使用Win 10系统,就直接用批处理脚本启动了(新建文本文档-输入以下内容-另存为:保存类型:所有文件,文件名:随便写.bat)
- java -Xms1G -Xmx1G -jar paper-1.16.5.jar -nogui
- pause
双击运行一次,文件夹里会出现一个eula.txt。打开,将最后一行的false改成true,保存。
再双击运行一次,服务端会自动下载运行环境,下载完成后自动运行。当显示开头为 > 的空行时,运行成功,如下图所示。
运行成功后还需最后一步。关闭服务器,打开server.properties文件,将online-mode=true改为false;否则必须正版账号登陆的客户端才能进入调试服务器。
这一步的目的是,将插件构建到上一步配置好的调试服务器插件目录下。
点击文件(Files)-项目结构(project structure)-工件(artifacts),点击对话框左上角的加号,新建一个JAR工件。
工件名称任意(例如TestPluginDebug),输出布局与构建插件的要求一样:编译输出、plugin.yml(不知道是在说什么的,请继续参阅此篇教程)。
唯一不同的是,输出目录为调试服务器的插件文件夹:例如,我将调试服务器放在Z:\SpigotPlugins\paper-1.16.5-runtime下;那这一步我的输出目录必须为Z:\SpigotPlugins\paper-1.16.5-runtime\plugins。
点击确定,这一步就成功了;也可以构建此工件检查:构建(Build)-构建工件(Build Artifacts)-选择刚才的工件,如果在输出目录找到 TestPluginDebug.jar,则无误。
这一步在IDEA中新建一个调试环境,用以在调试模式下运行(1)中的服务器与(2)中的插件。
首先,点击右上角 添加配置(Add Configurations...),会跳出一个叫运行/调试配置的对话框。点击左上角+号,新建一个JAR应用程序配置。
之后,填入调试环境的属性:
名称:自己取
JAR路径:(1)中服务端路径
虚拟机选项:服务端运行选项,只要两个参数即可:-Xmx2G -Xms2G。如果实际调试中内存不足导致服务端崩溃,可以适当增大这些数字。
程序实参:一般只填一个 nogui
工作目录:与第一行JAR路径相同,但只填到.jar文件父目录。没听懂的看图。
最后,看到界面最下面,有一个“执行前”标签。点击+号-编译Artifacts,选择(2)中配置的工件,一路确定。这一步会自动在启动调试环境前,将插件更新同步到(1)的调试服务器中。
现在万事俱备了!你可以在调试配置选项卡中看到刚设置好的调试环境。选择它,给自己的插件打一个断点,点击右边的小虫子,试一试程序是否如约暂停了。
大功告成!
MC原版服务端、客户端代码中,都有这样的逻辑:如果连续x秒没有收到任何数据包,则认为连接已断开。这意味着我们断点调试超过x秒,客户端就会被强制踢出世界;对于不能接受这一点的小伙伴,此处笔者提供一种解决方法及其思路。
对spigot系列的服务端,默认x=60。spigot很文明地将这个参数暴露在服务端配置文件spigot.yml中:
timeout-time: 60
将其改为6000即可。
对paper服务端,操作一样;毕竟paper是spigot的儿子(狗头)。
客户端x=30。修改这个就比较麻烦,因为原版并没有提供相应接口。笔者转而使用Forge学习背后的机制(使用Forge MDK版本1.16.5-36.2.29):
MC通信通过Netty进行。连接到服务器时,客户端建立一条ChannelPipeline负责处理数据包,其中第一个Pipeline名称为timeout、类型为ReadTimeOutHandler,正负责着30秒无流量时断开连接的工作。
(关于netty.channel.ChannelPipeline的工作机制,请参阅此文。MC相关代码位于net.minecraft.network.NetworkManager.connectToserver方法中,如下:)
- @OnlyIn(Dist.CLIENT)
- public static NetworkManager connectToServer(InetAddress p_181124_0_, int p_181124_1_, boolean p_181124_2_) {
-
- ......
-
- (new Bootstrap()).group(lazyvalue.get()).handler(new ChannelInitializer<Channel>() {
- protected void initChannel(Channel p_initChannel_1_) throws Exception {
- try {
- p_initChannel_1_.config().setOption(ChannelOption.TCP_NODELAY, true);
- } catch (ChannelException channelexception) {
- }
- //以下是生成pipeline的关键代码,注意第一个addLast
- p_initChannel_1_.pipeline().addLast("timeout", new ReadTimeoutHandler(30)).addLast("splitter", new NettyVarint21FrameDecoder()).addLast("decoder", new NettyPacketDecoder(PacketDirection.CLIENTBOUND)).addLast("prepender", new NettyVarint21FrameEncoder()).addLast("encoder", new NettyPacketEncoder(PacketDirection.SERVERBOUND)).addLast("packet_handler", networkmanager);
- }
- }).channel(oclass).connect(p_181124_0_, p_181124_1_).syncUninterruptibly();
- return networkmanager;
- }
怎么办呢?我们只需要把30秒的ReadTimeOutHandler替换成超级延时版即可。
笔者直接使用Forge,Forge Mod的开发环境配置教程网上有很多,大家可以百度学习一下。如果大家不能使用Forge客户端也没关系,最终替换掉旧的ReadTimeOutHandler即可,实现方式都无所谓啦~
Forge很贴心的在客户端连接到服务端时,注入了一个事件:
ClientPlayerNetworkEvent.LoggedInEvent
直接新建一个Forge Mod订阅这一事件,利用其返回的NetworkManager访问channel、替换pipeline。五行代码即可实现:
- @SubscribeEvent
- public void onConnectToServer(ClientPlayerNetworkEvent.LoggedInEvent event)
- {
- event.getNetworkManager().channel().pipeline().replace("timeout","timeout",new ReadTimeoutHandler(6000));
- }
客户端、服务器联合调试时,用装有此mod的Forge客户端进服即可。
参考链接:
【Minecraft】Bukkit/Spigot插件开发教程---搭建一个开发环境_WYH2004的博客-CSDN博客_spigot插件开发
IntelliJ: Debug Your Plugin | SpigotMC - High Performance Minecraft
java - Run Configuration to Debug Bukkit/Minecraft Plugin in IntelliJ IDEA? - Stack Overflow
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。