南柯郡守
本帖最后由 南柯郡守 于 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。

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