aboutsummaryrefslogtreecommitdiffhomepage
path: root/patches/server/1001-Eigencraft-redstone-implementation.patch
diff options
context:
space:
mode:
Diffstat (limited to 'patches/server/1001-Eigencraft-redstone-implementation.patch')
-rw-r--r--patches/server/1001-Eigencraft-redstone-implementation.patch1145
1 files changed, 1145 insertions, 0 deletions
diff --git a/patches/server/1001-Eigencraft-redstone-implementation.patch b/patches/server/1001-Eigencraft-redstone-implementation.patch
new file mode 100644
index 0000000000..b3e3db6504
--- /dev/null
+++ b/patches/server/1001-Eigencraft-redstone-implementation.patch
@@ -0,0 +1,1145 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: theosib <[email protected]>
+Date: Thu, 27 Sep 2018 01:43:35 -0600
+Subject: [PATCH] Eigencraft redstone implementation
+
+Author: theosib <[email protected]>
+
+Original license: MIT
+
+This patch implements theosib's redstone algorithms to completely overhaul the way redstone works.
+The new algorithms should be many times faster than current vanilla ones.
+From the original author's comments, it looks like it shouldn't interfere with any redstone save for very extreme edge-cases.
+
+Surprisingly, not a lot was touched aside from a few obfuscation helpers and BlockRedstoneWire.
+A lot of this code is self-contained in a helper class.
+
+Aside from making the obvious class/function renames and obfhelpers I didn't need to modify much.
+Just added Bukkit's event system and took a few liberties with dead code and comment misspellings.
+
+== AT ==
+public net.minecraft.world.level.block.RedStoneWireBlock shouldSignal
+public net.minecraft.world.level.block.RedStoneWireBlock canSurvive(Lnet/minecraft/world/level/block/state/BlockState;Lnet/minecraft/world/level/LevelReader;Lnet/minecraft/core/BlockPos;)Z
+
+Co-authored-by: egg82 <[email protected]>
+
+diff --git a/src/main/java/com/destroystokyo/paper/util/RedstoneWireTurbo.java b/src/main/java/com/destroystokyo/paper/util/RedstoneWireTurbo.java
+new file mode 100644
+index 0000000000000000000000000000000000000000..9f17170179cc99d84ad25a1e838aff3d8cc66f93
+--- /dev/null
++++ b/src/main/java/com/destroystokyo/paper/util/RedstoneWireTurbo.java
+@@ -0,0 +1,958 @@
++package com.destroystokyo.paper.util;
++
++import java.util.List;
++import java.util.Map;
++import java.util.concurrent.ThreadLocalRandom;
++import net.minecraft.core.BlockPos;
++import net.minecraft.core.Direction;
++import net.minecraft.world.item.ItemStack;
++import net.minecraft.world.item.Items;
++import net.minecraft.world.level.Level;
++import net.minecraft.world.level.block.Block;
++import net.minecraft.world.level.block.RedStoneWireBlock;
++import net.minecraft.world.level.block.state.BlockState;
++import org.bukkit.craftbukkit.block.CraftBlock;
++import org.bukkit.event.block.BlockRedstoneEvent;
++
++import com.google.common.collect.Lists;
++import com.google.common.collect.Maps;
++
++/**
++ * Used for the faster redstone algorithm.
++ * Original author: theosib
++ * Original license: MIT
++ *
++ * Ported to Paper and updated to 1.13 by egg82
++ */
++public class RedstoneWireTurbo {
++ /*
++ * This is Helper class for BlockRedstoneWire. It implements a minimally-invasive
++ * bolt-on accelerator that performs a breadth-first search through redstone wire blocks
++ * in order to more efficiently and deterministically compute new redstone wire power levels
++ * and determine the order in which other blocks should be updated.
++ *
++ * Features:
++ * - Changes to BlockRedstoneWire are very limited, no other classes are affected, and the
++ * choice between old and new redstone wire update algorithms is switchable on-line.
++ * - The vanilla implementation relied on World.notifyNeighborsOfStateChange for redstone
++ * wire blocks to communicate power level changes to each other, generating 36 block
++ * updates per call. This improved implementation propagates power level changes directly
++ * between redstone wire blocks. Redstone wire power levels are therefore computed more quickly,
++ * and block updates are sent only to non-redstone blocks, many of which may perform an
++ * action when informed of a change in redstone power level. (Note: Block updates are not
++ * the same as state changes to redstone wire. Wire block states are updated as soon
++ * as they are computed.)
++ * - Of the 36 block updates generated by a call to World.notifyNeighborsOfStateChange,
++ * 12 of them are obviously redundant (e.g. the west neighbor of the east neighbor).
++ * These are eliminated.
++ * - Updates to redstone wire and other connected blocks are propagated in a breath-first
++ * manner, radiating out from the initial trigger (a block update to a redstone wire
++ * from something other than redstone wire).
++ * - Updates are scheduled both deterministically and in an intuitive order, addressing bug
++ * MC-11193.
++ * - All redstone behavior that used to be locational now works the same in all locations.
++ * - All behaviors of redstone wire that used to be orientational now work the same in all
++ * orientations, as long as orientation can be determined; random otherwise. Some other
++ * redstone components still update directionally (e.g. switches), and this code can't
++ * compensate for that.
++ * - Information that is otherwise computed over and over again or which is expensive to
++ * to compute is cached for faster lookup. This includes coordinates of block position
++ * neighbors and block states that won't change behind our backs during the execution of
++ * this search algorithm.
++ * - Redundant block updates (both to redstone wire and to other blocks) are heavily
++ * consolidated. For worst-case scenarios (depowering of redstone wire) this results
++ * in a reduction of block updates by as much as 95% (factor of 1/21). Due to overheads,
++ * empirical testing shows a speedup better than 10x. This addresses bug MC-81098.
++ *
++ * Extensive testing has been performed to ensure that existing redstone contraptions still
++ * behave as expected. Results of early testing that identified undesirable behavior changes
++ * were addressed. Additionally, real-time performance testing revealed compute inefficiencies
++ * With earlier implementations of this accelerator. Some compatibility adjustments and
++ * performance optimizations resulted in harmless increases in block updates above the
++ * theoretical minimum.
++ *
++ * Only a single redstone machine was found to break: An instant dropper line hack that
++ * relies on powered rails and quasi-connectivity but doesn't work in all directions. The
++ * replacement is to lay redstone wire directly on top of the dropper line, which now works
++ * reliably in any direction.
++ *
++ * There are numerous other optimization that can be made, but those will be provided later in
++ * separate updates. This version is designed to be minimalistic.
++ *
++ * Many thanks to the following individuals for their help in testing this functionality:
++ * - pokechu22, _MethodZz_, WARBEN, NarcolepticFrog, CommandHelper (nessie), ilmango,
++ * OreoLamp, Xcom6000, tryashtar, RedCMD, Smokey95Dog, EDDxample, Rays Works,
++ * Nodnam, BlockyPlays, Grumm, NeunEinser, HelVince.
++ */
++
++ /* Reference to BlockRedstoneWire object, which uses this accelerator */
++ private final RedStoneWireBlock wire;
++
++ /*
++ * Implementation:
++ *
++ * RedstoneWire Blocks are updated in concentric rings or "layers" radiating out from the
++ * initial block update that came from a call to BlockRedstoneWire.neighborChanged().
++ * All nodes put in Layer N are those with Manhattan distance N from the trigger
++ * position, reachable through connected redstone wire blocks.
++ *
++ * Layer 0 represents the trigger block position that was input to neighborChanged.
++ * Layer 1 contains the immediate neighbors of that position.
++ * Layer N contains the neighbors of blocks in layer N-1, not including
++ * those in previous layers.
++ *
++ * Layers enforce an update order that is a function of Manhattan distance
++ * from the initial coordinates input to neighborChanged. The same
++ * coordinates may appear in multiple layers, but redundant updates are minimized.
++ * Block updates are sent layer-by-layer. If multiple of a block's neighbors experience
++ * redstone wire changes before its layer is processed, then those updates will be merged.
++ * If a block's update has been sent, but its neighboring redstone changes
++ * after that, then another update will be sent. This preserves compatibility with
++ * machines that rely on zero-tick behavior, except that the new functionality is non-
++ * locational.
++ *
++ * Within each layer, updates are ordered left-to-right relative to the direction of
++ * information flow. This makes the implementation non-orientational. Only when
++ * this direction is ambiguous is randomness applied (intentionally).
++ */
++ private List<UpdateNode> updateQueue0 = Lists.newArrayList();
++ private List<UpdateNode> updateQueue1 = Lists.newArrayList();
++ private List<UpdateNode> updateQueue2 = Lists.newArrayList();
++
++ public RedstoneWireTurbo(RedStoneWireBlock wire) {
++ this.wire = wire;
++ }
++
++ /*
++ * Compute neighbors of a block. When a redstone wire value changes, previously it called
++ * World.notifyNeighborsOfStateChange. That lists immediately neighboring blocks in
++ * west, east, down, up, north, south order. For each of those neighbors, their own
++ * neighbors are updated in the same order. This generates 36 updates, but 12 of them are
++ * redundant; for instance the west neighbor of a block's east neighbor.
++ *
++ * Note that this ordering is only used to create the initial list of neighbors. Once
++ * the direction of signal flow is identified, the ordering of updates is completely
++ * reorganized.
++ */
++ public static BlockPos[] computeAllNeighbors(final BlockPos pos) {
++ final int x = pos.getX();
++ final int y = pos.getY();
++ final int z = pos.getZ();
++ final BlockPos[] n = new BlockPos[24];
++
++ // Immediate neighbors, in the same order as
++ // World.notifyNeighborsOfStateChange, etc.:
++ // west, east, down, up, north, south
++ n[0] = new BlockPos(x - 1, y, z);
++ n[1] = new BlockPos(x + 1, y, z);
++ n[2] = new BlockPos(x, y - 1, z);
++ n[3] = new BlockPos(x, y + 1, z);
++ n[4] = new BlockPos(x, y, z - 1);
++ n[5] = new BlockPos(x, y, z + 1);
++
++ // Neighbors of neighbors, in the same order,
++ // except that duplicates are not included
++ n[6] = new BlockPos(x - 2, y, z);
++ n[7] = new BlockPos(x - 1, y - 1, z);
++ n[8] = new BlockPos(x - 1, y + 1, z);
++ n[9] = new BlockPos(x - 1, y, z - 1);
++ n[10] = new BlockPos(x - 1, y, z + 1);
++ n[11] = new BlockPos(x + 2, y, z);
++ n[12] = new BlockPos(x + 1, y - 1, z);
++ n[13] = new BlockPos(x + 1, y + 1, z);
++ n[14] = new BlockPos(x + 1, y, z - 1);
++ n[15] = new BlockPos(x + 1, y, z + 1);
++ n[16] = new BlockPos(x, y - 2, z);
++ n[17] = new BlockPos(x, y - 1, z - 1);
++ n[18] = new BlockPos(x, y - 1, z + 1);
++ n[19] = new BlockPos(x, y + 2, z);
++ n[20] = new BlockPos(x, y + 1, z - 1);
++ n[21] = new BlockPos(x, y + 1, z + 1);
++ n[22] = new BlockPos(x, y, z - 2);
++ n[23] = new BlockPos(x, y, z + 2);
++ return n;
++ }
++
++ /*
++ * We only want redstone wires to update redstone wires that are
++ * immediately adjacent. Some more distant updates can result
++ * in cross-talk that (a) wastes time and (b) can make the update
++ * order unintuitive. Therefore (relative to the neighbor order
++ * computed by computeAllNeighbors), updates are not scheduled
++ * for redstone wire in those non-connecting positions. On the
++ * other hand, updates will always be sent to *other* types of blocks
++ * in any of the 24 neighboring positions.
++ */
++ private static final boolean[] update_redstone = {
++ true, true, false, false, true, true, // 0 to 5
++ false, true, true, false, false, false, // 6 to 11
++ true, true, false, false, false, true, // 12 to 17
++ true, false, true, true, false, false // 18 to 23
++ };
++
++ // Internal numbering for cardinal directions
++ private static final int North = 0;
++ private static final int East = 1;
++ private static final int South = 2;
++ private static final int West = 3;
++
++ /*
++ * These lookup tables completely remap neighbor positions into a left-to-right
++ * ordering, based on the cardinal direction that is determined to be forward.
++ * See below for more explanation.
++ */
++ private static final int[] forward_is_north = {2, 3, 16, 19, 0, 4, 1, 5, 7, 8, 17, 20, 12, 13, 18, 21, 6, 9, 22, 14, 11, 10, 23, 15};
++ private static final int[] forward_is_east = {2, 3, 16, 19, 4, 1, 5, 0, 17, 20, 12, 13, 18, 21, 7, 8, 22, 14, 11, 15, 23, 9, 6, 10};
++ private static final int[] forward_is_south = {2, 3, 16, 19, 1, 5, 0, 4, 12, 13, 18, 21, 7, 8, 17, 20, 11, 15, 23, 10, 6, 14, 22, 9};
++ private static final int[] forward_is_west = {2, 3, 16, 19, 5, 0, 4, 1, 18, 21, 7, 8, 17, 20, 12, 13, 23, 10, 6, 9, 22, 15, 11, 14};
++
++ /* For any orientation, we end up with the update order defined below. This order is relative to any redstone wire block
++ * that is itself having an update computed, and this center position is marked with C.
++ * - The update position marked 0 is computed first, and the one marked 23 is last.
++ * - Forward is determined by the local direction of information flow into position C from prior updates.
++ * - The first updates are scheduled for the four positions below and above C.
++ * - Then updates are scheduled for the four horizontal neighbors of C, followed by the positions below and above those neighbors.
++ * - Finally, updates are scheduled for the remaining positions with Manhattan distance 2 from C (at the same Y coordinate).
++ * - For a given horizontal distance from C, updates are scheduled starting from directly left and stepping clockwise to directly
++ * right. The remaining positions behind C are scheduled counterclockwise so as to maintain the left-to-right ordering.
++ * - If C is in layer N of the update schedule, then all 24 positions may be scheduled for layer N+1. For redstone wire, no
++ * updates are scheduled for positions that cannot directly connect. Additionally, the four positions above and below C
++ * are ALSO scheduled for layer N+2.
++ * - This update order was selected after experimenting with a number of alternative schedules, based on its compatibility
++ * with existing redstone designs and behaviors that were considered to be intuitive by various testers. WARBEN in particular
++ * made some of the most challenging test cases, but the 3-tick clocks (made by RedCMD) were also challenging to fix,
++ * along with the rail-based instant dropper line built by ilmango. Numerous others made test cases as well, including
++ * NarcolepticFrog, nessie, and Pokechu22.
++ *
++ * - The forward direction is determined locally. So when there are branches in the redstone wire, the left one will get updated
++ * before the right one. Each branch can have its own relative forward direction, resulting in the left side of a left branch
++ * having priority over the right branch of a left branch, which has priority over the left branch of a right branch, followed
++ * by the right branch of a right branch. And so forth. Since redstone power reduces to zero after a path distance of 15,
++ * that imposes a practical limit on the branching. Note that the branching is not tracked explicitly -- relative forward
++ * directions dictate relative sort order, which maintains the proper global ordering. This also makes it unnecessary to be
++ * concerned about branches meeting up with each other.
++ *
++ * ^
++ * |
++ * Forward
++ * <-- Left Right -->
++ *
++ * 18
++ * 10 17 5 19 11
++ * 2 8 0 12 16 4 C 6 20 9 1 13 3
++ * 14 21 7 23 15
++ * Further 22 Further
++ * Down Down Up Up
++ *
++ * Backward
++ * |
++ * V
++ */
++
++ // This allows the above remapping tables to be looked up by cardial direction index
++ private static final int[][] reordering = { forward_is_north, forward_is_east, forward_is_south, forward_is_west };
++
++ /*
++ * Input: Array of UpdateNode objects in an order corresponding to the positions
++ * computed by computeAllNeighbors above.
++ * Output: Array of UpdateNode objects oriented using the above remapping tables
++ * corresponding to the identified heading (direction of information flow).
++ */
++ private static void orientNeighbors(final UpdateNode[] src, final UpdateNode[] dst, final int heading) {
++ final int[] re = reordering[heading];
++ for (int i = 0; i < 24; i++) {
++ dst[i] = src[re[i]];
++ }
++ }
++
++ /*
++ * Structure to keep track of redstone wire blocks and
++ * neighbors that will receive updates.
++ */
++ private static class UpdateNode {
++ public static enum Type {
++ UNKNOWN, REDSTONE, OTHER
++ }
++
++ BlockState currentState; // Keep track of redstone wire value
++ UpdateNode[] neighbor_nodes; // References to neighbors (directed graph edges)
++ BlockPos self; // UpdateNode's own position
++ BlockPos parent; // Which block pos spawned/updated this node
++ Type type = Type.UNKNOWN; // unknown, redstone wire, other type of block
++ int layer; // Highest layer this node is scheduled in
++ boolean visited; // To keep track of information flow direction, visited restone wire is marked
++ int xbias, zbias; // Remembers directionality of ancestor nodes; helps eliminate directional ambiguities.
++ }
++
++ /*
++ * Keep track of all block positions discovered during search and their current states.
++ * We want to remember one entry for each position.
++ */
++ private final Map<BlockPos, UpdateNode> nodeCache = Maps.newHashMap();
++
++ /*
++ * For a newly created UpdateNode object, determine what type of block it is.
++ */
++ private void identifyNode(final Level worldIn, final UpdateNode upd1) {
++ final BlockPos pos = upd1.self;
++ final BlockState oldState = worldIn.getBlockState(pos);
++ upd1.currentState = oldState;
++
++ // Some neighbors of redstone wire are other kinds of blocks.
++ // These need to receive block updates to inform them that
++ // redstone wire values have changed.
++ final Block block = oldState.getBlock();
++ if (block != wire) {
++ // Mark this block as not redstone wire and therefore
++ // requiring updates
++ upd1.type = UpdateNode.Type.OTHER;
++
++ // Non-redstone blocks may propagate updates, but those updates
++ // are not handled by this accelerator. Therefore, we do not
++ // expand this position's neighbors.
++ return;
++ }
++
++ // One job of BlockRedstoneWire.neighborChanged is to convert
++ // redstone wires to items if the block beneath was removed.
++ // With this accelerator, BlockRedstoneWire.neighborChanged
++ // is only typically called for a single wire block, while
++ // others are processed internally by the breadth first search
++ // algorithm. To preserve this game behavior, this check must
++ // be replicated here.
++ if (!wire.canSurvive(null, worldIn, pos)) {
++ // Pop off the redstone dust
++ Block.popResource(worldIn, pos, new ItemStack(Items.REDSTONE)); // TODO
++ worldIn.removeBlock(pos, false);
++
++ // Mark this position as not being redstone wire
++ upd1.type = UpdateNode.Type.OTHER;
++
++ // Note: Sending updates to air blocks leads to an empty method.
++ // Testing shows this to be faster than explicitly avoiding updates to
++ // air blocks.
++ return;
++ }
++
++ // If the above conditions fail, then this is a redstone wire block.
++ upd1.type = UpdateNode.Type.REDSTONE;
++ }
++
++ /*
++ * Given which redstone wire blocks have been visited and not visited
++ * around the position currently being updated, compute the cardinal
++ * direction that is "forward."
++ *
++ * rx is the forward direction along the West/East axis
++ * rz is the forward direction along the North/South axis
++ */
++ static private int computeHeading(final int rx, final int rz) {
++ // rx and rz can only take on values -1, 0, and 1, so we can
++ // compute a code number that allows us to use a single switch
++ // to determine the heading.
++ final int code = (rx + 1) + 3 * (rz + 1);
++ switch (code) {
++ case 0: {
++ // Both rx and rz are -1 (northwest)
++ // Randomly choose one to be forward.
++ final int j = ThreadLocalRandom.current().nextInt(0, 1);
++ return (j == 0) ? North : West;
++ }
++ case 1: {
++ // rx=0, rz=-1
++ // Definitively North
++ return North;
++ }
++ case 2: {
++ // rx=1, rz=-1 (northeast)
++ // Choose randomly between north and east
++ final int j = ThreadLocalRandom.current().nextInt(0, 1);
++ return (j == 0) ? North : East;
++ }
++ case 3: {
++ // rx=-1, rz=0
++ // Definitively West
++ return West;
++ }
++ case 4: {
++ // rx=0, rz=0
++ // Heading is completely ambiguous. Choose
++ // randomly among the four cardinal directions.
++ return ThreadLocalRandom.current().nextInt(0, 4);
++ }
++ case 5: {
++ // rx=1, rz=0
++ // Definitively East
++ return East;
++ }
++ case 6: {
++ // rx=-1, rz=1 (southwest)
++ // Choose randomly between south and west
++ final int j = ThreadLocalRandom.current().nextInt(0, 1);
++ return (j == 0) ? South : West;
++ }
++ case 7: {
++ // rx=0, rz=1
++ // Definitively South
++ return South;
++ }
++ case 8: {
++ // rx=1, rz=1 (southeast)
++ // Choose randomly between south and east
++ final int j = ThreadLocalRandom.current().nextInt(0, 1);
++ return (j == 0) ? South : East;
++ }
++ }
++
++ // We should never get here
++ return ThreadLocalRandom.current().nextInt(0, 4);
++ }
++
++ // Select whether to use updateSurroundingRedstone from BlockRedstoneWire (old)
++ // or this helper class (new)
++ private static final boolean old_current_change = false;
++
++ /*
++ * Process a node whose neighboring redstone wire has experienced value changes.
++ */
++ private void updateNode(final Level worldIn, final UpdateNode upd1, final int layer) {
++ final BlockPos pos = upd1.self;
++
++ // Mark this redstone wire as having been visited so that it can be used
++ // to calculate direction of information flow.
++ upd1.visited = true;
++
++ // Look up the last known state.
++ // Due to the way other redstone components are updated, we do not
++ // have to worry about a state changing behind our backs. The rare
++ // exception is handled by scheduleReentrantNeighborChanged.
++ final BlockState oldState = upd1.currentState;
++
++ // Ask the wire block to compute its power level from its neighbors.
++ // This will also update the wire's power level and return a new
++ // state if it has changed. When a wire power level is changed,
++ // calculateCurrentChanges will immediately update the block state in the world
++ // and return the same value here to be cached in the corresponding
++ // UpdateNode object.
++ BlockState newState;
++ if (old_current_change) {
++ newState = wire.calculateCurrentChanges(worldIn, pos, pos, oldState);
++ } else {
++ // Looking up block state is slow. This accelerator includes a version of
++ // calculateCurrentChanges that uses cahed wire values for a
++ // significant performance boost.
++ newState = this.calculateCurrentChanges(worldIn, upd1);
++ }
++
++ // Only inform neighbors if the state has changed
++ if (newState != oldState) {
++ // Store the new state
++ upd1.currentState = newState;
++
++ // Inform neighbors of the change
++ propagateChanges(worldIn, upd1, layer);
++ }
++ }
++
++ /*
++ * This identifies the neighboring positions of a new UpdateNode object,
++ * determines their types, and links those to into the graph. Then based on
++ * what nodes in the redstone wire graph have been visited, the neighbors
++ * are reordered left-to-right relative to the direction of information flow.
++ */
++ private void findNeighbors(final Level worldIn, final UpdateNode upd1) {
++ final BlockPos pos = upd1.self;
++
++ // Get the list of neighbor coordinates
++ final BlockPos[] neighbors = computeAllNeighbors(pos);
++
++ // Temporary array of neighbors in cardinal ordering
++ final UpdateNode[] neighbor_nodes = new UpdateNode[24];
++
++ // Target array of neighbors sorted left-to-right
++ upd1.neighbor_nodes = new UpdateNode[24];
++
++ for (int i=0; i<24; i++) {
++ // Look up each neighbor in the node cache
++ final BlockPos pos2 = neighbors[i];
++ UpdateNode upd2 = nodeCache.get(pos2);
++ if (upd2 == null) {
++ // If this is a previously unreached position, create
++ // a new update node, add it to the cache, and identify what it is.
++ upd2 = new UpdateNode();
++ upd2.self = pos2;
++ upd2.parent = pos;
++ nodeCache.put(pos2, upd2);
++ identifyNode(worldIn, upd2);
++ }
++
++ // For non-redstone blocks, any of the 24 neighboring positions
++ // should receive a block update. However, some block coordinates
++ // may contain a redstone wire that does not directly connect to the
++ // one being expanded. To avoid redundant calculations and confusing
++ // cross-talk, those neighboring positions are not included.
++ if (update_redstone[i] || upd2.type != UpdateNode.Type.REDSTONE) {
++ neighbor_nodes[i] = upd2;
++ }
++ }
++
++ // Determine the directions from which the redstone signal may have come from. This
++ // checks for redstone wire at the same Y level and also Y+1 and Y-1, relative to the
++ // block being expanded.
++ final boolean fromWest = (neighbor_nodes[0].visited || neighbor_nodes[7].visited || neighbor_nodes[8].visited);
++ final boolean fromEast = (neighbor_nodes[1].visited || neighbor_nodes[12].visited || neighbor_nodes[13].visited);
++ final boolean fromNorth = (neighbor_nodes[4].visited || neighbor_nodes[17].visited || neighbor_nodes[20].visited);
++ final boolean fromSouth = (neighbor_nodes[5].visited || neighbor_nodes[18].visited || neighbor_nodes[21].visited);
++
++ int cx = 0, cz = 0;
++ if (fromWest) cx += 1;
++ if (fromEast) cx -= 1;
++ if (fromNorth) cz += 1;
++ if (fromSouth) cz -= 1;
++
++ int heading;
++ if (cx==0 && cz==0) {
++ // If there is no clear direction, try to inherit the heading from ancestor nodes.
++ heading = computeHeading(upd1.xbias, upd1.zbias);
++
++ // Propagate that heading to descendant nodes.
++ for (int i=0; i<24; i++) {
++ final UpdateNode nn = neighbor_nodes[i];
++ if (nn != null) {
++ nn.xbias = upd1.xbias;
++ nn.zbias = upd1.zbias;
++ }
++ }
++ } else {
++ if (cx != 0 && cz != 0) {
++ // If the heading is somewhat ambiguous, try to disambiguate based on
++ // ancestor nodes.
++ if (upd1.xbias != 0) cz = 0;
++ if (upd1.zbias != 0) cx = 0;
++ }
++ heading = computeHeading(cx, cz);
++
++ // Propagate that heading to descendant nodes.
++ for (int i=0; i<24; i++) {
++ final UpdateNode nn = neighbor_nodes[i];
++ if (nn != null) {
++ nn.xbias = cx;
++ nn.zbias = cz;
++ }
++ }
++ }
++
++ // Reorder neighboring UpdateNode objects according to the forward direction
++ // determined above.
++ orientNeighbors(neighbor_nodes, upd1.neighbor_nodes, heading);
++ }
++
++ /*
++ * For any redstone wire block in layer N, inform neighbors to recompute their states
++ * in layers N+1 and N+2;
++ */
++ private void propagateChanges(final Level worldIn, final UpdateNode upd1, final int layer) {
++ if (upd1.neighbor_nodes == null) {
++ // If this node has not been expanded yet, find its neighbors
++ findNeighbors(worldIn, upd1);
++ }
++
++ final BlockPos pos = upd1.self;
++
++ // All neighbors may be scheduled for layer N+1
++ final int layer1 = layer + 1;
++
++ // If the node being updated (upd1) has already been expanded, then merely
++ // schedule updates to its neighbors.
++ for (int i = 0; i < 24; i++) {
++ final UpdateNode upd2 = upd1.neighbor_nodes[i];
++
++ // This test ensures that an UpdateNode is never scheduled to the same layer
++ // more than once. Also, skip non-connecting redstone wire blocks
++ if (upd2 != null && layer1 > upd2.layer) {
++ upd2.layer = layer1;
++ updateQueue1.add(upd2);
++
++ // Keep track of which block updated this neighbor
++ upd2.parent = pos;
++ }
++ }
++
++ // Nodes above and below are scheduled ALSO for layer N+2
++ final int layer2 = layer + 2;
++
++ // Repeat of the loop above, but only for the first four (above and below) neighbors
++ // and for layer N+2;
++ for (int i = 0; i < 4; i++) {
++ final UpdateNode upd2 = upd1.neighbor_nodes[i];
++ if (upd2 != null && layer2 > upd2.layer) {
++ upd2.layer = layer2;
++ updateQueue2.add(upd2);
++ upd2.parent = pos;
++ }
++ }
++ }
++
++ // The breadth-first search below will send block updates to blocks
++ // that are not redstone wire. If one of those updates results in
++ // a distant redstone wire getting an update, then this.neighborChanged
++ // will get called. This would be a reentrant call, and
++ // it is necessary to properly integrate those updates into the
++ // on-going search through redstone wire. Thus, we make the layer
++ // currently being processed visible at the object level.
++
++ // The current layer being processed by the breadth-first search
++ private int currentWalkLayer = 0;
++
++ private void shiftQueue() {
++ final List<UpdateNode> t = updateQueue0;
++ t.clear();
++ updateQueue0 = updateQueue1;
++ updateQueue1 = updateQueue2;
++ updateQueue2 = t;
++ }
++
++ /*
++ * Perform a breadth-first (layer by layer) traversal through redstone
++ * wire blocks, propagating value changes to neighbors in an order
++ * that is a function of distance from the initial call to
++ * this.neighborChanged.
++ */
++ private void breadthFirstWalk(final Level worldIn) {
++ shiftQueue();
++ currentWalkLayer = 1;
++
++ // Loop over all layers
++ while (updateQueue0.size()>0 || updateQueue1.size()>0) {
++ // Get the set of blocks in this layer
++ final List<UpdateNode> thisLayer = updateQueue0;
++
++ // Loop over all blocks in the layer. Recall that
++ // this is a List, preserving the insertion order of
++ // left-to-right based on direction of information flow.
++ for (UpdateNode upd : thisLayer) {
++ if (upd.type == UpdateNode.Type.REDSTONE) {
++ // If the node is is redstone wire,
++ // schedule updates to neighbors if its value
++ // has changed.
++ updateNode(worldIn, upd, currentWalkLayer);
++ } else {
++ // If this block is not redstone wire, send a block update.
++ // Redstone wire blocks get state updates, but they don't
++ // need block updates. Only non-redstone neighbors need updates.
++
++ // World.neighborChanged is called from
++ // World.notifyNeighborsOfStateChange, and
++ // notifyNeighborsOfStateExcept. We don't use
++ // World.notifyNeighborsOfStateChange here, since we are
++ // already keeping track of all of the neighbor positions
++ // that need to be updated. All on its own, handling neighbors
++ // this way reduces block updates by 1/3 (24 instead of 36).
++// worldIn.neighborChanged(upd.self, wire, upd.parent);
++
++ // [Space Walker]
++ // The neighbor update system got a significant overhaul in 1.19.
++ // Shape and block updates are now added to a stack before being
++ // processed. These changes make it so any neighbor updates emitted
++ // by this accelerator will not be processed until after the entire
++ // wire network has updated. This has a significant impact on the
++ // behavior and introduces Vanilla parity issues.
++ // To circumvent this issue we bypass the neighbor update stack and
++ // call BlockStateBase#neighborChanged directly. This change mostly
++ // restores old behavior, at the cost of bypassing the
++ // max-chained-neighbor-updates server property.
++ worldIn.getBlockState(upd.self).handleNeighborChanged(worldIn, upd.self, wire, upd.parent, false);
++ }
++ }
++
++ // Move on to the next layer
++ shiftQueue();
++ currentWalkLayer++;
++ }
++
++ currentWalkLayer = 0;
++ }
++
++ /*
++ * Normally, when Minecraft is computing redstone wire power changes, and a wire power level
++ * change sends a block update to a neighboring functional component (e.g. piston, repeater, etc.),
++ * those updates are queued. Only once all redstone wire updates are complete will any component
++ * action generate any further block updates to redstone wire. Instant repeater lines, for instance,
++ * will process all wire updates for one redstone line, after which the pistons will zero-tick,
++ * after which the next redstone line performs all of its updates. Thus, each wire is processed in its
++ * own discrete wave.
++ *
++ * However, there are some corner cases where this pattern breaks, with a proof of concept discovered
++ * by Rays Works, which works the same in vanilla. The scenario is as follows:
++ * (1) A redstone wire is conducting a signal.
++ * (2) Part-way through that wave of updates, a neighbor is updated that causes an update to a completely
++ * separate redstone wire.
++ * (3) This results in a call to BlockRedstoneWire.neighborChanged for that other wire, in the middle of
++ * an already on-going propagation through the first wire.
++ *
++ * The vanilla code, being depth-first, would end up fully processing the second wire before going back
++ * to finish processing the first one. (Although technically, vanilla has no special concept of "being
++ * in the middle" of processing updates to a wire.) For the breadth-first algorithm, we give this
++ * situation special handling, where the updates for the second wire are incorporated into the schedule
++ * for the first wire, and then the callstack is allowed to unwind back to the on-going search loop in
++ * order to continue processing both the first and second wire in the order of distance from the initial
++ * trigger.
++ */
++ private BlockState scheduleReentrantNeighborChanged(final Level worldIn, final BlockPos pos, final BlockState newState, final BlockPos source) {
++ if (source != null) {
++ // If the cause of the redstone wire update is known, we can use that to help determine
++ // direction of information flow.
++ UpdateNode src = nodeCache.get(source);
++ if (src == null) {
++ src = new UpdateNode();
++ src.self = source;
++ src.parent = source;
++ src.visited = true;
++ identifyNode(worldIn, src);
++ nodeCache.put(source, src);
++ }
++ }
++
++ // Find or generate a node for the redstone block position receiving the update
++ UpdateNode upd = nodeCache.get(pos);
++ if (upd == null) {
++ upd = new UpdateNode();
++ upd.self = pos;
++ upd.parent = pos;
++ upd.visited = true;
++ identifyNode(worldIn, upd);
++ nodeCache.put(pos, upd);
++ }
++ upd.currentState = newState;
++
++ // Receiving this block update may mean something in the world changed.
++ // Therefore we clear the cached block info about all neighbors of
++ // the position receiving the update and then re-identify what they are.
++ if (upd.neighbor_nodes != null) {
++ for (int i=0; i<24; i++) {
++ final UpdateNode upd2 = upd.neighbor_nodes[i];
++ if (upd2 == null) continue;
++ upd2.type = UpdateNode.Type.UNKNOWN;
++ upd2.currentState = null;
++ identifyNode(worldIn, upd2);
++ }
++ }
++
++ // The block at 'pos' is a redstone wire and has been updated already by calling
++ // wire.calculateCurrentChanges, so we don't schedule that. However, we do need
++ // to schedule its neighbors. By passing the current value of 'currentWalkLayer' to
++ // propagateChanges, the neighbors of 'pos' are scheduled for layers currentWalkLayer+1
++ // and currentWalkLayer+2.
++ propagateChanges(worldIn, upd, currentWalkLayer);
++
++ // Return here. The call stack will unwind back to the first call to
++ // updateSurroundingRedstone, whereupon the new updates just scheduled will
++ // be propagated. This also facilitates elimination of superfluous and
++ // redundant block updates.
++ return newState;
++ }
++
++ /*
++ * New version of pre-existing updateSurroundingRedstone, which is called from
++ * wire.updateSurroundingRedstone, which is called from wire.neighborChanged and a
++ * few other methods in BlockRedstoneWire. This sets off the breadth-first
++ * walk through all redstone dust connected to the initial position triggered.
++ */
++ public BlockState updateSurroundingRedstone(final Level worldIn, final BlockPos pos, final BlockState state, final BlockPos source) {
++ // Check this block's neighbors and see if its power level needs to change
++ // Use the calculateCurrentChanges method in BlockRedstoneWire since we have no
++ // cached block states at this point.
++ final BlockState newState = wire.calculateCurrentChanges(worldIn, pos, pos, state);
++
++ // If no change, exit
++ if (newState == state) {
++ return state;
++ }
++
++ // Check to see if this update was received during an on-going breadth first search
++ if (currentWalkLayer > 0 || nodeCache.size() > 0) {
++ // As breadthFirstWalk progresses, it sends block updates to neighbors. Some of those
++ // neighbors may affect the world so as to cause yet another redstone wire block to receive
++ // an update. If that happens, we need to integrate those redstone wire updates into the
++ // already on-going graph walk being performed by breadthFirstWalk.
++ return scheduleReentrantNeighborChanged(worldIn, pos, newState, source);
++ }
++ // If there are no on-going walks through redstone wire, then start a new walk.
++
++ // If the source of the block update to the redstone wire at 'pos' is known, we can use
++ // that to help determine the direction of information flow.
++ if (source != null) {
++ final UpdateNode src = new UpdateNode();
++ src.self = source;
++ src.parent = source;
++ src.visited = true;
++ nodeCache.put(source, src);
++ identifyNode(worldIn, src);
++ }
++
++ // Create a node representing the block at 'pos', and then propagate updates
++ // to its neighbors. As stated above, the call to wire.calculateCurrentChanges
++ // already performs the update to the block at 'pos', so it is not added to the schedule.
++ final UpdateNode upd = new UpdateNode();
++ upd.self = pos;
++ upd.parent = source!=null ? source : pos;
++ upd.currentState = newState;
++ upd.type = UpdateNode.Type.REDSTONE;
++ upd.visited = true;
++ nodeCache.put(pos, upd);
++ propagateChanges(worldIn, upd, 0);
++
++ // Perform the walk over all directly reachable redstone wire blocks, propagating wire value
++ // updates in a breadth first order out from the initial update received for the block at 'pos'.
++ breadthFirstWalk(worldIn);
++
++ // With the whole search completed, clear the list of all known blocks.
++ // We do not want to keep around state information that may be changed by other code.
++ // In theory, we could cache the neighbor block positions, but that is a separate
++ // optimization.
++ nodeCache.clear();
++
++ return newState;
++ }
++
++ // For any array of neighbors in an UpdateNode object, these are always
++ // the indices of the four immediate neighbors at the same Y coordinate.
++ private static final int[] rs_neighbors = {4, 5, 6, 7};
++ private static final int[] rs_neighbors_up = {9, 11, 13, 15};
++ private static final int[] rs_neighbors_dn = {8, 10, 12, 14};
++
++ /*
++ * Updated calculateCurrentChanges that is optimized for speed and uses
++ * the UpdateNode's neighbor array to find the redstone states of neighbors
++ * that might power it.
++ */
++ private BlockState calculateCurrentChanges(final Level worldIn, final UpdateNode upd) {
++ BlockState state = upd.currentState;
++ final int i = state.getValue(RedStoneWireBlock.POWER).intValue();
++ int j = 0;
++ j = getMaxCurrentStrength(upd, j);
++ int l = 0;
++
++ wire.shouldSignal = false;
++ // Unfortunately, World.isBlockIndirectlyGettingPowered is complicated,
++ // and I'm not ready to try to replicate even more functionality from
++ // elsewhere in Minecraft into this accelerator. So sadly, we must
++ // suffer the performance hit of this very expensive call. If there
++ // is consistency to what this call returns, we may be able to cache it.
++ final int k = worldIn.getBestNeighborSignal(upd.self);
++ wire.shouldSignal = true;
++
++ // The variable 'k' holds the maximum redstone power value of any adjacent blocks.
++ // If 'k' has the highest level of all neighbors, then the power level of this
++ // redstone wire will be set to 'k'. If 'k' is already 15, then nothing inside the
++ // following loop can affect the power level of the wire. Therefore, the loop is
++ // skipped if k is already 15.
++ if (k < 15) {
++ if (upd.neighbor_nodes == null) {
++ // If this node's neighbors are not known, expand the node
++ findNeighbors(worldIn, upd);
++ }
++
++ // These remain constant, so pull them out of the loop.
++ // Regardless of which direction is forward, the UpdateNode for the
++ // position directly above the node being calculated is always
++ // at index 1.
++ UpdateNode center_up = upd.neighbor_nodes[1];
++ boolean center_up_is_cube = center_up.currentState.isRedstoneConductor(worldIn, center_up.self); // TODO
++
++ for (int m = 0; m < 4; m++) {
++ // Get the neighbor array index of each of the four cardinal
++ // neighbors.
++ int n = rs_neighbors[m];
++
++ // Get the max redstone power level of each of the cardinal
++ // neighbors
++ UpdateNode neighbor = upd.neighbor_nodes[n];
++ l = getMaxCurrentStrength(neighbor, l);
++
++ // Also check the positions above and below the cardinal
++ // neighbors
++ boolean neighbor_is_cube = neighbor.currentState.isRedstoneConductor(worldIn, neighbor.self); // TODO
++ if (!neighbor_is_cube) {
++ UpdateNode neighbor_down = upd.neighbor_nodes[rs_neighbors_dn[m]];
++ l = getMaxCurrentStrength(neighbor_down, l);
++ } else
++ if (!center_up_is_cube) {
++ UpdateNode neighbor_up = upd.neighbor_nodes[rs_neighbors_up[m]];
++ l = getMaxCurrentStrength(neighbor_up, l);
++ }
++ }
++ }
++
++ // The new code sets this RedstoneWire block's power level to the highest neighbor
++ // minus 1. This usually results in wire power levels dropping by 2 at a time.
++ // This optimization alone has no impact on update order, only the number of updates.
++ j = l - 1;
++
++ // If 'l' turns out to be zero, then j will be set to -1, but then since 'k' will
++ // always be in the range of 0 to 15, the following if will correct that.
++ if (k > j) j = k;
++
++ // egg82's amendment
++ // Adding Bukkit's BlockRedstoneEvent - er.. event.
++ if (i != j) {
++ BlockRedstoneEvent event = new BlockRedstoneEvent(CraftBlock.at(worldIn, upd.self), i, j);
++ worldIn.getCraftServer().getPluginManager().callEvent(event);
++ j = event.getNewCurrent();
++ }
++
++ if (i != j) {
++ // If the power level has changed from its previous value, compute a new state
++ // and set it in the world.
++ // Possible optimization: Don't commit state changes to the world until they
++ // need to be known by some nearby non-redstone-wire block.
++ BlockPos pos = new BlockPos(upd.self.getX(), upd.self.getY(), upd.self.getZ());
++ if (wire.canSurvive(null, worldIn, pos)) {
++ state = state.setValue(RedStoneWireBlock.POWER, Integer.valueOf(j));
++ // [Space Walker] suppress shape updates and emit those manually to
++ // bypass the new neighbor update stack.
++ if (worldIn.setBlock(upd.self, state, Block.UPDATE_KNOWN_SHAPE | Block.UPDATE_CLIENTS))
++ updateNeighborShapes(worldIn, upd.self, state);
++ }
++ }
++
++ return state;
++ }
++
++ private static final Direction[] UPDATE_SHAPE_ORDER = { Direction.WEST, Direction.EAST, Direction.NORTH, Direction.SOUTH, Direction.DOWN, Direction.UP };
++
++ /*
++ * [Space Walker]
++ * This method emits shape updates around the given block,
++ * bypassing the new neighbor update stack. Diagonal shape
++ * updates are omitted, as they are mostly unnecessary.
++ * Diagonal shape updates are emitted exclusively to other
++ * redstone wires, in order to update their connection properties.
++ * Wire connections should never change as a result of power
++ * changes, so the only behavioral change will be in scenarios
++ * where earlier shape updates have been suppressed to keep a
++ * redstone wire in an invalid state.
++ */
++ public void updateNeighborShapes(Level level, BlockPos pos, BlockState state) {
++ // these updates will be added to the stack and processed after the entire network has updated
++ state.updateIndirectNeighbourShapes(level, pos, Block.UPDATE_KNOWN_SHAPE | Block.UPDATE_CLIENTS);
++
++ for (Direction dir : UPDATE_SHAPE_ORDER) {
++ BlockPos neighborPos = pos.relative(dir);
++ BlockState neighborState = level.getBlockState(neighborPos);
++
++ BlockState newState = neighborState.updateShape(dir.getOpposite(), state, level, neighborPos, pos);
++ Block.updateOrDestroy(neighborState, newState, level, neighborPos, Block.UPDATE_CLIENTS);
++ }
++ }
++
++ /*
++ * Optimized function to compute a redstone wire's power level based on cached
++ * state.
++ */
++ private static int getMaxCurrentStrength(final UpdateNode upd, final int strength) {
++ if (upd.type != UpdateNode.Type.REDSTONE) return strength;
++ final int i = upd.currentState.getValue(RedStoneWireBlock.POWER).intValue();
++ return i > strength ? i : strength;
++ }
++}
+diff --git a/src/main/java/net/minecraft/world/level/block/RedStoneWireBlock.java b/src/main/java/net/minecraft/world/level/block/RedStoneWireBlock.java
+index c73bf0b36252796ca93c500affa2be568e3f6c9e..18ed178223cca85dbba65b1e07741622e266d318 100644
+--- a/src/main/java/net/minecraft/world/level/block/RedStoneWireBlock.java
++++ b/src/main/java/net/minecraft/world/level/block/RedStoneWireBlock.java
+@@ -258,6 +258,116 @@ public class RedStoneWireBlock extends Block {
+ return floor.isFaceSturdy(world, pos, Direction.UP) || floor.is(Blocks.HOPPER);
+ }
+
++ // Paper start - Optimize redstone
++ // The bulk of the new functionality is found in RedstoneWireTurbo.java
++ com.destroystokyo.paper.util.RedstoneWireTurbo turbo = new com.destroystokyo.paper.util.RedstoneWireTurbo(this);
++
++ /*
++ * Modified version of pre-existing updateSurroundingRedstone, which is called from
++ * this.neighborChanged and a few other methods in this class.
++ * Note: Added 'source' argument so as to help determine direction of information flow
++ */
++ private void updateSurroundingRedstone(Level worldIn, BlockPos pos, BlockState state, BlockPos source) {
++ if (worldIn.paperConfig().misc.redstoneImplementation == io.papermc.paper.configuration.WorldConfiguration.Misc.RedstoneImplementation.EIGENCRAFT) {
++ turbo.updateSurroundingRedstone(worldIn, pos, state, source);
++ return;
++ }
++ updatePowerStrength(worldIn, pos, state);
++ }
++
++ /*
++ * Slightly modified method to compute redstone wire power levels from neighboring blocks.
++ * Modifications cut the number of power level changes by about 45% from vanilla, and this
++ * optimization synergizes well with the breadth-first search implemented in
++ * RedstoneWireTurbo.
++ * Note: RedstoneWireTurbo contains a faster version of this code.
++ * Note: Made this public so that RedstoneWireTurbo can access it.
++ */
++ public BlockState calculateCurrentChanges(Level worldIn, BlockPos pos1, BlockPos pos2, BlockState state) {
++ BlockState iblockstate = state;
++ int i = state.getValue(POWER);
++ int j = 0;
++ j = this.getPower(j, worldIn.getBlockState(pos2));
++ this.shouldSignal = false;
++ int k = worldIn.getBestNeighborSignal(pos1);
++ this.shouldSignal = true;
++
++ if (worldIn.paperConfig().misc.redstoneImplementation == io.papermc.paper.configuration.WorldConfiguration.Misc.RedstoneImplementation.VANILLA) {
++ // This code is totally redundant to if statements just below the loop.
++ if (k > 0 && k > j - 1) {
++ j = k;
++ }
++ }
++
++ int l = 0;
++
++ // The variable 'k' holds the maximum redstone power value of any adjacent blocks.
++ // If 'k' has the highest level of all neighbors, then the power level of this
++ // redstone wire will be set to 'k'. If 'k' is already 15, then nothing inside the
++ // following loop can affect the power level of the wire. Therefore, the loop is
++ // skipped if k is already 15.
++ if (worldIn.paperConfig().misc.redstoneImplementation == io.papermc.paper.configuration.WorldConfiguration.Misc.RedstoneImplementation.VANILLA || k < 15) {
++ for (Direction enumfacing : Direction.Plane.HORIZONTAL) {
++ BlockPos blockpos = pos1.relative(enumfacing);
++ boolean flag = blockpos.getX() != pos2.getX() || blockpos.getZ() != pos2.getZ();
++
++ if (flag) {
++ l = this.getPower(l, worldIn.getBlockState(blockpos));
++ }
++
++ if (worldIn.getBlockState(blockpos).isRedstoneConductor(worldIn, blockpos) && !worldIn.getBlockState(pos1.above()).isRedstoneConductor(worldIn, pos1)) {
++ if (flag && pos1.getY() >= pos2.getY()) {
++ l = this.getPower(l, worldIn.getBlockState(blockpos.above()));
++ }
++ } else if (!worldIn.getBlockState(blockpos).isRedstoneConductor(worldIn, blockpos) && flag && pos1.getY() <= pos2.getY()) {
++ l = this.getPower(l, worldIn.getBlockState(blockpos.below()));
++ }
++ }
++ }
++
++ if (worldIn.paperConfig().misc.redstoneImplementation == io.papermc.paper.configuration.WorldConfiguration.Misc.RedstoneImplementation.VANILLA) {
++ // The old code would decrement the wire value only by 1 at a time.
++ if (l > j) {
++ j = l - 1;
++ } else if (j > 0) {
++ --j;
++ } else {
++ j = 0;
++ }
++
++ if (k > j - 1) {
++ j = k;
++ }
++ } else {
++ // The new code sets this RedstoneWire block's power level to the highest neighbor
++ // minus 1. This usually results in wire power levels dropping by 2 at a time.
++ // This optimization alone has no impact on update order, only the number of updates.
++ j = l - 1;
++
++ // If 'l' turns out to be zero, then j will be set to -1, but then since 'k' will
++ // always be in the range of 0 to 15, the following if will correct that.
++ if (k > j) j = k;
++ }
++
++ if (i != j) {
++ org.bukkit.event.block.BlockRedstoneEvent event = new org.bukkit.event.block.BlockRedstoneEvent(org.bukkit.craftbukkit.block.CraftBlock.at(worldIn, pos1), i, j);
++ worldIn.getCraftServer().getPluginManager().callEvent(event);
++
++ j = event.getNewCurrent();
++ state = state.setValue(POWER, j);
++
++ if (worldIn.getBlockState(pos1) == iblockstate) {
++ // [Space Walker] suppress shape updates and emit those manually to
++ // bypass the new neighbor update stack.
++ if (worldIn.setBlock(pos1, state, Block.UPDATE_KNOWN_SHAPE | Block.UPDATE_CLIENTS))
++ turbo.updateNeighborShapes(worldIn, pos1, state);
++ }
++ }
++
++ return state;
++ }
++ // Paper end
++
+ private void updatePowerStrength(Level world, BlockPos pos, BlockState state) {
+ int i = this.calculateTargetStrength(world, pos);
+
+@@ -327,6 +437,7 @@ public class RedStoneWireBlock extends Block {
+ return Math.max(i, j - 1);
+ }
+
++ private int getPower(int min, BlockState iblockdata) { return Math.max(min, getWireSignal(iblockdata)); } // Paper - Optimize redstone
+ private int getWireSignal(BlockState state) {
+ return state.is((Block) this) ? (Integer) state.getValue(RedStoneWireBlock.POWER) : 0;
+ }
+@@ -349,7 +460,7 @@ public class RedStoneWireBlock extends Block {
+ @Override
+ protected void onPlace(BlockState state, Level world, BlockPos pos, BlockState oldState, boolean notify) {
+ if (!oldState.is(state.getBlock()) && !world.isClientSide) {
+- this.updatePowerStrength(world, pos, state);
++ this.updateSurroundingRedstone(world, pos, state, null); // Paper - Optimize redstone
+ Iterator iterator = Direction.Plane.VERTICAL.iterator();
+
+ while (iterator.hasNext()) {
+@@ -376,7 +487,7 @@ public class RedStoneWireBlock extends Block {
+ world.updateNeighborsAt(pos.relative(enumdirection), this);
+ }
+
+- this.updatePowerStrength(world, pos, state);
++ this.updateSurroundingRedstone(world, pos, state, null); // Paper - Optimize redstone
+ this.updateNeighborsOfNeighboringWires(world, pos);
+ }
+ }
+@@ -411,7 +522,7 @@ public class RedStoneWireBlock extends Block {
+ protected void neighborChanged(BlockState state, Level world, BlockPos pos, Block sourceBlock, BlockPos sourcePos, boolean notify) {
+ if (!world.isClientSide) {
+ if (state.canSurvive(world, pos)) {
+- this.updatePowerStrength(world, pos, state);
++ this.updateSurroundingRedstone(world, pos, state, sourcePos); // Paper - Optimize redstone
+ } else {
+ dropResources(state, world, pos);
+ world.removeBlock(pos, false);