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


你是不是没有实现shift+左击的方法……


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


github:GitHub - dawnflyc/Diminvo


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;
你的源码会导致,将你已解锁部分的分配到未解锁部分或其他容器中,来一幅图就是:
avatar
其中假设红色是已解锁部分,上半部分是你的容器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


你的数据markDirty()了吗?


nowandfuture


这个方法会在客户端和服务端执行,分配slot的点击结果,你直接return,点击就没有任何影响了。
你应该没有仔细看我的回复,你那样写是有问题的,但是我也不知道为什么会造成那样的bug,但是可以确定的是,你的物品没渲染出来是因为,那一格真的没有物品,你需要先正确重写那个函数;其次,Capability的保存与否在游戏如果不退出是不会导致问题的。minecraft的slot相关函数,应该是足够健壮的,应该不会有同步导致的问题,你好好检查,下当你shift左键后槽和里面物品的分布有没有问题;


FledgeXu


请问你的问题解决了吗?解决了请选择一个楼层或者自己补充一下,作为答案。


dawnflyc


解决了,我把服务端的解锁、判断、格式化都删掉了,客户端点了发个空白包给服务端,服务端判断并解锁,然后就好了。


system


该主题在最后一个回复创建后7天后自动关闭。不再允许新的回复。