Jump to content

[1.16.3] Sync inventory changes


JimiIT92

Recommended Posts

I am attempting to create a mod that adds the new 1.17 Bundles. To do this I listen to the mouse click event on an Inventory slot. Now, inside the Player container everything works fine, if I am in creative mode. However if I use the Bundle when another container is open (for example a Chest), or if the player is just in survival mode, whatever changes I made to the inventory won't be reflected as soon as I close the container or update it somehow.
For example, I have this survival inventory
image.png.0c446d58aef6500fe0cf25ba605a04ce.png

I then take the Bundle (the item near the crafting table) and click on the stack of 40 stone. When I click, only 32 items of the stack will go inside the bundle. The other items will remain inside the Inventory

image.png.6c7ebc9db05eb186089a2c92ffa52af1.pngimage.png.dd7cc661b535600a65768b1acc417df5.png

As you can see everything is right, but as soon as I click the Bundle again, I got this situation (if I close and open again the Inventory I still see the stone stack been shrinked to 8)

image.png.d98f8f7e17c5843c2b1ce352538ad48b.png

 

 

This is the method I use to add the Items to the Bundle
 

public static void addItemStackToBundle(ItemStack bundle, ItemStack stack, PlayerEntity player, Container container) {
  if(!isBundle(bundle) || isFull(bundle) || isBundle(stack)) {
    return;
  }
  ItemStack stackToAdd = stack.copy();
  int maxItemsToAdd = bundle.getMaxDamage() - getBundleItemsCount(bundle);
  stackToAdd.setCount(Math.min(getMaxStackSizeForBundleToInsert(stackToAdd), maxItemsToAdd));
  CompoundNBT bundleTag = bundle.getOrCreateTag();
  ListNBT items = bundleTag.getList(BundleResources.BUNDLE_ITEMS_LIST_NBT_RESOURCE_LOCATION, Constants.NBT.TAG_COMPOUND);
  CompoundNBT itemStackNbt = new CompoundNBT();
  ItemStack stackFromBundle = getItemStackFor(bundle, stackToAdd.getItem());
  int index = getItemStackIndex(bundle, stackFromBundle);
  if(!stackFromBundle.isEmpty()) {
    stackToAdd.setCount(Math.min(stackToAdd.getCount(), getMaxStackSizeForBundle(stack) - stackFromBundle.getCount()));
  }
  if(index != -1) {
    stackFromBundle.setCount(stackFromBundle.getCount() + stackToAdd.getCount());
    stackFromBundle.write(itemStackNbt);
    items.remove(index);
    items.add(index, itemStackNbt);
  } else {
    stackToAdd.write(itemStackNbt);
    items.add(itemStackNbt);
  }
  bundleTag.put(BundleResources.BUNDLE_ITEMS_LIST_NBT_RESOURCE_LOCATION, items);
  bundle.setTag(bundleTag);
  stack.setCount(stack.getCount() - stackToAdd.getCount());
  bundle.setDamage(bundle.getMaxDamage() - getBundleItemsCount(bundle));
  container.detectAndSendChanges();
  player.playSound(SoundEvents.ITEM_ARMOR_EQUIP_LEATHER, 1.0F, 1.0F);
}


I pass in the Container object for the slot that has been clicked and in the end call the detectAndSendChanges method, which should sync the inventory content between client and server (as the Click event is only fired client side, all I do here won't be visible to the server until I sync). Do I need to send a custom packet to the server to sync the inventory?

Don't blame me if i always ask for your help. I just want to learn to be better :)

Link to comment
Share on other sites

1 hour ago, JimiIT92 said:

To do this I listen to the mouse click event on an Inventory slot.

Handling this on the wrong side. You would send the information to the server which would modify the values and affect the creative screen respectively.

1 hour ago, JimiIT92 said:

Do I need to send a custom packet to the server to sync the inventory?

Most likely as the handling would need to be custom.

Link to comment
Share on other sites

Ok, so right now I do this

@SubscribeEvent(priority = EventPriority.HIGHEST)
  public static void onMouseReleased(final GuiScreenEvent.MouseReleasedEvent event) {
  if(!event.isCanceled() && event.getGui() instanceof ContainerScreen<?>) {
    ContainerScreen<?> containerScreen = (ContainerScreen<?>)event.getGui();
    Slot slot = containerScreen.getSlotUnderMouse();
    if(slot != null && !(slot instanceof CraftingResultSlot)) {
      PlayerEntity player = Minecraft.getInstance().player;
      if(player != null) {
        ItemStack draggedItemStack = player.inventory.getItemStack();
        ItemStack slotStack = slot.getStack();
        Container container = containerScreen.getContainer();
        if(slot.canTakeStack(player) && slot.isEnabled()
           && container.canMergeSlot(draggedItemStack, slot)
           && slot.isItemValid(draggedItemStack)
           && slot.getHasStack() && event.getButton() == 0
           && BundleItemUtils.isBundle(draggedItemStack)
           && BundleItemUtils.canAddItemStackToBundle(draggedItemStack, slotStack)) {
          BundleItemUtils.addItemStackToBundle(draggedItemStack, slotStack, player, container);
          event.setResult(Event.Result.DENY);
          event.setCanceled(true);
        }
      }
    }
  }
}


From what I understand, instead of calling directly BundleItemUtils I have to send a packet to the server with all the informations I need (draggedItemStack, slotStack, player and container) and then call the addItemStackToBundle while being on the server, am I right?

Don't blame me if i always ask for your help. I just want to learn to be better :)

Link to comment
Share on other sites

2 hours ago, poopoodice said:

Imo instead of sending stacks, you should probably send the slot numbers and let the server check then modify them.

Ok, so what I'm doing now is this. In the event handle, instead of calling the BundleItemUtils, I send a message to the server
 

BundleResources.NETWORK.sendToServer(new BundleServerMessage(draggedItemStack, Math.max(slot.slotNumber, slot.getSlotIndex()), false));

 Which when handled will do this
 

private static void processMessage(BundleServerMessage message, ServerPlayerEntity playerEntity) {
        Container container = playerEntity.openContainer;
        Slot slot = container.getSlot(message.slotId);
        ItemStack slotStack = slot.getStack();
        if(message.empty) {
            BundleItemUtils.emptyBundle(message.bundle, playerEntity, container);
        } else {
            BundleItemUtils.addItemStackToBundle(message.bundle, slotStack, playerEntity, container);
            slot.putStack(slotStack);
            playerEntity.inventory.setItemStack(message.bundle);
        }
    }


But I'm still having some quarkiness where Items gets duped now. Do I need to send something to the Client? If so, what should I send to it?

Edited by JimiIT92
Updated code

Don't blame me if i always ask for your help. I just want to learn to be better :)

Link to comment
Share on other sites

1 hour ago, JimiIT92 said:

slot.putStack(slotStack); playerEntity.inventory.setItemStack(message.bundle);

If you're in the player container, you shouldn't need to set anything directly to the inventory. Set it only to the container slot stacks, those will update the inventory. Second, if the item is getting duplicated, you probably aren't removing the stack on click. That should be handled cliently where when you click, you send the bundle and the slot it clicks. You remove the bundle from the slot clicked in the screen, but you don't add the other stack in its place if it's not empty. Similarly on the server, you grab the stack in the slot where you clicked, put it into the bundle, and put your stack in the slot replacing the cobblestone. Please make sure that you copied the stack you're adding or else you will dupe the bundle most likely.

Link to comment
Share on other sites

Ok, I'm slowly understanding how this works. Unfortunately I've never maged the inventory directly for a case like this, where the item stack you are dragging around with you mouse will be modified when you click but not put into the inventory yet. Now I made it kinda work, by essentially doing the same operation both on the client and on the server. I already know this is bad, however I don't know how to optimize this. Plus for some reason is working fine only inside the Player container and Creative Container and other fews. If I am, for example, inside the Furnace Container and left click a Bundle, the Items will be taken out of the Bundle, but the Bundle itself won't be updated, leading to the situation where if you spam the left click you will dupe the items inside the Bundle.

I've setup a GitHub repository so you can look up at what exactly is going on, from what I understand it has to do something with the fact that I'm setting the player inventory ItemStack (which is the item stack the player is dragging with its mouse) but if I remove entirely this then it doesn't get synced :/ 
Here is the repository https://github.com/JimiIT92/BundlesMod

Please I'm really lost into this :/ 

Don't blame me if i always ask for your help. I just want to learn to be better :)

Link to comment
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.
Note: Your post will require moderator approval before it will be visible.

Guest
Unfortunately, your content contains terms that we do not allow. Please edit your content to remove the highlighted words below.
Reply to this topic...

×   Pasted as rich text.   Restore formatting

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.



×
×
  • Create New...

Important Information

By using this site, you agree to our Terms of Use.