new event McMMOModifyBlockDropItemEvent and mcMMO now modifies the drop list in BlockDropItemEvent instead of spawning items

Fixes #5214
This commit is contained in:
nossr50
2025-09-21 12:19:55 -07:00
parent 8e049822a3
commit d38d74f82a
4 changed files with 556 additions and 62 deletions

View File

@@ -0,0 +1,226 @@
package com.gmail.nossr50.events.items;
import static java.util.Objects.requireNonNull;
import org.bukkit.block.Block;
import org.bukkit.block.BlockState;
import org.bukkit.entity.Item;
import org.bukkit.entity.Player;
import org.bukkit.event.Cancellable;
import org.bukkit.event.Event;
import org.bukkit.event.HandlerList;
import org.bukkit.event.block.BlockDropItemEvent;
import org.jetbrains.annotations.NotNull;
/**
* Called when mcMMO is modifying the amount of bonus drops to add to an Item involved in a {@link BlockDropItemEvent}.
* <p>
* This event is called before mcMMO has modified the ItemStack quantity on the {@link Item} entity.
* <p>
* This event is called once per Item entity that is involved in the {@link BlockDropItemEvent}.
* <p>
* This event is called during mcMMO logic on the {@link BlockDropItemEvent}, and can be used to
* modify the quantity that mcMMO will add to the ItemStack.
* <p>
* This event is considered cancelled if it is either cancelled directly or if bonus drops are 0 or
* less.
*/
public class McMMOModifyBlockDropItemEvent extends Event implements Cancellable {
private final @NotNull BlockDropItemEvent blockDropItemEvent;
private final int originalBonusAmountToAdd;
private int modifiedItemStackQuantity;
private final @NotNull Item itemThatHasBonusDrops;
private boolean isCancelled = false;
private final int originalItemStackQuantity;
public McMMOModifyBlockDropItemEvent(@NotNull BlockDropItemEvent blockDropItemEvent,
@NotNull Item itemThatHasBonusDrops, int bonusDropsToAdd) {
super(false);
requireNonNull(blockDropItemEvent, "blockDropItemEvent cannot be null");
requireNonNull(itemThatHasBonusDrops, "itemThatHasBonusDrops cannot be null");
if (bonusDropsToAdd <= 0) {
throw new IllegalArgumentException("cannot instantiate a new"
+ " McMMOModifyBlockDropItemEvent with a bonusDropsToAdd that is <= 0");
}
this.blockDropItemEvent = blockDropItemEvent;
this.itemThatHasBonusDrops = itemThatHasBonusDrops;
this.originalItemStackQuantity = itemThatHasBonusDrops.getItemStack().getAmount();
this.originalBonusAmountToAdd = bonusDropsToAdd;
this.modifiedItemStackQuantity = itemThatHasBonusDrops.getItemStack().getAmount()
+ bonusDropsToAdd;
}
@Override
public boolean isCancelled() {
return isCancelled;
}
@Override
public void setCancelled(boolean cancel) {
this.isCancelled = cancel;
}
/**
* The original BlockDropItemEvent which caused this event to be fired.
* @return the original BlockDropItemEvent
*/
public @NotNull BlockDropItemEvent getBlockDropItemEvent() {
return blockDropItemEvent;
}
/**
* The original bonus mcMMO would have added before any modifications to this event from
* other plugins.
* @return the original bonus amount to add
*/
public int getOriginalBonusAmountToAdd() {
return originalBonusAmountToAdd;
}
/**
* The Item entity that is being modified by this event.
* This item returned by this call should not be modified, it is provided as a convenience.
* @return the Item entity that is having bonus drops added to it.
*/
public @NotNull Item getItem() {
return itemThatHasBonusDrops;
}
/**
* The modified ItemStack quantity that will be set on the Item entity if this event is not
* cancelled.
*
* @return the modified ItemStack quantity that will be set on the Item entity
*/
public int getModifiedItemStackQuantity() {
return modifiedItemStackQuantity;
}
/**
* The original ItemStack quantity of the Item entity before any modifications from this event.
* This is a reflection of the state of the Item when mcMMO fired this event.
* It is possible it has modified since then, so do not rely on this value to be the current.
* @return the original ItemStack quantity of the Item entity before any modifications from this event
*/
public int getOriginalItemStackQuantity() {
return originalItemStackQuantity;
}
/**
* The amount of bonus that will be added to the ItemStack quantity if this event is not
* cancelled.
* @return the amount of bonus that will be added to the ItemStack quantity
*/
public int getBonusAmountToAdd() {
return Math.max(0, modifiedItemStackQuantity - originalItemStackQuantity);
}
/**
* Set the amount of bonus that will be added to the ItemStack quantity if this event is not
* cancelled.
* @param bonus the amount of bonus that will be added to the ItemStack quantity
* @throws IllegalArgumentException if bonus is less than 0
*/
public void setBonusAmountToAdd(int bonus) {
if (bonus < 0) throw new IllegalArgumentException("bonus must be >= 0");
this.modifiedItemStackQuantity = originalItemStackQuantity + bonus;
}
/**
* Set the modified ItemStack quantity that will be set on the Item entity if this event is not
* cancelled. This CANNOT be lower than the original quantity of the ItemStack.
* @param modifiedItemStackQuantity the modified ItemStack quantity that will be set on the Item entity
* @throws IllegalArgumentException if modifiedItemStackQuantity is less than originalItemStackQuantity
*/
public void setModifiedItemStackQuantity(int modifiedItemStackQuantity) {
if (modifiedItemStackQuantity < originalItemStackQuantity) {
throw new IllegalArgumentException(
"modifiedItemStackQuantity cannot be less than the originalItemStackQuantity");
}
this.modifiedItemStackQuantity = modifiedItemStackQuantity;
}
public boolean isEffectivelyNoBonus() {
return modifiedItemStackQuantity == originalItemStackQuantity;
}
/**
* Delegate method for {@link BlockDropItemEvent}, gets the Player that is breaking the block
* involved in this event.
*
* @return The Player that is breaking the block involved in this event
*/
public @NotNull Player getPlayer() {
return blockDropItemEvent.getPlayer();
}
/**
* Delegate method for {@link BlockDropItemEvent#getBlock()}.
* Gets the Block involved in this event.
*
* @return the Block involved in this event
*/
public @NotNull Block getBlock() {
return blockDropItemEvent.getBlock();
}
/**
* Delegate method for {@link BlockDropItemEvent#getBlockState()}.
* Gets the BlockState of the block involved in this event.
*
* @return the BlockState of the block involved in this event
*/
public @NotNull BlockState getBlockState() {
return blockDropItemEvent.getBlockState();
}
private static final @NotNull HandlerList handlers = new HandlerList();
@Override
public @NotNull HandlerList getHandlers() {
return handlers;
}
public static @NotNull HandlerList getHandlerList() {
return handlers;
}
@Override
public @NotNull String toString() {
return "McMMOModifyBlockDropItemEvent{" +
"blockDropItemEvent=" + blockDropItemEvent +
", originalBonusAmountToAdd=" + originalBonusAmountToAdd +
", modifiedItemStackQuantity=" + modifiedItemStackQuantity +
", itemThatHasBonusDrops=" + itemThatHasBonusDrops +
", isCancelled=" + isCancelled +
", originalItemStackQuantity=" + originalItemStackQuantity +
'}';
}
@Override
public final boolean equals(Object o) {
if (!(o instanceof McMMOModifyBlockDropItemEvent that)) {
return false;
}
return originalBonusAmountToAdd == that.originalBonusAmountToAdd
&& modifiedItemStackQuantity == that.modifiedItemStackQuantity
&& isCancelled == that.isCancelled
&& originalItemStackQuantity == that.originalItemStackQuantity
&& blockDropItemEvent.equals(that.blockDropItemEvent)
&& itemThatHasBonusDrops.equals(
that.itemThatHasBonusDrops);
}
@Override
public int hashCode() {
int result = blockDropItemEvent.hashCode();
result = 31 * result + originalBonusAmountToAdd;
result = 31 * result + modifiedItemStackQuantity;
result = 31 * result + itemThatHasBonusDrops.hashCode();
result = 31 * result + Boolean.hashCode(isCancelled);
result = 31 * result + originalItemStackQuantity;
return result;
}
}

View File

@@ -1,5 +1,6 @@
package com.gmail.nossr50.listeners;
import static com.gmail.nossr50.util.MetadataConstants.METADATA_KEY_BONUS_DROPS;
import static com.gmail.nossr50.util.Misc.getBlockCenter;
import com.gmail.nossr50.api.ItemSpawnReason;
@@ -14,6 +15,7 @@ import com.gmail.nossr50.datatypes.skills.ToolType;
import com.gmail.nossr50.events.fake.FakeBlockBreakEvent;
import com.gmail.nossr50.events.fake.FakeBlockDamageEvent;
import com.gmail.nossr50.events.fake.FakeEvent;
import com.gmail.nossr50.events.items.McMMOModifyBlockDropItemEvent;
import com.gmail.nossr50.mcMMO;
import com.gmail.nossr50.skills.alchemy.Alchemy;
import com.gmail.nossr50.skills.excavation.ExcavationManager;
@@ -35,6 +37,8 @@ import com.gmail.nossr50.util.sounds.SoundType;
import com.gmail.nossr50.worldguard.WorldGuardManager;
import com.gmail.nossr50.worldguard.WorldGuardUtils;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import org.bukkit.ChatColor;
import org.bukkit.GameMode;
import org.bukkit.Location;
@@ -62,6 +66,7 @@ import org.bukkit.event.block.BlockPistonRetractEvent;
import org.bukkit.event.block.BlockPlaceEvent;
import org.bukkit.event.block.EntityBlockFormEvent;
import org.bukkit.inventory.ItemStack;
import org.bukkit.metadata.MetadataValue;
public class BlockListener implements Listener {
private final mcMMO plugin;
@@ -70,88 +75,96 @@ public class BlockListener implements Listener {
this.plugin = plugin;
}
@EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = false)
@EventHandler(priority = EventPriority.LOWEST, ignoreCancelled = false)
public void onBlockDropItemEvent(BlockDropItemEvent event) {
//Make sure we clean up metadata on these blocks
final Block block = event.getBlock();
if (event.isCancelled()) {
if (event.getBlock().hasMetadata(MetadataConstants.METADATA_KEY_BONUS_DROPS)) {
event.getBlock().removeMetadata(MetadataConstants.METADATA_KEY_BONUS_DROPS, plugin);
if (block.hasMetadata(METADATA_KEY_BONUS_DROPS)) {
block.removeMetadata(METADATA_KEY_BONUS_DROPS, plugin);
}
return;
}
int tileEntityTolerance = 1;
try {
int tileEntityTolerance = 1;
// beetroot hotfix, potentially other plants may need this fix
if (event.getBlock().getType() == Material.BEETROOTS) {
tileEntityTolerance = 2;
}
//Track how many "things" are being dropped
HashSet<Material> uniqueMaterials = new HashSet<>();
boolean dontRewardTE = false; //If we suspect TEs are mixed in with other things don't reward bonus drops for anything that isn't a block
int blockCount = 0;
for (Item item : event.getItems()) {
//Track unique materials
uniqueMaterials.add(item.getItemStack().getType());
//Count blocks as a second failsafe
if (item.getItemStack().getType().isBlock()) {
blockCount++;
// beetroot hotfix, potentially other plants may need this fix
final Material blockType = block.getType();
if (blockType == Material.BEETROOTS) {
tileEntityTolerance = 2;
}
}
if (uniqueMaterials.size() > tileEntityTolerance) {
//Too many things are dropping, assume tile entities might be duped
//Technically this would also prevent something like coal from being bonus dropped if you placed a TE above a coal ore when mining it but that's pretty edge case and this is a good solution for now
dontRewardTE = true;
}
//Track how many "things" are being dropped
final Set<Material> uniqueMaterials = new HashSet<>();
boolean dontRewardTE = false; //If we suspect TEs are mixed in with other things don't reward bonus drops for anything that isn't a block
int blockCount = 0;
//If there are more than one block in the item list we can't really trust it and will back out of rewarding bonus drops
if (blockCount <= 1) {
for (Item item : event.getItems()) {
ItemStack is = new ItemStack(item.getItemStack());
final List<Item> eventItems = event.getItems();
for (Item item : eventItems) {
//Track unique materials
uniqueMaterials.add(item.getItemStack().getType());
if (is.getAmount() <= 0) {
continue;
//Count blocks as a second failsafe
if (item.getItemStack().getType().isBlock()) {
blockCount++;
}
}
//TODO: Ignore this abomination its rewritten in 2.2
if (!mcMMO.p.getGeneralConfig()
.getDoubleDropsEnabled(PrimarySkillType.MINING, is.getType())
&& !mcMMO.p.getGeneralConfig()
.getDoubleDropsEnabled(PrimarySkillType.HERBALISM, is.getType())
&& !mcMMO.p.getGeneralConfig()
.getDoubleDropsEnabled(PrimarySkillType.WOODCUTTING, is.getType())) {
continue;
}
if (uniqueMaterials.size() > tileEntityTolerance) {
// Too many things are dropping, assume tile entities might be duped
// Technically this would also prevent something like coal from being bonus dropped
// if you placed a TE above a coal ore when mining it but that's pretty edge case
// and this is a good solution for now
dontRewardTE = true;
}
//If we suspect TEs might be duped only reward block
if (dontRewardTE) {
if (!is.getType().isBlock()) {
continue;
}
}
//If there are more than one block in the item list we can't really trust it
// and will back out of rewarding bonus drops
if (!block.getMetadata(METADATA_KEY_BONUS_DROPS).isEmpty()) {
final MetadataValue bonusDropMeta = block
.getMetadata(METADATA_KEY_BONUS_DROPS).get(0);
if (blockCount <= 1) {
for (final Item item : eventItems) {
final ItemStack eventItemStack = item.getItemStack();
int originalAmount = eventItemStack.getAmount();
if (event.getBlock().getMetadata(MetadataConstants.METADATA_KEY_BONUS_DROPS).size()
> 0) {
final BonusDropMeta bonusDropMeta =
(BonusDropMeta) event.getBlock().getMetadata(
MetadataConstants.METADATA_KEY_BONUS_DROPS).get(0);
int bonusCount = bonusDropMeta.asInt();
final Location centeredLocation = getBlockCenter(event.getBlock());
for (int i = 0; i < bonusCount; i++) {
if (eventItemStack.getAmount() <= 0) {
continue;
}
ItemUtils.spawnItemNaturally(event.getPlayer(),
centeredLocation, is, ItemSpawnReason.BONUS_DROPS);
final Material itemType = eventItemStack.getType();
if (!mcMMO.p.getGeneralConfig()
.getDoubleDropsEnabled(PrimarySkillType.MINING, itemType)
&& !mcMMO.p.getGeneralConfig()
.getDoubleDropsEnabled(PrimarySkillType.HERBALISM, itemType)
&& !mcMMO.p.getGeneralConfig()
.getDoubleDropsEnabled(PrimarySkillType.WOODCUTTING, itemType)) {
continue;
}
//If we suspect TEs might be duped only reward block
if (dontRewardTE) {
if (!itemType.isBlock()) {
continue;
}
}
int amountToAddFromBonus = bonusDropMeta.asInt();
final McMMOModifyBlockDropItemEvent modifyBlockDropItemEvent
= new McMMOModifyBlockDropItemEvent(event, item, amountToAddFromBonus);
plugin.getServer().getPluginManager().callEvent(modifyBlockDropItemEvent);
if (!modifyBlockDropItemEvent.isCancelled()
&& modifyBlockDropItemEvent.getModifiedItemStackQuantity() > originalAmount) {
eventItemStack.setAmount(modifyBlockDropItemEvent.getModifiedItemStackQuantity());
}
}
}
}
}
if (event.getBlock().hasMetadata(MetadataConstants.METADATA_KEY_BONUS_DROPS)) {
event.getBlock().removeMetadata(MetadataConstants.METADATA_KEY_BONUS_DROPS, plugin);
} finally {
if (block.hasMetadata(METADATA_KEY_BONUS_DROPS)) {
block.removeMetadata(METADATA_KEY_BONUS_DROPS, plugin);
}
}
}

View File

@@ -0,0 +1,252 @@
package com.gmail.nossr50.events.items;
import static org.junit.jupiter.api.Assertions.*;
import static org.mockito.Mockito.RETURNS_DEEP_STUBS;
import static org.mockito.Mockito.atLeastOnce;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import org.bukkit.block.Block;
import org.bukkit.block.BlockState;
import org.bukkit.entity.Item;
import org.bukkit.entity.Player;
import org.bukkit.event.HandlerList;
import org.bukkit.event.block.BlockDropItemEvent;
import org.bukkit.inventory.ItemStack;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Nested;
import org.junit.jupiter.api.Test;
class McMMOModifyBlockDropItemEventTest {
private BlockDropItemEvent blockDropItemEvent;
private Item itemEntity;
private ItemStack itemStack;
private Player player;
private Block block;
private BlockState blockState;
@BeforeEach
void setUp() {
// Mocks for delegate passthroughs
player = mock(Player.class, RETURNS_DEEP_STUBS);
block = mock(Block.class, RETURNS_DEEP_STUBS);
blockState = mock(BlockState.class, RETURNS_DEEP_STUBS);
// Primary Bukkit event mock
blockDropItemEvent = mock(BlockDropItemEvent.class, RETURNS_DEEP_STUBS);
when(blockDropItemEvent.getPlayer()).thenReturn(player);
when(blockDropItemEvent.getBlock()).thenReturn(block);
when(blockDropItemEvent.getBlockState()).thenReturn(blockState);
// Item + ItemStack mock
itemStack = mock(ItemStack.class);
when(itemStack.getAmount()).thenReturn(3); // original count
itemEntity = mock(Item.class);
when(itemEntity.getItemStack()).thenReturn(itemStack);
}
private McMMOModifyBlockDropItemEvent newEvent(int bonus) {
return new McMMOModifyBlockDropItemEvent(blockDropItemEvent, itemEntity, bonus);
}
@Nested
@DisplayName("Constructor & validation")
class ConstructorValidation {
@Test
void ctorNullEventThrows() {
assertThrows(NullPointerException.class,
() -> new McMMOModifyBlockDropItemEvent(null, itemEntity, 1));
}
@Test
void ctorNullItemThrows() {
assertThrows(NullPointerException.class,
() -> new McMMOModifyBlockDropItemEvent(blockDropItemEvent, null, 1));
}
@Test
void ctorZeroBonusThrows() {
assertThrows(IllegalArgumentException.class,
() -> new McMMOModifyBlockDropItemEvent(blockDropItemEvent, itemEntity, 0));
}
@Test
void ctorNegativeBonusThrows() {
assertThrows(IllegalArgumentException.class,
() -> new McMMOModifyBlockDropItemEvent(blockDropItemEvent, itemEntity, -5));
}
@Test
void ctorSetsOriginalsAndModifiedCorrectly() {
// original amount = 3, bonus = 2
McMMOModifyBlockDropItemEvent ev = newEvent(2);
assertEquals(3, ev.getOriginalItemStackQuantity());
assertEquals(2, ev.getOriginalBonusAmountToAdd());
assertEquals(5, ev.getModifiedItemStackQuantity());
assertFalse(ev.isCancelled());
assertFalse(ev.isEffectivelyNoBonus());
}
}
@Nested
@DisplayName("Cancellable contract")
class Cancellation {
@Test
void cancelAndUncancel() {
McMMOModifyBlockDropItemEvent ev = newEvent(1);
assertFalse(ev.isCancelled());
ev.setCancelled(true);
assertTrue(ev.isCancelled());
ev.setCancelled(false);
assertFalse(ev.isCancelled());
}
}
@Nested
@DisplayName("Delta & absolute quantity semantics")
class DeltaAndAbsolute {
@Test
void getBonusAmountToAddReflectsDifferenceFromOriginal() {
// original 3, bonus 4 => modified 7
McMMOModifyBlockDropItemEvent ev = newEvent(4);
assertEquals(4, ev.getBonusAmountToAdd());
assertEquals(7, ev.getModifiedItemStackQuantity());
}
@Test
void setBonusAmountToAddUpdatesModifiedQuantity() {
McMMOModifyBlockDropItemEvent ev = newEvent(2); // original 3 -> modified 5
ev.setBonusAmountToAdd(10); // new modified should be 13
assertEquals(13, ev.getModifiedItemStackQuantity());
assertEquals(10, ev.getBonusAmountToAdd());
assertFalse(ev.isEffectivelyNoBonus());
}
@Test
void setBonusAmountToAddNegativeThrows() {
McMMOModifyBlockDropItemEvent ev = newEvent(1);
assertThrows(IllegalArgumentException.class, () -> ev.setBonusAmountToAdd(-1));
}
@Test
void setModifiedItemStackQuantityEqualToOriginalIsNoBonus() {
McMMOModifyBlockDropItemEvent ev = newEvent(2); // 3 -> 5
ev.setModifiedItemStackQuantity(3); // back to original => no bonus
assertEquals(3, ev.getModifiedItemStackQuantity());
assertEquals(0, ev.getBonusAmountToAdd());
assertTrue(ev.isEffectivelyNoBonus());
}
@Test
void setModifiedItemStackQuantityLessThanOriginalThrows() {
McMMOModifyBlockDropItemEvent ev = newEvent(1);
assertThrows(IllegalArgumentException.class, () -> ev.setModifiedItemStackQuantity(2)); // original is 3
}
@Test
void setModifiedItemStackQuantityGreaterThanOriginalUpdatesBonus() {
McMMOModifyBlockDropItemEvent ev = newEvent(1); // original 3 -> modified 4
ev.setModifiedItemStackQuantity(12);
assertEquals(12, ev.getModifiedItemStackQuantity());
assertEquals(9, ev.getBonusAmountToAdd()); // 12 - 3
assertFalse(ev.isEffectivelyNoBonus());
}
}
@Nested
@DisplayName("Delegate passthroughs")
class Delegates {
@Test
void getPlayerPassthrough() {
McMMOModifyBlockDropItemEvent ev = newEvent(1);
assertSame(player, ev.getPlayer());
verify(blockDropItemEvent, atLeastOnce()).getPlayer();
}
@Test
void getBlockPassthrough() {
McMMOModifyBlockDropItemEvent ev = newEvent(1);
assertSame(block, ev.getBlock());
verify(blockDropItemEvent, atLeastOnce()).getBlock();
}
@Test
void getBlockStatePassthrough() {
McMMOModifyBlockDropItemEvent ev = newEvent(1);
assertSame(blockState, ev.getBlockState());
verify(blockDropItemEvent, atLeastOnce()).getBlockState();
}
@Test
void getItemReturnsOriginalItemEntity() {
McMMOModifyBlockDropItemEvent ev = newEvent(1);
assertSame(itemEntity, ev.getItem());
}
}
@Nested
@DisplayName("HandlerList plumbing")
class HandlerListTests {
@Test
void handlerList_isNonNull_andShared() {
McMMOModifyBlockDropItemEvent ev = newEvent(1);
HandlerList fromInstance = ev.getHandlers();
HandlerList fromStatic = McMMOModifyBlockDropItemEvent.getHandlerList();
assertNotNull(fromInstance);
assertNotNull(fromStatic);
// Bukkit convention: same static instance
assertSame(fromStatic, fromInstance);
}
}
@Nested
@DisplayName("Object contracts")
class ObjectContracts {
@Test
void toStringContainsKeyFields() {
McMMOModifyBlockDropItemEvent ev = newEvent(2);
String s = ev.toString();
assertNotNull(s);
assertTrue(s.contains("originalBonusAmountToAdd=2"));
assertTrue(s.contains("modifiedItemStackQuantity=5"));
}
@Test
void equalsAndHashCodeReflectState() {
// Same inputs => equal (mocks are same instances)
McMMOModifyBlockDropItemEvent a = newEvent(2);
McMMOModifyBlockDropItemEvent b = newEvent(2);
assertEquals(a, b);
assertEquals(a.hashCode(), b.hashCode());
// Change cancellation and modified quantity => not equal
McMMOModifyBlockDropItemEvent c = newEvent(2);
c.setCancelled(true);
assertNotEquals(a, c);
McMMOModifyBlockDropItemEvent d = newEvent(2);
d.setModifiedItemStackQuantity(99);
assertNotEquals(a, d);
// Different underlying mocks => not equal
BlockDropItemEvent otherEvent = mock(BlockDropItemEvent.class, RETURNS_DEEP_STUBS);
when(otherEvent.getPlayer()).thenReturn(player);
when(otherEvent.getBlock()).thenReturn(block);
when(otherEvent.getBlockState()).thenReturn(blockState);
ItemStack otherStack = mock(ItemStack.class);
when(otherStack.getAmount()).thenReturn(3);
Item otherItem = mock(Item.class);
when(otherItem.getItemStack()).thenReturn(otherStack);
McMMOModifyBlockDropItemEvent e = new McMMOModifyBlockDropItemEvent(otherEvent, otherItem, 2);
assertNotEquals(a, e);
}
}
}