Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

ComponentItemHandler not working correctly with AbstractContainerMenu#moveItemStackTo #1206

Open
will-y opened this issue Jun 30, 2024 · 8 comments
Labels
1.21 Targeted at Minecraft 1.21 bug A bug or error triage Needs triaging and confirmation

Comments

@will-y
Copy link

will-y commented Jun 30, 2024

Minecraft Version: 1.21

NeoForge Version: 21.0.42-beta

Steps to Reproduce:

  1. Have an item container using a ComponentItemHandler
  2. Implement quickMoveStack using AbstractContainerMenu#moveItemStackTo (EX: Chest implementation)
  3. Shift click a stack into a container with a not full matching stack
  4. The amount that was supposed to be added to that stack will be voided
    Before:
    image
    After:
    image

Description of issue:
I think it is because ComponentItemHandler creates a copy of the item stack when retrieving from a slot. Then AbstractContainerMenu#moveItemStackTo modifies the stack in place and expects it to be saved. I was able to get around this issue by overriding AbstractContainerMenu#moveItemStackTo and changing the calls Slot#setChanged to Slot#set. Here is a simple example mod that shows this issue: https://github.com/will-y/QuickMoveTest

@will-y will-y added the triage Needs triaging and confirmation label Jun 30, 2024
@Shadows-of-Fire
Copy link
Contributor

I don't see a very good solution to this problem. There's an immutable vs mutable contract problem going on here, where the Component must be immutable and is unable to expose mutable data, but Slot expects the result of getItem to be mutable.

Realistically, solving this requires addressing that mutability disaster. The best-case scenario would be to deprecate Slot#setChanged and tell people to call Slot#set with the new ItemStack, and patch the vanilla cases where it might be problematic. The cases that might need updating are:

  1. AbstractContainerMenu#doClick, where slot7.setChanged(); should be changed to slot7.set(itemstack9);
  2. AbstractContainerMenu#moveItemStackTo, which has three sites (detailed in descending order):
    a. slot.setChanged(); >> slot.set(itemstack);
    b. slot.setChanged(); >> slot.set(itemstack);
    c. slot1.setChanged(); >> slot.set(itemstack1);

The most appropriate solution would be for vanilla to change Slot#setChanged() to protected, forcing external calls to go through Slot#set, but we can't impose that here.

@will-y
Copy link
Author

will-y commented Jul 1, 2024

I tried to write up a quick PR for this but it doesn't quite work.

  1. This itemstack9 is the item already existing in the slot. If you click a stack into an empty slot, it will then set the slot to an empty stack and void the item.
  2. The third instance here is the same sort of thing, itemstack1 is the item already in the slot.

The first one will be a problem for the ItemStackedOnOtherEvent mutating the stack or for people who override ItemStack#overrideOtherStackedOnMe

The second one seems like a redundant call because the slot1.setByPlayer calls setChanged() so it is probably fine to leave alone?

@Technici4n
Copy link
Member

@Shadows-of-Fire
Copy link
Contributor

I had thought of doing that kind of cursed slot-stack-caching but was worried about what terrible side effects that might incur under various conditions.

@Technici4n
Copy link
Member

I've been doing it for years :D

@sciwhiz12 sciwhiz12 added 1.21 Targeted at Minecraft 1.21 bug A bug or error labels Jul 2, 2024
@will-y
Copy link
Author

will-y commented Jul 2, 2024

Would just adding this logic to SlotItemHandler be the best solution, or applying similar caching logic in ComponentItemHandler because that's where the problem really is.

@Technici4n
Copy link
Member

IMO this should be as self-contained as possible. I would maybe reuse the HackySlot directly? This should certainly not leak to the item handler itself.

@will-y
Copy link
Author

will-y commented Jul 3, 2024

So create a new Slot that should be used for immutable ItemHandlers and then have SlotItemHandler only work with mutable ItemHandlers? Like maybe this? https://gist.github.com/will-y/c0e35adcedfe80f149bb9de9fa266d46

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
1.21 Targeted at Minecraft 1.21 bug A bug or error triage Needs triaging and confirmation
Projects
None yet
Development

No branches or pull requests

4 participants