Gui shitf双击出bug
dawnflyc
版本信息
你使用的IDE:IDEA
你使用的IDE版本:2019.3.3
Forge版本: 14.23.5.2847
Minecraft版本: 1.12.2
错误情况简述
Shift+左击两下物品假消失。
报错日志
完全没有日志
相关代码
transferStackInSlot
public ItemStack transferStackInSlot(EntityPlayer playerIn, int index) {
ItemStack itemstack = ItemStack.EMPTY;
Slot slot = this.inventorySlots.get(index);
if (slot != null && slot.getHasStack() && !LockItem.isLock(slot.getStack().getItem()))
{
ItemStack itemstack1 = slot.getStack();
itemstack = itemstack1.copy();
int maxSlot=playerIn.getCapability(DimInvoProvider.DIMINV,null).getUnLockIndex();
if (index < maxSlot)
{
if (!this.mergeItemStack(itemstack1, maxSlot, this.inventorySlots.size(), true))
{
return ItemStack.EMPTY;
}
}
else if (!this.mergeItemStack(itemstack1, 0, maxSlot, false))
{
return ItemStack.EMPTY;
}
if (itemstack1.isEmpty())
{
slot.putStack(ItemStack.EMPTY);
}
else
{
slot.onSlotChanged();
}
}
return itemstack;
}
LockSlot
@Override
public boolean canTakeStack(EntityPlayer playerIn) {
if (LockItem.isLock(this.getStack().getItem())){
IDimInvo dimInvo=playerIn.getCapability(DimInvoProvider.DIMINV,null);
if (LockItem.isLock(this.getStack().getItem(),LockItem.UN_LOCK_ITEM) && this.slotNumber ==dimInvo.getUnLockIndex()){
unLock(playerIn,dimInvo);
}else {
format(playerIn);
}
return false;
}
return true;
}
private void format(EntityPlayer player){
player.getCapability(DimInvoProvider.DIMINV,null).format();
}
private void unLock(EntityPlayer player,IDimInvo dimInvo){
dimInvo.setUnLockIndex(dimInvo.getUnLockIndex()+1);
dimInvo.format();
}
我自己写了一格一格解锁的箱子,弄了两个物品作为解锁和锁定。设置点击解锁会调用格式化方法,点击锁定无相应。然后和发现一个bug,我只要在这个gui里对着任何物品连续两下shift+左击,那么锁和解锁这俩物品就会有点一下不见再点一下出来的bug,我哪怕重开gui也不行,只有kill和重开游戏。我是用能力存储物品栈的
FledgeXu
dawnflyc
FledgeXu
应该是这个函数实现有问题。
public ItemStack transferStackInSlot(PlayerEntity playerIn, int index)
不要在这个函数里加锁,这个函数不是干这件事的地方。
这个函数尽量应该是一个「纯函数」,不是能有「副作用」
这里是个例子:
nowandfuture
没看懂你在说什么,shift+左键 的单击 和 shift+左键 的双击,在不同情况下是不同的,当你点击slot时,鼠标处有一个物品hold的时候,双击shift+左键会在第一次(双击有两次单击)单击时使用普通的 shift+左键在服务端调用一次slotClick,其中的type是QUICK_MOVE,同时缓存shift+左键时点击的槽,当第二次点击生效后,会检测之前的缓存槽记录搜索物品栏中所有可以合并的物件,并各执行一次QUICK_MOVE;当点击slot时没有hold任何一个物品,相当于执行两次普通的shift+左键,第二次因为点击的为空相当于没有效果。
至于:
public ItemStack transferStackInSlot(PlayerEntity playerIn, int index)
你的实现应该是正确的,可以把问题排查放在PICK_UP时发生了什么,所以我在看不懂你碰到的问题的情况下不知道怎么寻找问题,可以gif或者重新叙述一次问题吗?
dawnflyc
不行啊,我不管是不重写还是直接返回Itemstack.EMPTY,都还会那样。
dawnflyc
我先往玩家背包里放一个任意物品,鼠标放在这个物品上,或者放在锁上,鼠标没有物品直接shift按住左击两下,也就是说,需要在shift左击后格子东西不变的时候才会触发bug,然而只有锁会消失,其他物品不会消失。
LockItem
public static final LockItem LOCK_ITEM =new LockItem("lock");
public static final LockItem UN_LOCK_ITEM =new LockItem("un_lock");
public LockItem(String id) {
this.setRegistryName(Diminvo.MODID,id);
this.setUnlocalizedName(id);
EventHandler.ITEMS.put(id,this);
this.setMaxStackSize(1);
}
public static void initialize(){
//在preInit调用这个方法,不然不会加载这个类,也就不会注册这俩物品。
}
@Override
public boolean onDroppedByPlayer(ItemStack item, EntityPlayer player) {
return super.onDroppedByPlayer(item, player);
}
public static boolean isLock(Item item){
return isLock(item,UN_LOCK_ITEM) || isLock(item,LOCK_ITEM);
}
public static boolean isLock(Item item0,LockItem item1){
if (item0 ==null || item1 ==null){
return false;
}
return item0.getRegistryName().getResourcePath().equals(item1.getRegistryName().getResourcePath());
}
注册锁
public static final Map<String,Item> ITEMS=new HashMap<>();
@SubscribeEvent
public void registerItems(RegistryEvent.Register<Item> event) {
for (Item item : ITEMS.values()) {
event.getRegistry().register(item);
ModelLoader.setCustomModelResourceLocation(item,0,new ModelResourceLocation(item.getRegistryName(), "inventory"));
}
}
dawnflyc
nowandfuture
还是没看懂你的问题,我前面已经说了,你如果没有持物品的话,shift+左键 和 shift+左键双击 最终效果是一样的,只是双击时第二次的shift+左键被阻断在mouseRelease中了,所以我的意思是,你在shift+左键时发生了什么,你可以测试下,理论上这两者应该同样触发bug,当这些发生后物品是不是只是未能正常渲染,渲染的代码,是扫描整个container的slot集合并逐个绘制,没有被绘制说明:它不在物品槽集合中,你可以尝试在container中尝试输出slot集合的数量检查是否被移动到其他容器中导致未正常绘制。因为,槽本身涉及到的东西比较多,包含了网络,绘制和点击逻辑,不太好判断是哪里出了问题,建议给下gif图片,或者普通图片展示下是在怎么样的情况下出了问题。
nowandfuture
看了下,如果你的代码transferStackInSlot中的mergeItemStack函数的作用是:
以以下具体形式为例:
mergeItemStack(source, start, end, true)
参数source是源物品栈,merge的结果就是将source中的所有物品尝试分配到整个inventorySlots中起始下标start(包含)到结束下标end(不包含)的所有slot中,当source中的物品一个都无法分配时返回false,否则返回true;
你的源码会导致,将你已解锁部分的分配到未解锁部分或其他容器中,来一幅图就是:
其中假设红色是已解锁部分,上半部分是你的容器a,下半部分是容器b,你这时候shift+左键点击了黄色部分,那么在客户端和服务端都会调用上述方法,如果你的写法,会将红色部分(包含点击的黄色)尝试分配到剩下的所有白色块(容器a和容器b中的所有可填充槽);
对于整个函数,当他返回Empty时表示它完成了 分配/转移,停止循环,否则进行下一次分配尝试。
dawnflyc
在mergeItemStack里面判断了没用啊,我也尝试在客户端层面判断,
protected void handleMouseClick(Slot slotIn, int slotId, int mouseButton, ClickType type)
这个方法啥也不写直接 return;都不行。。。。
dawnflyc
唯一改变的,就是需要三下才会消失,而且消失了得重新打开界面,否则不出来。有一个疑问,我触发bug,我关闭界面重新打开都不行,这是为啥,只有重新进游戏或者kill才能恢复。
dawnflyc
好像和我保存有点关系,它好像不是客户端问题,我在CapabilityEntity调试,发现他真的会把数组改成全air。一般都是什么时候保存啊,我是这样的
·DiminvoInventory·
public DiminvoInventory(EntityPlayer player)
{
if (player.getCapability(DimInvoProvider.DIMINV,null)!=null && player.getCapability(DimInvoProvider.DIMINV,null).getInventory()!=null){
this.player=player;
this.inventoryContents=player.getCapability(DimInvoProvider.DIMINV,null).getInventory();
this.addInventoryChangeListener(this::update);
}else {
this.inventoryContents=player.getCapability(DimInvoProvider.DIMINV,null).initialize();
}
}
private void update(IInventory iInventory) {
player.getCapability(DimInvoProvider.DIMINV,null).setInventory(inventoryContents);
}
FledgeXu
FledgeXu
nowandfuture
这个方法会在客户端和服务端执行,分配slot的点击结果,你直接return,点击就没有任何影响了。
你应该没有仔细看我的回复,你那样写是有问题的,但是我也不知道为什么会造成那样的bug,但是可以确定的是,你的物品没渲染出来是因为,那一格真的没有物品,你需要先正确重写那个函数;其次,Capability的保存与否在游戏如果不退出是不会导致问题的。minecraft的slot相关函数,应该是足够健壮的,应该不会有同步导致的问题,你好好检查,下当你shift左键后槽和里面物品的分布有没有问题;
FledgeXu
请问你的问题解决了吗?解决了请选择一个楼层或者自己补充一下,作为答案。
dawnflyc
解决了,我把服务端的解锁、判断、格式化都删掉了,客户端点了发个空白包给服务端,服务端判断并解锁,然后就好了。
system
该主题在最后一个回复创建后7天后自动关闭。不再允许新的回复。