From 0b9983dfbc2846499759f8d7b74fb03d5f43e0d8 Mon Sep 17 00:00:00 2001 From: egg82 Date: Wed, 14 Nov 2018 20:09:37 -0700 Subject: [PATCH] Optimize redstone algorithm (#1494) --- .../0408-Optimize-redstone-algorithm.patch | 1164 +++++++++++++++++ 1 file changed, 1164 insertions(+) create mode 100644 Spigot-Server-Patches/0408-Optimize-redstone-algorithm.patch diff --git a/Spigot-Server-Patches/0408-Optimize-redstone-algorithm.patch b/Spigot-Server-Patches/0408-Optimize-redstone-algorithm.patch new file mode 100644 index 000000000000..c96ae932473e --- /dev/null +++ b/Spigot-Server-Patches/0408-Optimize-redstone-algorithm.patch @@ -0,0 +1,1164 @@ +From b49f9544a9d45081de36ca836fe839a8f84ce6c2 Mon Sep 17 00:00:00 2001 +From: theosib +Date: Thu, 27 Sep 2018 01:43:35 -0600 +Subject: [PATCH] Optimize redstone algorithm + +Author: theosib +Co-authored-by: egg82 + +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. + +diff --git a/src/main/java/com/destroystokyo/paper/PaperWorldConfig.java b/src/main/java/com/destroystokyo/paper/PaperWorldConfig.java +index eabb2c1ba..5fb6e263a 100644 +--- a/src/main/java/com/destroystokyo/paper/PaperWorldConfig.java ++++ b/src/main/java/com/destroystokyo/paper/PaperWorldConfig.java +@@ -569,4 +569,14 @@ public class PaperWorldConfig { + private void preventMovingIntoUnloadedChunks() { + preventMovingIntoUnloadedChunks = getBoolean("prevent-moving-into-unloaded-chunks", false); + } ++ ++ public boolean useEigencraftRedstone = false; ++ private void useEigencraftRedstone() { ++ useEigencraftRedstone = this.getBoolean("use-faster-eigencraft-redstone", false); ++ if (useEigencraftRedstone) { ++ log("Using Eigencraft redstone algorithm by theosib."); ++ } else { ++ log("Using vanilla redstone algorithm."); ++ } ++ } + } +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 000000000..21d9d6d7e +--- /dev/null ++++ b/src/main/java/com/destroystokyo/paper/util/RedstoneWireTurbo.java +@@ -0,0 +1,910 @@ ++package com.destroystokyo.paper.util; ++ ++import java.util.List; ++import java.util.Map; ++import java.util.concurrent.ThreadLocalRandom; ++ ++import org.bukkit.event.block.BlockRedstoneEvent; ++ ++import com.google.common.collect.Lists; ++import com.google.common.collect.Maps; ++ ++import net.minecraft.server.Block; ++import net.minecraft.server.BlockPosition; ++import net.minecraft.server.BlockRedstoneWire; ++import net.minecraft.server.IBlockData; ++import net.minecraft.server.World; ++ ++/** ++ * 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 BlockRedstoneWire 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 updateQueue0 = Lists.newArrayList(); ++ private List updateQueue1 = Lists.newArrayList(); ++ private List updateQueue2 = Lists.newArrayList(); ++ ++ public RedstoneWireTurbo(BlockRedstoneWire 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 BlockPosition[] computeAllNeighbors(final BlockPosition pos) { ++ final int x = pos.getX(); ++ final int y = pos.getY(); ++ final int z = pos.getZ(); ++ final BlockPosition[] n = new BlockPosition[24]; ++ ++ // Immediate neighbors, in the same order as ++ // World.notifyNeighborsOfStateChange, etc.: ++ // west, east, down, up, north, south ++ n[0] = new BlockPosition(x - 1, y, z); ++ n[1] = new BlockPosition(x + 1, y, z); ++ n[2] = new BlockPosition(x, y - 1, z); ++ n[3] = new BlockPosition(x, y + 1, z); ++ n[4] = new BlockPosition(x, y, z - 1); ++ n[5] = new BlockPosition(x, y, z + 1); ++ ++ // Neighbors of neighbors, in the same order, ++ // except that duplicates are not included ++ n[6] = new BlockPosition(x - 2, y, z); ++ n[7] = new BlockPosition(x - 1, y - 1, z); ++ n[8] = new BlockPosition(x - 1, y + 1, z); ++ n[9] = new BlockPosition(x - 1, y, z - 1); ++ n[10] = new BlockPosition(x - 1, y, z + 1); ++ n[11] = new BlockPosition(x + 2, y, z); ++ n[12] = new BlockPosition(x + 1, y - 1, z); ++ n[13] = new BlockPosition(x + 1, y + 1, z); ++ n[14] = new BlockPosition(x + 1, y, z - 1); ++ n[15] = new BlockPosition(x + 1, y, z + 1); ++ n[16] = new BlockPosition(x, y - 2, z); ++ n[17] = new BlockPosition(x, y - 1, z - 1); ++ n[18] = new BlockPosition(x, y - 1, z + 1); ++ n[19] = new BlockPosition(x, y + 2, z); ++ n[20] = new BlockPosition(x, y + 1, z - 1); ++ n[21] = new BlockPosition(x, y + 1, z + 1); ++ n[22] = new BlockPosition(x, y, z - 2); ++ n[23] = new BlockPosition(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 ++ } ++ ++ IBlockData currentState; // Keep track of redstone wire value ++ UpdateNode[] neighbor_nodes; // References to neighbors (directed graph edges) ++ BlockPosition self; // UpdateNode's own position ++ BlockPosition 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 nodeCache = Maps.newHashMap(); ++ ++ /* ++ * For a newly created UpdateNode object, determine what type of block it is. ++ */ ++ private void identifyNode(final World worldIn, final UpdateNode upd1) { ++ final BlockPosition pos = upd1.self; ++ final IBlockData oldState = worldIn.getType(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.canPlace(null, worldIn, pos)) { ++ // Pop off the redstone dust ++ oldState.dropNaturally(worldIn, pos, 0); ++ worldIn.setAir(pos); ++ ++ // 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 World worldIn, final UpdateNode upd1, final int layer) { ++ final BlockPosition 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 IBlockData 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. ++ IBlockData 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 World worldIn, final UpdateNode upd1) { ++ final BlockPosition pos = upd1.self; ++ ++ // Get the list of neighbor coordinates ++ final BlockPosition[] 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 BlockPosition 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 World 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 BlockPosition 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 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 World 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 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); ++ } ++ } ++ ++ // 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 IBlockData scheduleReentrantNeighborChanged(final World worldIn, final BlockPosition pos, final IBlockData newState, final BlockPosition 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 IBlockData updateSurroundingRedstone(final World worldIn, final BlockPosition pos, final IBlockData state, final BlockPosition 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 IBlockData 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 IBlockData calculateCurrentChanges(final World worldIn, final UpdateNode upd) { ++ IBlockData state = upd.currentState; ++ final int i = state.get(BlockRedstoneWire.POWER).intValue(); ++ int j = 0; ++ j = getMaxCurrentStrength(upd, j); ++ int l = 0; ++ ++ wire.setCanProvidePower(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.isBlockIndirectlyGettingPowered(upd.self); ++ wire.setCanProvidePower(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.isOccluding(); ++ ++ 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.isOccluding(); ++ 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(worldIn.getWorld().getBlockAt(upd.self.getX(), upd.self.getY(), upd.self.getZ()), i, j); ++ worldIn.getServer().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. ++ state = state.set(BlockRedstoneWire.POWER, Integer.valueOf(j)); ++ worldIn.setTypeAndData(upd.self, state, 2); ++ } ++ ++ return state; ++ } ++ ++ /* ++ * 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.get(BlockRedstoneWire.POWER).intValue(); ++ return i > strength ? i : strength; ++ } ++} +diff --git a/src/main/java/net/minecraft/server/BlockRedstoneWire.java b/src/main/java/net/minecraft/server/BlockRedstoneWire.java +index 80bad6755..3b45489a4 100644 +--- a/src/main/java/net/minecraft/server/BlockRedstoneWire.java ++++ b/src/main/java/net/minecraft/server/BlockRedstoneWire.java +@@ -1,5 +1,7 @@ + package net.minecraft.server; + ++import com.destroystokyo.paper.PaperConfig; ++import com.destroystokyo.paper.util.RedstoneWireTurbo; + import com.google.common.collect.ImmutableMap; + import com.google.common.collect.Lists; + import com.google.common.collect.Maps; +@@ -22,7 +24,10 @@ public class BlockRedstoneWire extends Block { + public static final BlockStateInteger POWER = BlockProperties.al; + public static final Map> q = Maps.newEnumMap(ImmutableMap.of(EnumDirection.NORTH, BlockRedstoneWire.NORTH, EnumDirection.EAST, BlockRedstoneWire.EAST, EnumDirection.SOUTH, BlockRedstoneWire.SOUTH, EnumDirection.WEST, BlockRedstoneWire.WEST)); + protected static final VoxelShape[] r = new VoxelShape[] { Block.a(3.0D, 0.0D, 3.0D, 13.0D, 1.0D, 13.0D), Block.a(3.0D, 0.0D, 3.0D, 13.0D, 1.0D, 16.0D), Block.a(0.0D, 0.0D, 3.0D, 13.0D, 1.0D, 13.0D), Block.a(0.0D, 0.0D, 3.0D, 13.0D, 1.0D, 16.0D), Block.a(3.0D, 0.0D, 0.0D, 13.0D, 1.0D, 13.0D), Block.a(3.0D, 0.0D, 0.0D, 13.0D, 1.0D, 16.0D), Block.a(0.0D, 0.0D, 0.0D, 13.0D, 1.0D, 13.0D), Block.a(0.0D, 0.0D, 0.0D, 13.0D, 1.0D, 16.0D), Block.a(3.0D, 0.0D, 3.0D, 16.0D, 1.0D, 13.0D), Block.a(3.0D, 0.0D, 3.0D, 16.0D, 1.0D, 16.0D), Block.a(0.0D, 0.0D, 3.0D, 16.0D, 1.0D, 13.0D), Block.a(0.0D, 0.0D, 3.0D, 16.0D, 1.0D, 16.0D), Block.a(3.0D, 0.0D, 0.0D, 16.0D, 1.0D, 13.0D), Block.a(3.0D, 0.0D, 0.0D, 16.0D, 1.0D, 16.0D), Block.a(0.0D, 0.0D, 0.0D, 16.0D, 1.0D, 13.0D), Block.a(0.0D, 0.0D, 0.0D, 16.0D, 1.0D, 16.0D)}; ++ public boolean canProvidePower() { return this.s; } // Paper - OBFHELPER ++ public void setCanProvidePower(boolean value) { this.s = value; } // Paper - OBFHELPER + private boolean s = true; ++ private Set getBlocksNeedingUpdate() { return this.t; } // Paper - OBFHELPER + private final Set t = Sets.newHashSet(); + + public BlockRedstoneWire(Block.Info block_info) { +@@ -154,8 +159,120 @@ public class BlockRedstoneWire extends Block { + return iblockdata1.q() || iblockdata1.getBlock() == Blocks.GLOWSTONE; + } + +- private IBlockData a(World world, BlockPosition blockposition, IBlockData iblockdata) { +- iblockdata = this.b(world, blockposition, iblockdata); ++ // Paper start - Optimize redstone ++ // The bulk of the new functionality is found in RedstoneWireTurbo.java ++ RedstoneWireTurbo turbo = new 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 IBlockData updateSurroundingRedstone(World worldIn, BlockPosition pos, IBlockData state, BlockPosition source) { ++ if (worldIn.paperConfig.useEigencraftRedstone) { ++ return turbo.updateSurroundingRedstone(worldIn, pos, state, source); ++ } ++ return a(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 IBlockData calculateCurrentChanges(World worldIn, BlockPosition pos1, BlockPosition pos2, IBlockData state) { ++ IBlockData iblockstate = state; ++ int i = state.get(POWER).intValue(); ++ int j = 0; ++ j = this.getPower(j, worldIn.getType(pos2)); ++ this.setCanProvidePower(false); ++ int k = worldIn.isBlockIndirectlyGettingPowered(pos1); ++ this.setCanProvidePower(true); ++ ++ if (!worldIn.paperConfig.useEigencraftRedstone) { ++ // 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.useEigencraftRedstone || k < 15) { ++ for (EnumDirection enumfacing : EnumDirection.EnumDirectionLimit.HORIZONTAL) { ++ BlockPosition blockpos = pos1.shift(enumfacing); ++ boolean flag = blockpos.getX() != pos2.getX() || blockpos.getZ() != pos2.getZ(); ++ ++ if (flag) { ++ l = this.getPower(l, worldIn.getType(blockpos)); ++ } ++ ++ if (worldIn.getType(blockpos).isOccluding() && !worldIn.getType(pos1.up()).isOccluding()) { ++ if (flag && pos1.getY() >= pos2.getY()) { ++ l = this.getPower(l, worldIn.getType(blockpos.up())); ++ } ++ } else if (!worldIn.getType(blockpos).isOccluding() && flag && pos1.getY() <= pos2.getY()) { ++ l = this.getPower(l, worldIn.getType(blockpos.down())); ++ } ++ } ++ } ++ ++ if (!worldIn.paperConfig.useEigencraftRedstone) { ++ // 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) { ++ state = state.set(POWER, Integer.valueOf(j)); ++ ++ if (worldIn.getType(pos1) == iblockstate) { ++ worldIn.setTypeAndData(pos1, state, 2); ++ } ++ ++ if (!worldIn.paperConfig.useEigencraftRedstone) { ++ // The new search algorithm keeps track of blocks needing updates in its own data structures, ++ // so only add anything to blocksNeedingUpdate if we're using the vanilla update algorithm. ++ this.getBlocksNeedingUpdate().add(pos1); ++ ++ for (EnumDirection enumfacing1 : EnumDirection.values()) { ++ this.getBlocksNeedingUpdate().add(pos1.shift(enumfacing1)); ++ } ++ } ++ } ++ ++ return state; ++ } ++ // Paper end ++ ++ private IBlockData a(World worldIn, BlockPosition pos, IBlockData state) { ++ state = this.b(worldIn, pos, state); + ArrayList arraylist = Lists.newArrayList(this.t); + + this.t.clear(); +@@ -164,10 +281,10 @@ public class BlockRedstoneWire extends Block { + while (iterator.hasNext()) { + BlockPosition blockposition1 = (BlockPosition) iterator.next(); + +- world.applyPhysics(blockposition1, this); ++ worldIn.applyPhysics(blockposition1, this); + } + +- return iblockdata; ++ return state; + } + + private IBlockData b(World world, BlockPosition blockposition, IBlockData iblockdata) { +@@ -264,7 +381,7 @@ public class BlockRedstoneWire extends Block { + + public void onPlace(IBlockData iblockdata, World world, BlockPosition blockposition, IBlockData iblockdata1) { + if (iblockdata1.getBlock() != iblockdata.getBlock() && !world.isClientSide) { +- this.a(world, blockposition, iblockdata); ++ this.updateSurroundingRedstone(world, blockposition, iblockdata, null); // Paper - Optimize redstone + Iterator iterator = EnumDirection.EnumDirectionLimit.VERTICAL.iterator(); + + EnumDirection enumdirection; +@@ -310,7 +427,7 @@ public class BlockRedstoneWire extends Block { + world.applyPhysics(blockposition.shift(enumdirection), this); + } + +- this.a(world, blockposition, iblockdata); ++ this.updateSurroundingRedstone(world, blockposition, iblockdata, null); // Paper - Optimize redstone + Iterator iterator = EnumDirection.EnumDirectionLimit.HORIZONTAL.iterator(); + + EnumDirection enumdirection1; +@@ -350,7 +467,7 @@ public class BlockRedstoneWire extends Block { + public void doPhysics(IBlockData iblockdata, World world, BlockPosition blockposition, Block block, BlockPosition blockposition1) { + if (!world.isClientSide) { + if (iblockdata.canPlace(world, blockposition)) { +- this.a(world, blockposition, iblockdata); ++ this.updateSurroundingRedstone(world, blockposition, iblockdata, blockposition1); // Paper - Optimize redstone + } else { + iblockdata.a(world, blockposition, 0); + world.setAir(blockposition); +diff --git a/src/main/java/net/minecraft/server/World.java b/src/main/java/net/minecraft/server/World.java +index 7914f9b60..cdbb247da 100644 +--- a/src/main/java/net/minecraft/server/World.java ++++ b/src/main/java/net/minecraft/server/World.java +@@ -646,6 +646,7 @@ public abstract class World implements IEntityAccess, GeneratorAccess, IIBlockAc + + } + ++ public void neighborChanged(BlockPosition pos, Block blockIn, BlockPosition fromPos) { a(pos, blockIn, fromPos); } // Paper - OBFHELPER + public void a(BlockPosition blockposition, Block block, BlockPosition blockposition1) { + if (!this.isClientSide) { + IBlockData iblockdata = this.getType(blockposition); +@@ -2667,6 +2668,7 @@ public abstract class World implements IEntityAccess, GeneratorAccess, IIBlockAc + return this.getBlockFacePower(blockposition.down(), EnumDirection.DOWN) > 0 ? true : (this.getBlockFacePower(blockposition.up(), EnumDirection.UP) > 0 ? true : (this.getBlockFacePower(blockposition.north(), EnumDirection.NORTH) > 0 ? true : (this.getBlockFacePower(blockposition.south(), EnumDirection.SOUTH) > 0 ? true : (this.getBlockFacePower(blockposition.west(), EnumDirection.WEST) > 0 ? true : this.getBlockFacePower(blockposition.east(), EnumDirection.EAST) > 0)))); + } + ++ public int isBlockIndirectlyGettingPowered(BlockPosition pos) { return u(pos); } // Paper - OBFHELPER + public int u(BlockPosition blockposition) { + int i = 0; + EnumDirection[] aenumdirection = World.a; +-- +2.19.1.windows.1 +