--- a/net/minecraft/world/level/block/DispenserBlock.java
+++ b/net/minecraft/world/level/block/DispenserBlock.java
@@ -71,15 +_,14 @@
 
     @Override
     protected InteractionResult useWithoutItem(BlockState state, Level level, BlockPos pos, Player player, BlockHitResult hitResult) {
-        if (!level.isClientSide && level.getBlockEntity(pos) instanceof DispenserBlockEntity dispenserBlockEntity) {
-            player.openMenu(dispenserBlockEntity);
+        if (!level.isClientSide && level.getBlockEntity(pos) instanceof DispenserBlockEntity dispenserBlockEntity && player.openMenu(dispenserBlockEntity).isPresent()) { // Paper - Fix InventoryOpenEvent cancellation
             player.awardStat(dispenserBlockEntity instanceof DropperBlockEntity ? Stats.INSPECT_DROPPER : Stats.INSPECT_DISPENSER);
         }
 
         return InteractionResult.SUCCESS;
     }
 
-    protected void dispenseFrom(ServerLevel level, BlockState state, BlockPos pos) {
+    public void dispenseFrom(ServerLevel level, BlockState state, BlockPos pos) {
         DispenserBlockEntity dispenserBlockEntity = level.getBlockEntity(pos, BlockEntityType.DISPENSER).orElse(null);
         if (dispenserBlockEntity == null) {
             LOGGER.warn("Ignoring dispensing attempt for Dispenser without matching block entity at {}", pos);
@@ -87,17 +_,26 @@
             BlockSource blockSource = new BlockSource(level, pos, state, dispenserBlockEntity);
             int randomSlot = dispenserBlockEntity.getRandomSlot(level.random);
             if (randomSlot < 0) {
+                if (org.bukkit.craftbukkit.event.CraftEventFactory.handleBlockFailedDispenseEvent(level, pos)) { // Paper - Add BlockFailedDispenseEvent
                 level.levelEvent(1001, pos, 0);
                 level.gameEvent(GameEvent.BLOCK_ACTIVATE, pos, GameEvent.Context.of(dispenserBlockEntity.getBlockState()));
+                } // Paper - Add BlockFailedDispenseEvent
             } else {
                 ItemStack item = dispenserBlockEntity.getItem(randomSlot);
                 DispenseItemBehavior dispenseMethod = this.getDispenseMethod(level, item);
                 if (dispenseMethod != DispenseItemBehavior.NOOP) {
+                    if (!org.bukkit.craftbukkit.event.CraftEventFactory.handleBlockPreDispenseEvent(level, pos, item, randomSlot)) return; // Paper - Add BlockPreDispenseEvent
                     dispenserBlockEntity.setItem(randomSlot, dispenseMethod.dispense(blockSource, item));
                 }
             }
         }
     }
+
+    // Paper start - Fix NPE with equippable and items without behavior
+    public static DispenseItemBehavior getDispenseBehavior(BlockSource pointer, ItemStack stack) {
+        return ((DispenserBlock) pointer.state().getBlock()).getDispenseMethod(pointer.level(), stack);
+    }
+    // Paper end - Fix NPE with equippable and items without behavior
 
     protected DispenseItemBehavior getDispenseMethod(Level level, ItemStack item) {
         if (!item.isItemEnabled(level.enabledFeatures())) {
