南柯郡守
本帖最后由 南柯郡守 于 2020-7-22 20:06 编辑


前言

作为一个刚入坑一个月的萌新开发者
这一个月磕磕绊绊遇到了N多的问题
依然有很多没接触到的 没接触过的东西要学习
好在论坛提供了一个专门的编程开发板块供我这样的小白学习

因为是个半吊子 所以在这个帖子中有什么错漏之处还望海涵
更希望各位大佬能不吝赐教
更新日志


2020.7.22:
   更新镜像站信息及吐槽篇
        该部分更新完成时共计23个Bukkit标签
2020.6.13:
   更新镜像站信息
        该部分更新完成时共计23个Bukkit标签
2020.6.13:
   更新技巧篇内容并增加了内容折叠
        该部分更新完成时共计23个Bukkit标签
2020.5.30:
   更新镜像站信息
        该部分更新完成时共计23个Bukkit标签
2020.5.21:
   更新吐槽篇
        该部分更新完成时共计23个Bukkit标签
2020.5.1516:
   更新技巧篇
        该部分更新完成时共计21个Bukkit标签
2020.5.4:
   更新逻辑部分内容(欢迎文明观猴)
        该部分更新完成时共计16个Bukkit标签
2020.5.3:
   开贴并更新准备部分







2021.12 数据,可能有更多内容
前言
---作为一个刚入坑一个月的萌新开发者这一个月磕磕绊绊遇到了N多的问题依然有很多没接触到的 没接触过的东西要学习好在论坛提供了一个专门的编程开发板块供我这样的小白学习---因为是个半吊子 所以在这个帖子中有什么错漏之处还望海涵更希望各位大佬能不吝赐教
更新日志
---
2020.7.22:   更新镜像站信息及吐槽篇  该部分更新完成时共计23个Bukkit标签2020.6.13:   更新镜像站信息  该部分更新完成时共计23个Bukkit标签2020.6.13:   更新技巧篇内容并增加了内容折叠  该部分更新完成时共计23个Bukkit标签2020.5.30:   更新镜像站信息  该部分更新完成时共计23个Bukkit标签2020.5.21:   更新吐槽篇  该部分更新完成时共计23个Bukkit标签2020.5.1516:   更新技巧篇  该部分更新完成时共计21个Bukkit标签2020.5.4:   更新逻辑部分内容(欢迎文明观猴)   该部分更新完成时共计16个Bukkit标签2020.5.3:   开贴并更新准备部分---






关于准备部分


因为个人比较倾向于使用离线开发包(依赖)(Maven和Grade用不来)
(因为这俩东西下载依赖的速度太慢了)
所以我先放一下依赖包的下载方式
(不管你能不能找到我就当你找不到)


这个是我用着非常棒的镜像站
DoveMc镜像站 好像倒闭了
站内贴:DoveMc镜像站 —— |国内下载源|长期维护|文件直链|

新的镜像站
繁星若尘服务端镜像站站内贴:繁星若尘服务端镜像站——公益|简约|快捷|高速|方便|资源全|直链下载


新的镜像站MC Mirror站内贴:MC Mirror镜像站




新的镜像站Lss233's.Mirror();
站内贴:Lss233's.Mirror(); //一个Minecraft开发Maven镜像


他来源就是官网了 不过有些是需要自己构建的网络不好的同学推荐去镜像站下载哦
- Paper|Waterfall|Travertine- Spigot|BungeeCord- CraftBukkit- Minecraft_Server- Sponge- 摘自 DoveMc镜像站


依赖包有了自然也得有JavaDoc
以下是1.7-1.15的JavaDoc建议收藏到浏览器Bukkit 1.7.10-R0.1-SNAPSHOT API
Paper-API 1.12.2-R0.1-SNAPSHOT API
Paper-API 1.13.2-R0.1-SNAPSHOT APIPaper-API 1.14.4-R0.1-SNAPSHOT APIPaper-API 1.15.2-R0.1-SNAPSHOT API
Paper中包含Bukkit的JavaDoc 懂我意思吧- 摘自 我自己浏览器的收藏夹


其他
该部分为本页错漏补充收录各位开发者在帖子中提出的宝贵意见、建议---
Ghost_chu 提出PaperApi存在与Bukkit冲突(或者说重复)的部分 建议大家使用Spigot作为依赖而不是Paper
上面的链接我就不改了 因为Paper这个网页的右上角有个搜索功能还是挺好用的
感谢huanmeng_cn 补全MC Mirror镜像站的站内贴
---






关于逻辑部分

逻辑嘛logic 大家都懂的随便一个循环也是需要逻辑的嘛

其实这部分我也没有什么代码能拿出来给大伙儿乐呵乐呵因为很多错误代码我并没有及时记录所以我只能将我印象相对深刻的几个代码及解决方案放上来给大伙儿参考参考我的解决方案不一定是正确(最佳)解决方案

代码部分

这里有几段给我自己都整乐了的代码



ChatColor在游戏里的识别格式


line = msg;
while(line.contains("§6"))line=msg.substring(0,msg.indexOf("§6"))+ChatColor.GOLD+""+msg.substring(msg.indexOf("§6")+2);
这段代码出现在我自己写的字体颜色替换中因为我不知道§符号是否所有端都通用比如1.7.10的某些端后台就不会解析§所以想替换成代码中的标准格式ChatColor.Color然鹅!这个循环的后果就是服务器线程卡死 (Server tick loop)为啥呢 因为好死不死的ChatColor.GOLD就是§6至少在我测试的1.12.2版本中可以说这两个是等价的所以导致这个while循环成了死循环解决方案
实现方法有点绕了先将msg中的§替换成&然后将上面那个语句中的§全部改为&这样就不会出现死循环了



配置文件获取String问题


Inventory inv= Bukkit.createInventory(null,54, config.gui.getString("inv.title"));

这段代码出现在创建一个Inventory的时候在服务器运行时会抛出Tiele could not be null的错误但是我配置文件里就是有inv.title的值啊然后尝试了
String title= config.gui.getString("inv.title");
Inventory inv= Bukkit.createInventory(null,54, title);



他就可以了!为啥呢我稍微看了下MemorySectiongetString()的源码
是的 我没有看出原因来所以最后我把他归结于getString()滞后(很明显不是这个原因,但我这么理解的)也因此 我很多读取配置文件的部分都要额外声明一个变量



InventoryEvent


Inventory inv= e.getInventory();
if(inv.contains(item)){
    //do something
}

这段代码出现在监听器中 针对的事件是InventoryCloseEvent
问题出在哪里呢?问题在inv中总是找不到item即便我很确定item就在这里面
后来我想通了(该逻辑错误)在触发close事件时,e.getInventory()的这个inv已经被销毁(误)所以获取到的只能是一个空的inv


那么要怎么获取到真正的Inventory呢在API中可以看到有两个方法的返回类型也是Inventorye.getView().getTopInventory(); 获取界面上方的库存e.getView().getBottomInventory(); 获取界面下方的库存


建议以后监听Inventory相关事件时,尽可能的使用这两种方法因为e.getInventory()及方法在高版本中被弃用(甚至直接删除了)


ItemStack的Displayname


ItemMeta meta= item.getItemMeta();
meta.setLocalizedName("绑定XXX");
item.setItemMeta(meta);

这段代码他出现在设置LocalizedName的时候但是按理说LocalizedName是内部名称(误)不应该被展示出来的(实现隐藏绑定信息)(主要还是不会用nms的nbt信息)纠结一番后只能放弃LocalizedName改为lore设置绑定信息直到后来写别的项目时又尝试了一遍


ItemMeta meta= item.getItemMeta();
meta.setDisplayName("大宝剑");
meta.setLocalizedName("绑定XXX");
item.setItemMeta(meta);

他成了:>
后来我理解为
原版物品是没有
DisplayerName
所以当你设置
LocalizedName
内部名称(误)被作为显示名称展示出来


如非必要
还是不要对原版物品使用
setLocalizedName();
(原版物品指他的名称是minecraft.item.dirt这种翻译识别名称的物品)








其他
该部分为本页错漏补充收录各位开发者在帖子中提出的宝贵意见、建议---
---






技巧篇
---害 能有啥技巧就是学了些花里胡哨的东西没得写了就想拿出来 分享充数---这是十分/非常/很不成熟的一篇所写所用都不一定是最佳解决方案懂我意思吧

如何生艹NMS



NMS是啥多的就不说了各种教程贴多多少少都有涉及到这东西要生艹NMS 搞一份反混淆的ForgeSrc很重要因为Bukkit里面的NMS绝大多数方法都是混淆过的没有对照对应版本的ForgeSrc的话 理解难度成几何倍数关系这里有各个版本的、构建好的ForgeSrc 对网络不好、刚接触的开发者有极大帮助


如何找对应的反混淆过的方法呢?以下我举个栗子比如现在要让一只狼实现无性繁殖
Entity wolfweget;
EntityWolf wolf = ((CraftWolf)wolfweget).getHandle();
这里面((CraftWolf)wolfweget).getHandle();
可以直接把Entity(Bukkit通用格式的狼)转换成NMS格式然后就可以直接开艹了
不过方法名不知道咋整呢不急,先去把你导入的依赖包解压然后顺着EntityWolf的包名找到他,用随便一款反编译器打开映入眼帘的应该是这么个东西 这个东西暂时称为混淆类
public class EntityWolfextends EntityTameableAnimal{private static final DataWatcherObject<Float> DATA_HEALTH = DataWatcher.a(EntityWolf.class, DataWatcherRegistry.c);private static final DataWatcherObject<Boolean> bC = DataWatcher.a(EntityWolf.class, DataWatcherRegistry.h);private static final DataWatcherObject<Integer> bD = DataWatcher.a(EntityWolf.class, DataWatcherRegistry.b);private float bE;private float bF;private boolean bG;private boolean bH;private float bI;private float bJ;
public EntityWolf(World world){    super(world);    setSize(0.6F, 0.85F);    setTamed(false);}
那么接下来的问题就很粗了
这TMD bF/bG/bH是啥?方法里面 a/b/c又是啥?


这个时候刚才说到的ForgeSrc包就派上用场了通过对比类名及其所声明的变量可以在ForgeSrc中找到如下这个暂时称为反混淆类
public class EntityWolf
extends EntityTameable
{
    private static final DataParameter<Float> DATA_HEALTH_ID = EntityDataManager.createKey(EntityWolf.class, DataSerializers.FLOAT);
    private static final DataParameter<Boolean> BEGGING = EntityDataManager.createKey(EntityWolf.class, DataSerializers.BOOLEAN);
    private static final DataParameter<Integer> COLLAR_COLOR = EntityDataManager.createKey(EntityWolf.class, DataSerializers.VARINT);
    private float headRotationCourse;
    private float headRotationCourseOld;
    private boolean isWet;
    private boolean isShaking;
    private float timeWolfIsShaking;
    private float prevTimeWolfIsShaking;

public EntityWolf(World worldIn)
{
    super(worldIn);
    setSize(0.6F, 0.85F);
    setTamed(false);
}
比较好的地方在哪里呢就是这个Class名字是一致的 所以很容易找到当然了 大部分其实变动也不大通过翻译Class的名字也能知道大概意思
那么然后我们就可以在这里面找狼生崽儿的方法
public EntityWolf createChild(EntityAgeable ageable)
{
    EntityWolf entitywolf = new EntityWolf(this.world);
    UUID uuid = getOwnerId();
    if (uuid != null)
    {
   entitywolf.setOwnerId(uuid);
   entitywolf.setTamed(true);
    }
    return entitywolf;
}
找到了 这个是生崽儿的方法
然后对照大致代码 去混淆类中找到这个方法
public EntityWolf b(EntityAgeable entityageable)
{
    EntityWolf entitywolf = new EntityWolf(this.world);
    UUID uuid = getOwnerUUID();
    if (uuid != null)
    {
   entitywolf.setOwnerUUID(uuid);
   entitywolf.setTamed(true);
    }
    return entitywolf;
}
那么就很明显了在Bukkit的混淆类中这个EntityWolf的b(entityageable)方法就是生崽儿了所以结合最开始的代码我们只需要
Entity wolfweget;
EntityWolf wolf = ((CraftWolf)wolfweget).getHandle();
wolf.b(wolf);

就可以让狼不需要同房就能产生后代了



Bukkit的主线程与异线程



昨天与人在论坛上讨论了关于异线程注册监听器的必要性突然有点感想所以这一篇可能会比较干 (干货)

首先对于监听器这个大家都熟悉也应该是在插件里接触最多的东西以下的讲解可能会涉及到一些简单的运用主要还是会讲一下异线程注册监听器的可行性和实用性但是由于我对底层不是很熟悉 可能会有错漏所以对一部分地方我只能用举例的方式来讲解
要了解异线程 那么我们要先分清主线程和异线程的概念对于主线程 我们可以理解为一条高速路 所有的程序代码都在这条高速路上飞驰而异线程可以理解为辅路甚至国道 他们在一定程度上是有从属关系的
(正常情况视图,高速路与辅路齐头并进且互不影响)那么什么是线程堵塞呢简单讲就是高速路堵车了为啥高速路会堵车呢?比如有时候或许会因为一个不查 在代码中出现了一个死循环那么在高速路上的表现就是在高速路的某个路段 有一辆车占了车道在旋转漂移转圈圈他这一转圈就把唯一一条路堵住了后面堆了一堆的车要过去 但是这个车在转圈 就导致整条高速路都瘫痪了车队越来越长 高速路的通行负担越来越大然后高速路就不干了(服务器崩了)
(报错视图,高速路发生堵塞,辅路还能走 但是没有卵用)
如果你看明白了这个图 那么恭喜你 你理解能力是真的好因为我感觉我自己讲的还是有点模糊的
当然这个线程的概念并不是这个样子的 以上讲解只能提供一个模糊的概念纸上得来终觉浅 绝知此事要躬行要深入了解一个东西还是要自己实际操作多试写代码这东西不要怕出错相反的 出错才会有进步自己独立解决一个问题会对自己的能力有一个巨大的提升
回到正题上在平常情况下 我们注册监听器一般都是直接在 onEnable()部分
Bukkit.getPluginManager().registerEvents(new SomeListener() ,this);



在正常情况下,监听器是主线程的一部分这也就意味着如果出现某个监听器中的事件 主线程会等待监听器处理比如说服务器产生了一个爆炸事件 主线程现在交给监听器处理


服务器:喂,监听器,这里有个爆炸事件你处理一下,我等你(0.0001ms Later)监听器:可以爆炸 但是不要伤害玩家服务器: ok ,那就是把地炸了,动物杀了,放过玩家(Boommmmmmmmmm


在这里我们可以看到 监听器在处理事件的时候仅耗时0.0001毫秒 对比1秒=1000毫秒 这点耗时几乎就可以忽略不计
那么为什么会出现一个讨论异线程注册的话题呢我一开始是觉得有点不可思议 监听器有必要/需要 去做异线程来注册嘛?同时也拎不清是否异线程注册的监听器最后会不会依然挂在主线程上


那么经过在贴子的讨论呢 我想通了异线程注册的监听器确实是在异线程上处理事件那么就意味着主线程不会等这个监听器处理,而是直接往下走


依旧是上面那个例子 也是我在那个帖子里举的例子
服务器发生爆炸了


服务器:喂,这里有个爆炸事件你处理一下监听器:哦好,我来处理(转身埋头计算)(异线程)服务器:(继续执行)Boommmmmmmmmm... And Player Dead

监听器:嗨,服务器,这个爆炸离这个玩家的家太近了 不要炸了服务器:???


那么我们可以看到 在这个部分为什么这个监听器走了异线程呢因为他计算量大 耗时长(3 days)如果是在主线程 那么服务器会卡住(3 days)


可能吗?不现实吧所以这部分的监听器运用了异线程 这样就不会导致服务器崩溃了
所以监听器使用异线程注册时可以实现并且有意义的但为什么很少用到这个操作呢因为没必要







就目的来说 平常时候咱们监听事件是要对游戏做出修改比如玩家玩家交互事件当玩家手上拿着某个物品右键的时候插件监听到这个事件 然后以玩家为主体释放一个技能或者其他处理这个时候是可以直接在主线程上操作原因有以下几点:1. 不需要大量的计算,耗时短2. 不需要频繁的进行IO操作最重要的一点. 不会造成主线程卡死(服务器崩溃)





其他
该部分为本页错漏补充收录各位开发者在帖子中提出的宝贵意见、建议---
---




吐槽篇
---这一篇我单纯用来吐槽用可看可不看看了也没用因为这里的问题都没有得到解决---怎么没解决删了不就解决了主要是这东西也不重要用别的方法就代替过去了但还是希望能解决了还请各位大佬能给我顺手支个招

正文
---版本特性这种东西基本上是个无法逾越的鸿沟反正我是不会整了---刚刚就遇到一个新特性(已解决)
addItem(Material.AIR);

这就是个很常用的空气占位
但是!到了1.15.2里面
获取Type的时候 我的这个AIR
他就变成了LEGACY_AIR
咋滴?还给我整过期了?
过期的空气呗?
?????
解决方案:

在plugin.yml里添加一行api-version: 1.13






其他
该部分为本页解决方案收录各位开发者在帖子中给出的宝贵意见及解决方案---
感谢William_Shi 针对过期物品提出的解决方案
---




鬼畜畜
用PaperAPI才是真的踩雷,Paper的API设计很失败,PaperAPI的内容和BukkitAPI混在一起,例如ItemStack#getLores,即使是熟练编写插件的开发者也会经常被PaperAPI坑。
因此强烈不建议使用PaperAPI,最好的选择是SpigotAPI。

南柯郡守
Ghost_chu 发表于 2020-5-3 10:09
用PaperAPI才是真的踩雷,Paper的API设计很失败,PaperAPI的内容和BukkitAPI混在一起,例如ItemStack#getLo ...

哈哈哈哈 我一般也是导入spigot作为依赖  查api的时候是开paper的javadoc就是了
我顺手加一下说明吧

结城希亚
一直用spigot核心开发,用Um或者Th测试插件的时候
有时候会报错,扔spigot端里就没事

真不懂为什么核心的优化要删API,做兼容真难

PercyDan
这个标签太草了

my2000
可以留个联系方式吗,有些地方我想问问

南柯郡守
my2000 发表于 2020-5-29 01:12
可以留个联系方式吗,有些地方我想问问

可以直接站内信问哦 在头像下面的  发消息

RE_OVO
1. Material.AIR那个有什么问题吗? 无论有没有更改,1.13之前出现过的材质都有LEGACY版,即使它的名字没有改变。况且,1.15有CAVE_AIR和VOID_AIR
AIR: 普通空气
CAVE_AIR: 洞穴里的空气
VOID_AIR: 虚空里的空气
2. 创建背包读取配置文件那个BUG,WTF? 还有这种BUG?我用到现在也没发现还有这种BUG啊,你确定不是以下原因?
1. config.gui = null
2. 配置文件中没有定义inv.title

南柯郡守
本帖最后由 南柯郡守 于 2020-6-21 14:40 编辑
jebme 发表于 2020-6-21 14:02
1. Material.AIR那个有什么问题吗? 无论有没有更改,1.13之前出现过的材质都有LEGACY版,即使它的名字没有 ...

1.我直接获取了原版游戏的空气就是AIR 我addItem加进去的就是LEGACY_AIR

2. 不仅是我这里 之前还看过一个人直接获取String来Material.valueOf() 也是null
但是只要先做个变量get一下而不是在括号里直接get就是可以而我别的地方用到也确实不需要先做一个变量
只能说这大概是个偶发性bug

William_Shi
南柯郡守 发表于 2020-6-21 14:39
1.我直接获取了原版游戏的空气就是AIR 我addItem加进去的就是LEGACY_AIR

2. 不仅是我这里 之前还看过一个 ...

你是不是没加上api-version啊
可能会导致部分材质无法获取或者是过期材质的

南柯郡守
William_Shi 发表于 2020-6-21 14:58
你是不是没加上api-version啊
可能会导致部分材质无法获取或者是过期材质的 ...

api-version是让插件只能在某个版本运行的意思嘛

William_Shi
南柯郡守 发表于 2020-6-21 16:07
api-version是让插件只能在某个版本运行的意思嘛

这是指定你的插件是1.13id扁平化之后的插件
加上去之后,1.13以后的插件也有可能兼容1.12或者以下的服务端了
就是说旧版本的ID可以帮你进行一些转换
高版本不加上这个的话
你所有涉及到Material的东西,都有可能会报错,要不然就是matchMaterial无法匹配得到
加上之后就正常

南柯郡守
William_Shi 发表于 2020-6-21 16:12
这是指定你的插件是1.13id扁平化之后的插件
加上去之后,1.13以后的插件也有可能兼容1.12或者以下的服务 ...

哦~ 那我明白了
感谢

huanmeng_cn
MC Mirror
https://www.mcbbs.net/thread-830124-1-1.html

我的丶老公
哈哈哈哈 我一般也是导入spigot作为依赖  查api的时候是开paper的javadoc就是了
我顺手加一下说明吧

月银秋

这个有解释了没,我大概也碰到过个类似的。
但当时我做的稍微复杂了点,最后莫名其妙解决了,也不知道怎么回事。现在想好像就是差不多这样。
但是这理论上不可能啊……我寻思这甚至会被某些编译器优化成一样的……

南柯郡守
月银秋 发表于 2020-8-17 11:05
这个有解释了没,我大概也碰到过个类似的。
但当时我做的稍微复杂了点,最后莫名其妙解决了,也不知道怎么 ...

害 到现在我不清楚这个算不算BUG
也弄不清到底为啥会发生这种问题
只能是习惯性的加个变量了

其实我自己测试的时候也都正常 只有极小的可能性会触发这个问题

洞穴夜莺
南柯郡守 发表于 2020-8-17 12:17
害 到现在我不清楚这个算不算BUG
也弄不清到底为啥会发生这种问题
只能是习惯性的加个变量了

我觉得这种解释也说不通,虽然说确实有可能是javac的bug
但是你单步调试有遇到什么反常现象吗


William_Shi
洞穴夜莺 发表于 2020-8-17 13:15
我觉得这种解释也说不通,虽然说确实有可能是javac的bug
但是你单步调试有遇到什么反常现象吗

说实话我感觉,那个把Title赋值给变量的写法
和不赋值直接传入参数的写法真的没差别啊,,,

洞穴夜莺
南柯郡守 发表于 2020-8-17 12:17
害 到现在我不清楚这个算不算BUG
也弄不清到底为啥会发生这种问题
只能是习惯性的加个变量了

我并不认为两者有任何差别,在我的电脑上测试,两者效果完全一样,并没有那个会出错的说法,理论上也不可能
如果你要说getString滞后,给出jdb调试输出来证明你的说法

南柯郡守
洞穴夜莺 发表于 2020-8-17 15:20
我并不认为两者有任何差别,在我的电脑上测试,两者效果完全一样,并没有那个会出错的说法,理论上也不可 ...

1 我在原贴中说
很明显不是这个原因

2 在9楼已经解释过了
3 再次强调 并不是只有我这里出现过该问题 同时也并没有误导任何人说必须要像我这么做
4 该问题确实存在 我的做法也确实能解决问题 我自己也没有复现过该问题 是别人用我的插件后提交给我的  你自己没出现过并不代表不存在
5 理论不代表一切

第一页 上一页 下一页 最后一页