--- a/net/minecraft/world/level/block/RedstoneTorchBlock.java
+++ b/net/minecraft/world/level/block/RedstoneTorchBlock.java
@@ -24,7 +_,7 @@
 public class RedstoneTorchBlock extends BaseTorchBlock {
     public static final MapCodec<RedstoneTorchBlock> CODEC = simpleCodec(RedstoneTorchBlock::new);
     public static final BooleanProperty LIT = BlockStateProperties.LIT;
-    private static final Map<BlockGetter, List<RedstoneTorchBlock.Toggle>> RECENT_TOGGLES = new WeakHashMap<>();
+    // Paper - Faster redstone torch rapid clock removal; Move the mapped list to World
     public static final int RECENT_TOGGLE_TIMER = 60;
     public static final int MAX_RECENT_TOGGLES = 8;
     public static final int RESTART_DELAY = 160;
@@ -72,14 +_,34 @@
     @Override
     protected void tick(BlockState state, ServerLevel level, BlockPos pos, RandomSource random) {
         boolean hasNeighborSignal = this.hasNeighborSignal(level, pos, state);
-        List<RedstoneTorchBlock.Toggle> list = RECENT_TOGGLES.get(level);
-
-        while (list != null && !list.isEmpty() && level.getGameTime() - list.get(0).when > 60L) {
-            list.remove(0);
+        // Paper start - Faster redstone torch rapid clock removal
+        java.util.ArrayDeque<RedstoneTorchBlock.Toggle> redstoneUpdateInfos = level.redstoneUpdateInfos;
+        if (redstoneUpdateInfos != null) {
+            RedstoneTorchBlock.Toggle curr;
+            while ((curr = redstoneUpdateInfos.peek()) != null && level.getGameTime() - curr.when > 60L) {
+                redstoneUpdateInfos.poll();
+            }
         }
-
+        // Paper end - Faster redstone torch rapid clock removal
+
+        // CraftBukkit start
+        org.bukkit.plugin.PluginManager manager = level.getCraftServer().getPluginManager();
+        org.bukkit.block.Block block = level.getWorld().getBlockAt(pos.getX(), pos.getY(), pos.getZ());
+        int oldCurrent = ((Boolean) state.getValue(RedstoneTorchBlock.LIT)).booleanValue() ? 15 : 0;
+
+        org.bukkit.event.block.BlockRedstoneEvent event = new org.bukkit.event.block.BlockRedstoneEvent(block, oldCurrent, oldCurrent);
+        // CraftBukkit end
         if (state.getValue(LIT)) {
             if (hasNeighborSignal) {
+                // CraftBukkit start
+                if (oldCurrent != 0) {
+                    event.setNewCurrent(0);
+                    manager.callEvent(event);
+                    if (event.getNewCurrent() != 0) {
+                        return;
+                    }
+                }
+                // CraftBukkit end
                 level.setBlock(pos, state.setValue(LIT, Boolean.valueOf(false)), 3);
                 if (isToggledTooFrequently(level, pos, true)) {
                     level.levelEvent(1502, pos, 0);
@@ -87,6 +_,15 @@
                 }
             }
         } else if (!hasNeighborSignal && !isToggledTooFrequently(level, pos, false)) {
+            // CraftBukkit start
+            if (oldCurrent != 15) {
+                event.setNewCurrent(15);
+                manager.callEvent(event);
+                if (event.getNewCurrent() != 15) {
+                    return;
+                }
+            }
+            // CraftBukkit end
             level.setBlock(pos, state.setValue(LIT, Boolean.valueOf(true)), 3);
         }
     }
@@ -124,7 +_,12 @@
     }
 
     private static boolean isToggledTooFrequently(Level level, BlockPos pos, boolean logToggle) {
-        List<RedstoneTorchBlock.Toggle> list = RECENT_TOGGLES.computeIfAbsent(level, toggles -> Lists.newArrayList());
+        // Paper start - Faster redstone torch rapid clock removal
+        java.util.ArrayDeque<RedstoneTorchBlock.Toggle> list = level.redstoneUpdateInfos;
+        if (list == null) {
+            list = level.redstoneUpdateInfos = new java.util.ArrayDeque<>();
+        }
+        // Paper end - Faster redstone torch rapid clock removal
         if (logToggle) {
             list.add(new RedstoneTorchBlock.Toggle(pos.immutable(), level.getGameTime()));
         }
