SpongePowered / Sponge

The SpongeAPI implementation targeting vanilla Minecraft and 3rd party platforms.
MIT License
383 stars 211 forks source link

ChangeInventoryEvent item duplication & weird behavior #3335

Open CDFN opened 3 years ago

CDFN commented 3 years ago

I am currently running

Issue Description Today I encountered weird issue with ChangeInventoryEvent. Hard to explain what happens so I made short video showing problem: https://youtu.be/AV9kfiCUt7I Basically items are duping when moving them around in inventory using SHIFT+LMB. I was dropping them out using CTRL+Q. Normal clicking LMB in inventory moves items either. It started happening after adding listener for ChangeInventoryEvent.

Code:

public class PlayerItemListener {

  private final HubPlayerRepository hubPlayerRepository;

  @Inject
  public PlayerItemListener(@Named("nonpersistent") HubPlayerRepository hubPlayerRepository) {
    this.hubPlayerRepository = hubPlayerRepository;
  }

  @Listener
  public void onUse(InteractItemEvent.Secondary event, @First Player player) {
    var itemType = event.getItemStack().getType();

    hubPlayerRepository.getPlayer(player.getUniqueId()).thenAccept(hubPlayer -> {
      var optionalStatefulItem = hubPlayer.getStatefulItems()
          .stream()
          .filter(statefulItem -> statefulItem.getItemType().equals(itemType))
          .findFirst();

      if (optionalStatefulItem.isEmpty()) {
        return;
      }

      var statefulItem = optionalStatefulItem.get();
      var useResult = statefulItem.onUse(event.getItemStack());

      //TODO: Use InteractEvent#handType once https://github.com/SpongePowered/SpongeAPI/pull/2312 gets merged
      var optionalHandType = event.getCause().getContext().get(EventContextKeys.USED_HAND);
      if (optionalHandType.isEmpty()) {
        return;
      }

      var handType = optionalHandType.get();
      var inventory = player.getInventory();

      if (HandTypes.MAIN_HAND.get().equals(handType)) {
        var selectedSlot = inventory.getHotbar().getSelectedSlotIndex();
        inventory.set(selectedSlot, useResult);
        return;
      }
      inventory.getOffhand().set(useResult);

    });
  }

  @Listener
  @Exclude({ChangeInventoryEvent.Held.class})
  public void onItemMove(ChangeInventoryEvent event){
    event.setCancelled(true);
  }
}
ImMorpheus commented 3 years ago

Please follow the issue template, it's actually rather important if you want to get dev attention to fix your issue.

Edit this issue with the required details / template, and I'll reopen and hide this comment.

CDFN commented 3 years ago

@ImMorpheus Done 👌 Sorry for not following the template, I was creating issue in SpongeAPI and I think templates aren't configured there 👀

JBYoshi commented 3 years ago

I was able to reproduce the dropped items part on commit 02135c2a9b6d824725a44813c819de705b6242b9 using just the onItemMove listener, although I can't reproduce the shift-clicking bug anymore. (EDIT: The same code does reproduce the bug on the older build RC378, so I'll declare that part of the issue fixed.)

The cause of the dropped items bug seems to that API 8 no longer waits to spawn entities until the current phase completes. Instead, it spawns them immediately and relies on the phase to despawn them. (So putting a call to processSpawnedEntities only when the event is not cancelled doesn't prevent them from spawning.)

gabizou commented 3 years ago

Ah yes, I was beginning to wonder whether there'd be some conflict of organization here, It's difficult to say what could be the better solution. I may have to have a think on this.